improve mobile mode for account page

This commit is contained in:
SrGooglo 2023-07-08 18:28:10 +00:00
parent 6c59b8b850
commit 0a54cc6f1a
8 changed files with 272 additions and 104 deletions

View File

@ -2,59 +2,6 @@
@borderRadius: 12px;
#root {
&.mobile {
.accountProfile {
display: flex;
flex-direction: column;
.panels {
display: flex;
flex-direction: column;
.tabMenuWrapper {
position: fixed;
top: 0px;
left: 0px;
z-index: 300;
width: 100%;
height: @top_bar_height;
padding: @top_bar_padding;
box-shadow: @card-shadow-top;
display: flex;
flex-direction: row;
align-items: center;
opacity: 0;
.ant-menu {
display: flex;
flex-direction: row;
.ant-menu-item {
width: fit-content;
}
}
}
}
&.topHidden {
.tabMenuWrapper {
opacity: 1;
}
}
}
}
}
.accountProfile {
display: flex;
@ -123,15 +70,18 @@
.panels {
position: relative;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 1fr;
grid-column-gap: 20px;
grid-row-gap: 0px;
display: flex;
// grid-template-columns: repeat(3, 1fr);
// grid-template-rows: 1fr;
// grid-column-gap: 20px;
// grid-row-gap: 0px;
gap: 20px;
padding-top: 20px;
height: 100%;
width: 100%;
.leftPanel {
position: sticky;
@ -178,6 +128,7 @@
.content {
height: 100%;
width: 90%;
.fade-opacity-active {
height: 100%;
@ -196,19 +147,14 @@
padding: 20px;
height: fit-content;
width: 20vw;
min-width: 400px;
width: fit-content;
//width: 20vw;
// min-width: 400px;
justify-self: center;
}
}
.details {
background-color: var(--background-color-accent);
padding: 20px;
border-radius: @borderRadius;
}
.followersList {
background-color: var(--background-color-accent);
padding: 20px;

View File

@ -4,11 +4,12 @@ import classnames from "classnames"
import { Translation } from "react-i18next"
import { createIconRender, Icons } from "components/Icons"
import { Skeleton, FollowButton, UserCard } from "components"
import { Skeleton } from "components"
import { SessionModel, UserModel, FollowsModel } from "models"
import { PagePanelWithNavMenu } from "components/PagePanels"
import { MobileUserCard } from "components/UserCard"
import DetailsTab from "./tabs/details"
import PostsTab from "./tabs/posts"
import FollowersTab from "./tabs/followers"
@ -58,6 +59,59 @@ export default class Account extends React.Component {
}
componentDidMount = async () => {
app.layout.toggleCenteredContent(true)
this.loadUser()
}
componentWillUnmount = () => {
app.layout.toggleCenteredContent(false)
}
toggleLike = async () => {
const accept = await new Promise((resolve, reject) => {
antd.Modal.confirm({
title: <Translation>
{t => t("Confirm")}
</Translation>,
content: <Translation>
{t => t("Are you sure you want to unfollow this user?")}
</Translation>,
okText: <Translation>
{t => t("Yes")}
</Translation>,
cancelText: <Translation>
{t => t("No")}
</Translation>,
onOk: () => {
resolve(true)
},
onCancel: () => {
resolve(false)
}
})
})
if (!accept) {
return false
}
const result = await FollowsModel.toggleFollow({
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,
})
}
loadUser = async () => {
const token = await SessionModel.getDecodedToken()
const location = window.app.history.location
const query = new URLSearchParams(location.search)
@ -137,18 +191,28 @@ export default class Account extends React.Component {
"_mobile_account-profile",
)}
>
<UserCard
<MobileUserCard
user={user}
isSelf={this.state.isSelf}
isFollowed={this.state.isFollowed}
followers={this.state.followers}
onClickFollow={this.toggleLike}
/>
{/* <PagePanelWithNavMenu
<PagePanelWithNavMenu
tabs={Tabs}
useSetQueryType
transition
tabProps={{
state: this.state,
}}
/> */}
onTabChange={() => {
app.layout.scrollTo({
top: 0,
})
}}
no_top_padding
/>
</div>
}
}

View File

@ -1,10 +1,12 @@
@import "theme/vars.less";
._mobile_account-profile {
display: flex;
flex-direction: column;
padding: 0 10px;
width: 100%;
padding-top: calc(@topBar_height + @top_bar_padding);
gap: 20px;
}

View File

@ -1,46 +1,113 @@
import React from "react"
import moment from "moment"
import { DateTime } from "luxon"
import { Skeleton } from "antd"
import { UserBadges } from "components"
import "./details.less"
import { Icons } from "components/Icons"
export default React.memo((props) => {
function getJoinLabel(jsDate) {
const date = DateTime.fromJSDate(new Date(jsDate))
const month = String(date.toLocaleString({ month: "long" })).toTitleCase()
const year = String(date.year)
return `${month} ${year}`
}
export default (props) => {
return <div id="details" className="details">
{
props.state.user.fullName &&
<div>
<h2>{props.state.user.fullName}</h2>
props.state.user.roles.includes("admin") && <div className="inline_field">
<div className="field_header">
<div className="field_icon">
<Icons.MdAdminPanelSettings />
</div>
<span>
Administrators Team
</span>
</div>
</div>
}
<div>
<h3>
@{props.state.user.username} #{props.state.user._id}
</h3>
<div className="inline_field">
<div className="field_header">
<div className="field_icon">
<Icons.MdTag />
</div>
<span>
ID
</span>
</div>
<div className="field_value">
<p>
{props.state.user._id}
</p>
</div>
</div>
{
props.state.user.description &&
<div>
<h4>
{props.state.user.description}
</h4>
<div className="inline_field">
<div className="field_header">
<div className="field_icon">
<Icons.Users />
</div>
<span>
Followers
</span>
</div>
}
{
props.state.user.roles.includes("admin") &&
<div>
<span><Icons.MdAdminPanelSettings /> Administrators Team</span>
<div className="field_value">
<p>
{props.state.followers.length}
</p>
</div>
}
<div>
<span><Icons.Users /> {props.state.followers.length} Followers</span>
</div>
{
props.state.user?.badges.length > 0 &&
<div>
<span><Icons.Award /> {props.state.user?.badges.length} Badges collected</span>
<div className="inline_field">
<div className="field_header">
<div className="field_icon">
<Icons.Calendar />
</div>
<span>
Joined at
</span>
</div>
<div className="field_value">
<p>
{
getJoinLabel(Number(props.state.user.createdAt))
}
</p>
</div>
}
<div>
<span><Icons.Calendar /> Joined at {moment(new Date(Number(props.state.user.createdAt))).format("YYYY")}</span>
</div>
<div className="inline_field">
<div className="field_header">
<div className="field_icon">
<Icons.Award />
</div>
<span>
Badges collected
</span>
</div>
<div className="field_value">
<p>
{props.state.user?.badges.length}
</p>
</div>
</div>
<React.Suspense fallback={<Skeleton />}>
<UserBadges user_id={props.state.user?._id} />
</React.Suspense>
</div>
})
}

View File

@ -0,0 +1,78 @@
@border-radius: 12px;
.details {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
padding: 15px;
gap: 10px;
background-color: var(--background-color-accent);
border-radius: @border-radius;
h1,
h2,
h3,
h4,
h5,
h6,
p {
margin: 0;
}
.inline_field {
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 10px;
background-color: var(--background-color-primary);
border-radius: 12px;
.field_header {
display: inline-flex;
flex-direction: row;
align-items: center;
gap: 8px;
font-size: 0.8rem;
.field_icon {
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
background-color: var(--background-color-primary);
border-radius: 100%;
padding: 5px;
font-size: 1rem;
svg {
margin: 0;
}
}
}
.field_value {
font-family: "DM Mono", monospace;
}
}
}

View File

@ -2,6 +2,8 @@ import React from "react"
import { FollowersList } from "components"
import "./followers.less"
export default React.memo((props) => {
return <FollowersList
user_id={props.state.user._id}

View File

@ -0,0 +1,3 @@
.followersList {
width: 100%;
}

View File

@ -28,8 +28,14 @@ export default class Home extends React.Component {
tabs={Tabs}
navMenuHeader={navMenuHeader}
primaryPanelClassName="full"
onTabChange={() => {
app.layout.scrollTo({
top: 0,
})
}}
useSetQueryType
transition
masked
/>
}
}