mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
331 lines
7.0 KiB
JavaScript
Executable File
331 lines
7.0 KiB
JavaScript
Executable File
import React from "react"
|
|
import * as antd from "antd"
|
|
import classnames from "classnames"
|
|
import Loadable from "react-loadable"
|
|
|
|
import { Icons, createIconRender } from "components/Icons"
|
|
import { Image, Skeleton, FollowButton } from "components"
|
|
import { Session, User } from "models"
|
|
|
|
import DetailsTab from "./tabs/details"
|
|
import PostsTab from "./tabs/posts"
|
|
import FollowersTab from "./tabs/followers"
|
|
|
|
import "./index.less"
|
|
|
|
const TabsComponent = {
|
|
"posts": PostsTab,
|
|
"followers": FollowersTab,
|
|
"details": DetailsTab
|
|
}
|
|
|
|
const TabRender = React.memo((props) => {
|
|
const [transitionActive, setTransitionActive] = React.useState(false)
|
|
const [activeKey, setActiveKey] = React.useState(props.renderKey)
|
|
|
|
React.useEffect(() => {
|
|
setTransitionActive(true)
|
|
|
|
setTimeout(() => {
|
|
setActiveKey(props.renderKey)
|
|
|
|
setTimeout(() => {
|
|
setTransitionActive(false)
|
|
}, 100)
|
|
}, 100)
|
|
}, [props.renderKey])
|
|
|
|
const Tab = TabsComponent[activeKey]
|
|
|
|
if (!Tab) {
|
|
return null
|
|
}
|
|
|
|
return <div className={classnames("fade-opacity-active", { "fade-opacity-leave": transitionActive })}>
|
|
<Tab {...props} />
|
|
</div>
|
|
})
|
|
|
|
const UserBadges = React.memo((props) => {
|
|
return React.createElement(Loadable({
|
|
loader: async () => {
|
|
let { badges } = props
|
|
|
|
if (!badges || Array.isArray(badges) === false || badges.length === 0) {
|
|
return null
|
|
}
|
|
|
|
// fetch badges datam from api
|
|
const badgesData = await app.api.request("main", "get", "badges", {
|
|
_id: badges
|
|
}).catch(() => false)
|
|
|
|
if (!badgesData) {
|
|
return null
|
|
}
|
|
|
|
return () => badgesData.map((badge, index) => {
|
|
return <antd.Tooltip placement="bottom" title={badge.description ?? "An badge"}>
|
|
<antd.Tag color={badge.color ?? "default"} key={index} id={badge.name} icon={createIconRender(badge.icon)} className="badge">
|
|
<span>{badge.label}</span>
|
|
</antd.Tag>
|
|
</antd.Tooltip>
|
|
})
|
|
},
|
|
loading: antd.Skeleton,
|
|
}))
|
|
})
|
|
|
|
export default class Account extends React.Component {
|
|
state = {
|
|
requestedUser: null,
|
|
|
|
user: null,
|
|
followers: [],
|
|
|
|
isSelf: false,
|
|
isFollowed: false,
|
|
|
|
tabActiveKey: "posts",
|
|
|
|
isNotExistent: false,
|
|
}
|
|
|
|
contentRef = React.createRef()
|
|
|
|
coverComponent = React.createRef()
|
|
|
|
api = window.app.api.withEndpoints("main")
|
|
|
|
componentDidMount = async () => {
|
|
const token = await Session.decodedToken()
|
|
const location = window.app.history.location
|
|
const query = new URLSearchParams(location.search)
|
|
|
|
const requestedUser = this.props.username ?? location.state?.username ?? query.get("username") ?? token?.username
|
|
|
|
let isSelf = false
|
|
let user = null
|
|
let isFollowed = false
|
|
let followers = []
|
|
|
|
if (requestedUser != null) {
|
|
if (token.username === requestedUser) {
|
|
isSelf = true
|
|
}
|
|
|
|
user = await this.fetchData(requestedUser)
|
|
|
|
if (!user) {
|
|
this.setState({
|
|
isNotExistent: true,
|
|
})
|
|
|
|
return false
|
|
}
|
|
|
|
if (!isSelf) {
|
|
const followedResult = await this.api.get.isFollowed(undefined, { user_id: user._id }).catch(() => false)
|
|
|
|
if (followedResult) {
|
|
isFollowed = followedResult.isFollowed
|
|
}
|
|
}
|
|
|
|
const followersResult = await this.api.get.followers(undefined, { user_id: user._id }).catch(() => false)
|
|
|
|
if (followersResult) {
|
|
followers = followersResult
|
|
}
|
|
}
|
|
|
|
await this.setState({
|
|
isSelf,
|
|
user,
|
|
requestedUser,
|
|
isFollowed,
|
|
followers,
|
|
})
|
|
|
|
//
|
|
app.eventBus.emit("style.compactMode", true)
|
|
}
|
|
|
|
componentWillUnmount = () => {
|
|
app.eventBus.emit("style.compactMode", false)
|
|
}
|
|
|
|
fetchData = async (username) => {
|
|
return await this.api.get.user(undefined, {
|
|
username: username
|
|
}).catch((error) => {
|
|
console.error(error)
|
|
|
|
return false
|
|
})
|
|
}
|
|
|
|
onClickFollow = async () => {
|
|
const result = await this.api.post.followUser({
|
|
username: this.state.requestedUser,
|
|
}).catch((error) => {
|
|
console.error(error)
|
|
antd.message.error(error.message)
|
|
|
|
return false
|
|
})
|
|
|
|
await this.setState({
|
|
isFollowed: result.following,
|
|
followers: result.followers,
|
|
})
|
|
}
|
|
|
|
toogleCoverExpanded = 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
|
|
})
|
|
}
|
|
|
|
handleScroll = (e) => {
|
|
if (!this.state.user?.cover) {
|
|
return false
|
|
}
|
|
|
|
// if component scrolled foward set cover height to 0
|
|
if (e.target.scrollTop > 0) {
|
|
this.coverComponent.current.style.height = "0px"
|
|
this.contentRef.current.style.height = "100vh"
|
|
} else {
|
|
this.coverComponent.current.style.height = ""
|
|
this.contentRef.current.style.height = ""
|
|
}
|
|
}
|
|
|
|
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 <Skeleton />
|
|
}
|
|
|
|
return <div className="accountProfile">
|
|
{user.cover && <div
|
|
className={classnames("cover", {
|
|
["expanded"]: this.state.coverExpanded
|
|
})}
|
|
ref={this.coverComponent}
|
|
style={{ backgroundImage: `url("${user.cover}")` }}
|
|
onClick={() => this.toogleCoverExpanded()}
|
|
/>}
|
|
<div className="profileCardWrapper">
|
|
<div className="profileCard">
|
|
<div className="basicData">
|
|
<div className="title">
|
|
<div className="field">
|
|
<div className="avatar">
|
|
<Image
|
|
alt="ProfileImage"
|
|
src={user.avatar}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="field">
|
|
<div>
|
|
<h1>{user.fullName ?? user.username}</h1>
|
|
{user.verified && <Icons.verifiedBadge />}
|
|
</div>
|
|
|
|
<span>@{user.username}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{!this.state.isSelf && <div>
|
|
<FollowButton
|
|
count={this.state.followers.length}
|
|
onClick={this.onClickFollow}
|
|
followed={this.state.isFollowed}
|
|
/>
|
|
</div>}
|
|
</div>
|
|
|
|
<div className="description">
|
|
<p>
|
|
{user.description}
|
|
</p>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="badgesTab">
|
|
<React.Suspense fallback={<antd.Skeleton />}>
|
|
<UserBadges badges={user.badges} />
|
|
</React.Suspense>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
ref={this.contentRef}
|
|
className="contents"
|
|
onScroll={this.handleScroll}
|
|
style={{ height: !this.state.user?.cover ? "100vh" : undefined }}
|
|
|
|
>
|
|
<div className="tabMenuWrapper">
|
|
<antd.Menu
|
|
className="tabMenu"
|
|
mode="vertical"
|
|
selectedKeys={[this.state.tabActiveKey]}
|
|
onClick={(e) => this.handlePageTransition(e.key)}
|
|
>
|
|
<antd.Menu.Item key="posts">
|
|
Posts
|
|
</antd.Menu.Item>
|
|
|
|
<antd.Menu.Item key="followers">
|
|
Followers
|
|
</antd.Menu.Item>
|
|
|
|
<antd.Menu.Item key="details">
|
|
Details
|
|
</antd.Menu.Item>
|
|
</antd.Menu>
|
|
</div>
|
|
|
|
<div className="tabContent">
|
|
<TabRender
|
|
renderKey={this.state.tabActiveKey}
|
|
state={this.state}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
} |