Restructure account pages and convert class to function

This commit is contained in:
SrGooglo 2025-06-16 20:46:10 +00:00
parent 8b9afae7eb
commit 0f70bf43fd
15 changed files with 287 additions and 306 deletions

View File

@ -1,8 +0,0 @@
import React from "react"
import Account from "."
export default (props) => {
const username = props.params.username
return <Account username={username} />
}

View File

@ -1,8 +0,0 @@
import React from "react"
import Account from "./index.mobile"
export default (props) => {
const username = props.params.username
return <Account username={username} />
}

View File

@ -0,0 +1,256 @@
import React, { useState, useEffect, useRef } from "react"
import * as antd from "antd"
import classnames from "classnames"
import { motion, AnimatePresence } from "motion/react"
import { Icons } from "@components/Icons"
import FollowButton from "@components/FollowButton"
import UserCard from "@components/UserCard"
import GenerateMenuItems from "@utils/generateMenuItems"
import UserModel from "@models/user"
import FollowsModel from "@models/follows"
import DetailsTab from "./tabs/details"
import PostsTab from "./tabs/posts"
import MusicTab from "./tabs/music"
import FollowersTab from "./tabs/followers"
import "./index.less"
const TabsComponent = {
posts: PostsTab,
followers: FollowersTab,
details: DetailsTab,
music: MusicTab,
}
const Account = ({ params }) => {
const [requestedUser, setRequestedUser] = useState(null)
const [user, setUser] = useState(null)
const [isSelf, setIsSelf] = useState(false)
const [followersCount, setFollowersCount] = useState(0)
const [following, setFollowing] = useState(false)
const [tabActiveKey, setTabActiveKey] = useState("posts")
const [isNotExistent, setIsNotExistent] = useState(false)
const [coverExpanded, setCoverExpanded] = useState(false)
const contentRef = useRef()
const loadUserData = async () => {
const requestedUsername = params.username ?? app.userData.username
let isSelfUser = false
let userData = null
let followersCountData = 0
if (requestedUsername != null) {
if (app.userData.username === requestedUsername) {
isSelfUser = true
}
userData = await UserModel.data({
username: requestedUsername,
}).catch((error) => {
console.error(error)
return false
})
if (!userData) {
setIsNotExistent(true)
return false
}
console.log(`Loaded User [${userData.username}] >`, userData)
const followersResult = await FollowsModel.getFollowers(
userData._id,
).catch(() => false)
if (followersResult) {
followersCountData = followersResult.count
}
}
setIsSelf(isSelfUser)
setRequestedUser(requestedUsername)
setUser(userData)
setFollowing(userData?.following || false)
setFollowersCount(followersCountData)
}
const onClickFollow = async () => {
const result = await FollowsModel.toggleFollow({
user_id: user._id,
}).catch((error) => {
console.error(error)
antd.message.error(error.message)
return false
})
setFollowing(result.following)
setFollowersCount(result.count)
}
const toggleCoverExpanded = (to) => {
setCoverExpanded(to ?? !coverExpanded)
}
const handlePageTransition = (key) => {
if (typeof key !== "string") {
console.error(
"Cannot handle page transition. Invalid key, only valid passing string",
key,
)
return
}
const normalizedKey = key.toLowerCase()
if (tabActiveKey === normalizedKey) {
return false
}
setTabActiveKey(normalizedKey)
}
const onPostListTopVisibility = () => {
// This function was referenced but not defined in the original component
// You may need to implement this based on your requirements
}
useEffect(() => {
loadUserData()
}, [params.username])
if (isNotExistent) {
return (
<antd.Result
status="404"
title="This user does not exist, yet..."
></antd.Result>
)
}
if (!user) {
return <antd.Skeleton active />
}
const state = {
requestedUser,
user,
isSelf,
followersCount,
following,
tabActiveKey,
isNotExistent,
coverExpanded,
}
return (
<div
id="profile"
className={classnames("account-profile", {
["withCover"]: user.cover,
})}
>
{user.cover && (
<div
className={classnames("cover", {
["expanded"]: coverExpanded,
})}
style={{ backgroundImage: `url("${user.cover}")` }}
onClick={() => toggleCoverExpanded()}
id="profile-cover"
/>
)}
<div className="panels">
<div className="left-panel">
<UserCard user={user} />
<div className="actions">
<FollowButton
count={followersCount}
onClick={onClickFollow}
followed={following}
self={isSelf}
/>
{!isSelf && (
<antd.Button
icon={<Icons.MdMessage />}
onClick={() =>
app.location.push(`/messages/${user._id}`)
}
/>
)}
</div>
</div>
<div className="center-panel" ref={contentRef}>
<AnimatePresence mode="wait">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
transition={{
duration: 0.15,
}}
key={tabActiveKey}
style={{
width: "100%",
}}
>
{React.createElement(TabsComponent[tabActiveKey], {
onTopVisibility: onPostListTopVisibility,
state: state,
})}
</motion.div>
</AnimatePresence>
</div>
<div className="right-panel">
<antd.Menu
className="tabMenu"
mode={app.isMobile ? "horizontal" : "vertical"}
selectedKeys={[tabActiveKey]}
onClick={(e) => handlePageTransition(e.key)}
items={GenerateMenuItems([
{
id: "posts",
label: "Posts",
icon: "FiBookOpen",
},
{
id: "music",
label: "Music",
icon: "MdAlbum",
},
{
id: "followers",
label: "Followers",
icon: "FiUsers",
},
{
id: "details",
label: "Details",
icon: "FiInfo",
},
])}
/>
</div>
</div>
</div>
)
}
Account.options = {
layout: {
type: "default",
centeredContent: false,
},
}
export default Account

View File

@ -0,0 +1,31 @@
import React from "react"
import { Result } from "antd"
import PostsList from "@components/PostsList"
import { Icons } from "@components/Icons"
import PostModel from "@models/post"
const emptyListRender = () => {
return (
<Result icon={<Icons.FiUserX style={{ fontSize: "50px" }} />}>
<h2>It's seems this user has no public post, yet.</h2>
</Result>
)
}
export default class UserPosts extends React.Component {
render() {
console.log(this.props.state)
return (
<PostsList
onTopVisibility={this.props.onTopVisibility}
emptyListRender={emptyListRender}
loadFromModel={PostModel.getUserPosts}
loadFromModelProps={{
user_id: this.props.state.user._id,
}}
/>
)
}
}

View File

@ -1,260 +0,0 @@
import React from "react"
import * as antd from "antd"
import classnames from "classnames"
import { motion, AnimatePresence } from "motion/react"
import { Icons } from "@components/Icons"
import FollowButton from "@components/FollowButton"
import UserCard from "@components/UserCard"
import GenerateMenuItems from "@utils/generateMenuItems"
import SessionModel from "@models/session"
import UserModel from "@models/user"
import FollowsModel from "@models/follows"
import DetailsTab from "./tabs/details"
import PostsTab from "./tabs/posts"
import MusicTab from "./tabs/music"
import FollowersTab from "./tabs/followers"
import "./index.less"
const TabsComponent = {
posts: PostsTab,
followers: FollowersTab,
details: DetailsTab,
music: MusicTab,
}
export default class Account extends React.Component {
state = {
requestedUser: null,
user: null,
isSelf: false,
followersCount: 0,
following: false,
tabActiveKey: "posts",
isNotExistent: false,
}
contentRef = React.createRef()
componentDidMount = async () => {
app.layout.toggleCenteredContent(false)
const token = await SessionModel.getDecodedToken()
const requestedUser = this.props.username ?? token?.username
let isSelf = false
let user = null
let followersCount = 0
if (requestedUser != null) {
if (token.username === requestedUser) {
isSelf = true
}
user = await UserModel.data({
username: requestedUser,
}).catch((error) => {
console.error(error)
return false
})
if (!user) {
this.setState({
isNotExistent: true,
})
return false
}
console.log(`Loaded User [${user.username}] >`, user)
const followersResult = await FollowsModel.getFollowers(
user._id,
).catch(() => false)
if (followersResult) {
followersCount = followersResult.count
}
}
await this.setState({
isSelf,
requestedUser,
user,
following: user.following,
followersCount: followersCount,
})
}
onClickFollow = async () => {
const result = await FollowsModel.toggleFollow({
user_id: this.state.user._id,
}).catch((error) => {
console.error(error)
antd.message.error(error.message)
return false
})
await this.setState({
following: result.following,
followersCount: result.count,
})
}
toggleCoverExpanded = async (to) => {
this.setState({
coverExpanded: to ?? !this.state.coverExpanded,
})
}
handlePageTransition = (key) => {
if (typeof key !== "string") {
console.error(
"Cannot handle page transition. Invalid key, only valid passing string",
key,
)
return
}
key = key.toLowerCase()
if (this.state.tabActiveKey === key) {
return false
}
this.setState({
tabActiveKey: key,
})
}
render() {
const user = this.state.user
if (this.state.isNotExistent) {
return (
<antd.Result
status="404"
title="This user does not exist, yet..."
></antd.Result>
)
}
if (!user) {
return <antd.Skeleton active />
}
return (
<div
id="profile"
className={classnames("account-profile", {
["withCover"]: user.cover,
})}
>
{user.cover && (
<div
className={classnames("cover", {
["expanded"]: this.state.coverExpanded,
})}
style={{ backgroundImage: `url("${user.cover}")` }}
onClick={() => this.toggleCoverExpanded()}
id="profile-cover"
/>
)}
<div className="panels">
<div className="left-panel">
<UserCard user={user} />
<div className="actions">
<FollowButton
count={this.state.followersCount}
onClick={this.onClickFollow}
followed={this.state.following}
self={this.state.isSelf}
/>
{!this.state.isSelf && (
<antd.Button
icon={<Icons.MdMessage />}
onClick={() =>
app.location.push(
`/messages/${user._id}`,
)
}
/>
)}
</div>
</div>
<div className="center-panel" ref={this.contentRef}>
<AnimatePresence mode="wait">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
transition={{
duration: 0.15,
}}
key={this.state.tabActiveKey}
style={{
width: "100%",
}}
>
{React.createElement(
TabsComponent[this.state.tabActiveKey],
{
onTopVisibility:
this.onPostListTopVisibility,
state: this.state,
},
)}
</motion.div>
</AnimatePresence>
</div>
<div className="right-panel">
<antd.Menu
className="tabMenu"
mode={app.isMobile ? "horizontal" : "vertical"}
selectedKeys={[this.state.tabActiveKey]}
onClick={(e) => this.handlePageTransition(e.key)}
items={GenerateMenuItems([
{
id: "posts",
label: "Posts",
icon: "FiBookOpen",
},
{
id: "music",
label: "Music",
icon: "MdAlbum",
},
{
id: "followers",
label: "Followers",
icon: "FiUsers",
},
{
id: "details",
label: "Details",
icon: "FiInfo",
},
])}
/>
</div>
</div>
</div>
)
}
}

View File

@ -1,30 +0,0 @@
import React from "react"
import { Result } from "antd"
import PostsList from "@components/PostsList"
import { Icons } from "@components/Icons"
import PostModel from "@models/post"
const emptyListRender = () => {
return <Result
icon={<Icons.FiUserX style={{ fontSize: "50px" }} />}
>
<h2>
It's seems this user has no public post, yet.
</h2>
</Result>
}
export default class UserPosts extends React.Component {
render() {
return <PostsList
onTopVisibility={this.props.onTopVisibility}
emptyListRender={emptyListRender}
loadFromModel={PostModel.getUserPosts}
loadFromModelProps={{
user_id: this.props.state.user._id,
}}
/>
}
}