mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
rework account layout style
This commit is contained in:
parent
cd853cd4e9
commit
11126914e6
@ -1,12 +1,11 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import classnames from "classnames"
|
import classnames from "classnames"
|
||||||
import Loadable from "react-loadable"
|
|
||||||
import { Translation } from "react-i18next"
|
import { Translation } from "react-i18next"
|
||||||
|
|
||||||
import { Icons, createIconRender } from "components/Icons"
|
import { Icons } from "components/Icons"
|
||||||
import { Image, Skeleton, FollowButton } from "components"
|
import { Skeleton, FollowButton, UserCard } from "components"
|
||||||
import { Session, User } from "models"
|
import { Session } from "models"
|
||||||
|
|
||||||
import DetailsTab from "./tabs/details"
|
import DetailsTab from "./tabs/details"
|
||||||
import PostsTab from "./tabs/posts"
|
import PostsTab from "./tabs/posts"
|
||||||
@ -52,35 +51,6 @@ const TabRender = React.memo((props, ref) => {
|
|||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
|
|
||||||
const UserBadges = React.memo((props) => {
|
|
||||||
return React.createElement(Loadable({
|
|
||||||
loader: async () => {
|
|
||||||
let { user_id } = props
|
|
||||||
|
|
||||||
const badgesData = await User.getUserBadges(user_id).catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
|
|
||||||
app.message.error("Failed to fetch user badges")
|
|
||||||
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
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 {
|
export default class Account extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
requestedUser: null,
|
requestedUser: null,
|
||||||
@ -100,7 +70,9 @@ export default class Account extends React.Component {
|
|||||||
|
|
||||||
coverComponent = React.createRef()
|
coverComponent = React.createRef()
|
||||||
|
|
||||||
tabNavigatorRef = React.createRef()
|
leftPanelRef = React.createRef()
|
||||||
|
|
||||||
|
actionsRef = React.createRef()
|
||||||
|
|
||||||
api = window.app.api.withEndpoints("main")
|
api = window.app.api.withEndpoints("main")
|
||||||
|
|
||||||
@ -131,6 +103,8 @@ export default class Account extends React.Component {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`Loaded User [${user.username}] >`, user)
|
||||||
|
|
||||||
if (!isSelf) {
|
if (!isSelf) {
|
||||||
const followedResult = await this.api.get.isFollowed(undefined, { user_id: user._id }).catch(() => false)
|
const followedResult = await this.api.get.isFollowed(undefined, { user_id: user._id }).catch(() => false)
|
||||||
|
|
||||||
@ -154,11 +128,25 @@ export default class Account extends React.Component {
|
|||||||
followers,
|
followers,
|
||||||
})
|
})
|
||||||
|
|
||||||
app.eventBus.emit("style.compactMode", true)
|
// create intersection observer for cover
|
||||||
|
this.coverIntersectionObserver = new IntersectionObserver((e) => {
|
||||||
|
if (e[0].intersectionRatio > 0) {
|
||||||
|
this.leftPanelRef.current.style.transform = "translate(0, -100px)"
|
||||||
|
this.actionsRef.current.style.opacity = "1"
|
||||||
|
} else {
|
||||||
|
this.leftPanelRef.current.style.transform = "translate(0, 0)"
|
||||||
|
this.actionsRef.current.style.opacity = "0"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
root: document.querySelector("#root"),
|
||||||
|
threshold: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
this.coverIntersectionObserver.observe(this.coverComponent.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount = () => {
|
componentWillUnmount = () => {
|
||||||
app.eventBus.emit("style.compactMode", false)
|
this.coverIntersectionObserver.disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchData = async (username) => {
|
fetchData = async (username) => {
|
||||||
@ -210,29 +198,6 @@ export default class Account extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
if (window.isMobile) {
|
|
||||||
this.tabNavigatorRef.current.style.overflow = "hidden"
|
|
||||||
this.tabNavigatorRef.current.style.height = "0px"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.coverComponent.current.style.height = ""
|
|
||||||
|
|
||||||
if (window.isMobile) {
|
|
||||||
this.tabNavigatorRef.current.style.overflow = ""
|
|
||||||
this.tabNavigatorRef.current.style.height = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const user = this.state.user
|
const user = this.state.user
|
||||||
|
|
||||||
@ -249,65 +214,55 @@ export default class Account extends React.Component {
|
|||||||
return <Skeleton />
|
return <Skeleton />
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="accountProfile">
|
return <div
|
||||||
{user.cover && <div
|
className="accountProfile"
|
||||||
className={classnames("cover", {
|
id="profile"
|
||||||
["expanded"]: this.state.coverExpanded
|
>
|
||||||
})}
|
{
|
||||||
ref={this.coverComponent}
|
user.cover && <div
|
||||||
style={{ backgroundImage: `url("${user.cover}")` }}
|
className={classnames("cover", {
|
||||||
onClick={() => this.toogleCoverExpanded()}
|
["expanded"]: this.state.coverExpanded
|
||||||
/>}
|
})}
|
||||||
<div className="profileCardWrapper">
|
ref={this.coverComponent}
|
||||||
<div className="profileCard">
|
style={{ backgroundImage: `url("${user.cover}")` }}
|
||||||
<div className="basicData">
|
onClick={() => this.toogleCoverExpanded()}
|
||||||
<div className="title">
|
id="profile-cover"
|
||||||
<div className="field">
|
/>
|
||||||
<div className="avatar">
|
}
|
||||||
<Image
|
|
||||||
alt="ProfileImage"
|
|
||||||
src={user.avatar}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="field">
|
<div className="panels">
|
||||||
<div>
|
<div
|
||||||
<h1>{user.fullName ?? user.username}</h1>
|
className="leftPanel"
|
||||||
{user.verified && <Icons.verifiedBadge />}
|
ref={this.leftPanelRef}
|
||||||
</div>
|
>
|
||||||
|
<UserCard
|
||||||
|
user={user}
|
||||||
|
/>
|
||||||
|
|
||||||
<span>@{user.username}</span>
|
<div
|
||||||
</div>
|
className="actions"
|
||||||
</div>
|
ref={this.actionsRef}
|
||||||
|
>
|
||||||
<div>
|
<FollowButton
|
||||||
<FollowButton
|
count={this.state.followers.length}
|
||||||
count={this.state.followers.length}
|
onClick={this.onClickFollow}
|
||||||
onClick={this.onClickFollow}
|
followed={this.state.isFollowed}
|
||||||
followed={this.state.isFollowed}
|
self={this.state.isSelf}
|
||||||
self={this.state.isSelf}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="description">
|
|
||||||
<p>
|
|
||||||
{user.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="badgesTab">
|
<div
|
||||||
<React.Suspense fallback={<antd.Skeleton />}>
|
className="content"
|
||||||
<UserBadges user_id={user._id} />
|
ref={this.contentRef}
|
||||||
</React.Suspense>
|
>
|
||||||
|
<TabRender
|
||||||
|
renderKey={this.state.tabActiveKey}
|
||||||
|
state={this.state}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="contents">
|
<div className="tabMenuWrapper">
|
||||||
<div className="tabMenuWrapper" ref={this.tabNavigatorRef}>
|
|
||||||
<antd.Menu
|
<antd.Menu
|
||||||
className="tabMenu"
|
className="tabMenu"
|
||||||
mode={window.isMobile ? "horizontal" : "vertical"}
|
mode={window.isMobile ? "horizontal" : "vertical"}
|
||||||
@ -342,13 +297,6 @@ export default class Account extends React.Component {
|
|||||||
</antd.Menu.Item>
|
</antd.Menu.Item>
|
||||||
</antd.Menu>
|
</antd.Menu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="tabContent" ref={this.contentRef} onScroll={this.handleScroll}>
|
|
||||||
<TabRender
|
|
||||||
renderKey={this.state.tabActiveKey}
|
|
||||||
state={this.state}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -1,208 +1,107 @@
|
|||||||
@borderRadius: 12px;
|
@borderRadius: 12px;
|
||||||
|
|
||||||
.accountProfile {
|
.accountProfile {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
padding: 10px 20px;
|
|
||||||
|
|
||||||
// max-width: 70vw;
|
|
||||||
// min-width: 900px;
|
|
||||||
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
|
|
||||||
.cover {
|
.cover {
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: @borderRadius @borderRadius 0 0;
|
|
||||||
|
|
||||||
outline: 1px solid var(--border-color);
|
outline: 1px solid var(--border-color);
|
||||||
|
border-radius: @borderRadius;
|
||||||
content: "";
|
content: "";
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 25vh;
|
||||||
|
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
|
||||||
height: 25vh;
|
|
||||||
|
|
||||||
transform: translate(0, 10px);
|
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease-in-out;
|
||||||
|
|
||||||
&.expanded {
|
&.expanded {
|
||||||
height: 70vh;
|
height: 70vh;
|
||||||
transform: translate(0, 0);
|
|
||||||
|
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
border-radius: @borderRadius;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.profileCardWrapper {
|
.panels {
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 150;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profileCard {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 150;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
color: var(--background-color-contrast);
|
|
||||||
background-color: var(--background-color-primary);
|
|
||||||
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: @borderRadius @borderRadius 0 @borderRadius;
|
|
||||||
|
|
||||||
padding: 20px 15px;
|
|
||||||
|
|
||||||
height: 15vh;
|
|
||||||
min-height: 175px;
|
|
||||||
|
|
||||||
.basicData {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
margin-bottom: 15px;
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: var(--appColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
margin-right: 15px;
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
font-size: 35px;
|
|
||||||
line-height: 37px;
|
|
||||||
}
|
|
||||||
|
|
||||||
>div {
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
max-height: 5vh;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
p {
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6,
|
|
||||||
span,
|
|
||||||
p {
|
|
||||||
user-select: text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.badgesTab {
|
|
||||||
position: relative;
|
|
||||||
z-index: 140;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
padding: 30px 10px 10px 10px;
|
|
||||||
|
|
||||||
background-color: var(--background-color-accent);
|
|
||||||
|
|
||||||
border-radius: 0 0 @borderRadius @borderRadius;
|
|
||||||
transform: translate(0, -20px);
|
|
||||||
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
margin-left: 10px;
|
|
||||||
height: fit-content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabMenuWrapper {
|
|
||||||
position: sticky !important;
|
|
||||||
top: 20px;
|
|
||||||
|
|
||||||
height: fit-content;
|
|
||||||
|
|
||||||
.tabMenu {
|
|
||||||
width: fit-content;
|
|
||||||
min-width: 10vw;
|
|
||||||
|
|
||||||
padding: 10px;
|
|
||||||
background-color: var(--background-color-accent) !important;
|
|
||||||
border-radius: @borderRadius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.contents {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 10vw 1fr 10vw;
|
grid-template-columns: repeat(3, 1fr);
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
grid-column-gap: 50px;
|
grid-column-gap: 20px;
|
||||||
grid-row-gap: 0px;
|
grid-row-gap: 0px;
|
||||||
|
|
||||||
width: 100%;
|
padding-top: 20px;
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
overflow: hidden;
|
.leftPanel {
|
||||||
overflow-y: hidden;
|
|
||||||
|
|
||||||
margin-top: -1.5vh;
|
|
||||||
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
|
|
||||||
.tabContent {
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
border-radius: @borderRadius;
|
top: 0;
|
||||||
|
|
||||||
padding: 20px 10px 20vh 10px;
|
z-index: 55;
|
||||||
|
|
||||||
overflow-y: overlay;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
|
||||||
|
.userCard {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
height: fit-content;
|
||||||
|
width: 20vw;
|
||||||
|
min-width: 400px;
|
||||||
|
|
||||||
|
padding: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
background-color: var(--background-color-accent);
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
|
||||||
|
.followButton {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabMenuWrapper {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
background-color: var(--background-color-accent);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
height: fit-content;
|
||||||
|
width: 20vw;
|
||||||
|
min-width: 400px;
|
||||||
|
|
||||||
|
justify-self: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,18 +109,11 @@
|
|||||||
background-color: var(--background-color-accent);
|
background-color: var(--background-color-accent);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: @borderRadius;
|
border-radius: @borderRadius;
|
||||||
|
}
|
||||||
|
|
||||||
font-family: "DM Mono", monospace;
|
.followersList {
|
||||||
|
background-color: var(--background-color-accent);
|
||||||
h1,
|
padding: 20px;
|
||||||
h2,
|
border-radius: @borderRadius;
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6,
|
|
||||||
p,
|
|
||||||
span {
|
|
||||||
user-select: text;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user