Merge pull request #82 from hyperspacedev/profile-page-81

Redesign profile page
This commit is contained in:
Marquis Kurt 2019-09-16 13:52:42 -04:00 committed by GitHub
commit a709872e0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 152 additions and 71 deletions

View File

@ -69,7 +69,7 @@
],
"build": {
"appId": "net.marquiskurt.hyperspace",
"afterSign": "desktop/notarize.js",
"afterSign": "desktop/notarize.js",
"directories": {
"buildResources": "desktop"
},

View File

@ -1,5 +1,6 @@
import { Theme, createStyles } from "@material-ui/core";
import { isDarwinApp } from "../utilities/desktop";
import { isAppbarExpanded } from "../utilities/appbar";
export const styles = (theme: Theme) => createStyles({
root: {
@ -69,7 +70,8 @@ export const styles = (theme: Theme) => createStyles({
backgroundColor: theme.palette.primary.dark,
width: '100%',
color: theme.palette.common.white,
zIndex: 1
zIndex: 1,
top: isAppbarExpanded()? 80: 64,
},
pageHeroBackgroundImage: {
position: 'absolute',
@ -80,18 +82,19 @@ export const styles = (theme: Theme) => createStyles({
backgroundSize: 'cover',
height: '100%',
width: '100%',
opacity: 0.40,
zIndex: -1
opacity: 0.35,
zIndex: -1,
filter: 'blur(2px)'
},
pageHeroContent: {
padding: 16,
paddingTop: 116,
paddingTop: 8,
textAlign: 'center',
width: '100%',
height: '100%',
[theme.breakpoints.up('md')]: {
paddingLeft: '25%',
paddingRight: '25%',
paddingLeft: '5%',
paddingRight: '5%',
},
position: "relative",
zIndex: 1
@ -110,6 +113,38 @@ export const styles = (theme: Theme) => createStyles({
},
//backgroundColor: theme.palette.background.default
},
profileToolbar: {
zIndex: 2,
paddingTop: 8,
},
profileContent: {
padding: 16,
[theme.breakpoints.up('md')]: {
paddingLeft: '5%',
paddingRight: '5%',
paddingBottom: 48,
paddingTop: 24,
},
width: '100%',
height: '100%',
position: 'relative',
zIndex: 1,
display: 'flex',
paddingBottom: 24,
paddingTop: 24,
},
profileAvatar: {
width: 64,
height: 64,
[theme.breakpoints.up('md')]: {
width: 128,
height: 128,
},
backgroundColor: theme.palette.primary.main
},
profileUserBox: {
paddingLeft: theme.spacing.unit * 2
},
pageProfileAvatar: {
width: 128,
height: 128,
@ -120,6 +155,7 @@ export const styles = (theme: Theme) => createStyles({
},
pageProfileNameEmoji: {
height: theme.typography.h4.fontSize,
fontWeight: theme.typography.fontWeightMedium,
},
pageProfileStatsDiv: {
display: 'inline-flex',
@ -139,9 +175,9 @@ export const styles = (theme: Theme) => createStyles({
pageContentLayoutConstraints: {
paddingLeft: theme.spacing.unit,
paddingRight: theme.spacing.unit,
paddingTop: theme.spacing.unit * 4,
paddingTop: theme.spacing.unit * 12,
paddingBottom: theme.spacing.unit * 2,
[theme.breakpoints.up('md')]: {
[theme.breakpoints.up('lg')]: {
paddingLeft: theme.spacing.unit * 32,
paddingRight: theme.spacing.unit * 32
},
@ -239,5 +275,8 @@ export const styles = (theme: Theme) => createStyles({
top: theme.spacing.unit,
right: theme.spacing.unit,
color: theme.palette.common.white
},
pageGrow: {
flexGrow: 1
}
});

View File

@ -1,5 +1,21 @@
import React, {Component} from 'react';
import {withStyles, Typography, Avatar, Divider, Button, CircularProgress, Paper, Tooltip, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions} from '@material-ui/core';
import {
withStyles,
Typography,
Avatar,
Divider,
Button,
CircularProgress,
Paper,
Tooltip,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
Toolbar,
IconButton
} from '@material-ui/core';
import {styles} from './PageLayout.styles';
import Mastodon from 'megalodon';
import { Account } from '../types/Account';
@ -7,10 +23,19 @@ import { Status } from '../types/Status';
import { Relationship } from '../types/Relationship';
import Post from '../components/Post';
import {withSnackbar} from 'notistack';
import { LinkableButton, LinkableIconButton } from '../interfaces/overrides';
import { LinkableIconButton } from '../interfaces/overrides';
import { emojifyString } from '../utilities/emojis';
import AccountEditIcon from 'mdi-material-ui/AccountEdit';
import PersonAddIcon from '@material-ui/icons/PersonAdd';
import PersonAddDisabledIcon from '@material-ui/icons/PersonAddDisabled';
import AccountMinusIcon from 'mdi-material-ui/AccountMinus';
import ChatIcon from '@material-ui/icons/Chat';
import AccountRemoveIcon from 'mdi-material-ui/AccountRemove';
import AccountHeartIcon from 'mdi-material-ui/AccountHeart';
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
interface IProfilePageState {
account?: Account;
@ -112,23 +137,6 @@ class ProfilePage extends Component<any, IProfilePageState> {
});
}
statElement(classes: any, stat: 'following' | 'followers' | 'posts') {
let number = 0;
if (this.state.account) {
if (stat == 'following') {
number = this.state.account.following_count;
} else if (stat == 'followers') {
number = this.state.account.followers_count;
} else if (stat == 'posts') {
number = this.state.account.statuses_count;
}
}
return <div className={classes.pageProfileStat}>
<Typography variant="h6" color="inherit">{number}</Typography>
<Typography color="inherit">{stat}</Typography>
</div>;
}
loadMoreTimelinePieces() {
const { match: {params}} = this.props;
this.setState({ viewDidLoad: false, viewIsLoading: true})
@ -225,50 +233,73 @@ class ProfilePage extends Component<any, IProfilePageState> {
<div className={classes.pageLayoutMinimalConstraints}>
<div className={classes.pageHeroBackground}>
<div className={classes.pageHeroBackgroundImage} style={{ backgroundImage: this.state.account? `url("${this.state.account.header}")`: `url("")`}}/>
<div className={classes.pageHeroContent}>
{
this.isItMe()?
<Tooltip title="Edit profile">
<LinkableIconButton to="/you" color="inherit" className={classes.pageHeroToolbar}>
<AccountEditIcon/>
</LinkableIconButton>
</Tooltip>: null
}
<Avatar className={classes.pageProfileAvatar} src={this.state.account ? this.state.account.avatar: ""}/>
<Typography variant="h4" color="inherit" dangerouslySetInnerHTML={{__html: this.state.account? emojifyString(this.state.account.display_name, this.state.account.emojis, classes.pageProfileNameEmoji): ""}}></Typography>
<Typography variant="caption" color="inherit">{this.state.account ? '@' + this.state.account.acct: ""}</Typography>
<Typography paragraph color="inherit">{this.state.account ? this.state.account.note: ""}</Typography>
<Divider/>
<div className={classes.pageProfileStatsDiv}>
{this.statElement(classes, 'followers')}
{this.statElement(classes, 'following')}
{this.statElement(classes, 'posts')}
<Toolbar className={classes.profileToolbar}>
<div className={classes.pageGrow}/>
<Tooltip title={
this.isItMe()?
"You can't follow yourself.":
this.state.relationship && this.state.relationship.following?
"Unfollow":
"Follow"
}>
<IconButton color={"inherit"} disabled={this.isItMe()} onClick={() => this.toggleFollow()}>
{
this.isItMe()?
<PersonAddDisabledIcon/>:
this.state.relationship && this.state.relationship.following?
<AccountMinusIcon/>:
<PersonAddIcon/>
}
</IconButton>
</Tooltip>
<Tooltip title={"Send a message or post"}>
<LinkableIconButton to={`/compose?acct=${this.state.account? this.state.account.acct: ""}`} color={"inherit"}>
<ChatIcon/>
</LinkableIconButton>
</Tooltip>
<Tooltip title={this.state.relationship && this.state.relationship.blocking? "Unblock this account": "Block this account"}>
<IconButton color={"inherit"} disabled={this.isItMe()} onClick={() => this.toggleBlockDialog()}>
{
this.state.relationship && this.state.relationship.blocking? <AccountHeartIcon/>: <AccountRemoveIcon/>
}
</IconButton>
</Tooltip>
<Tooltip title="Open in web">
<IconButton href={this.state.account? this.state.account.url: ""} target="_blank" rel={"nofollower noreferrer noopener"} color={"inherit"}>
<OpenInNewIcon/>
</IconButton>
</Tooltip>
{
this.isItMe()?
<Tooltip title="Edit profile">
<LinkableIconButton to="/you" color="inherit">
<AccountEditIcon/>
</LinkableIconButton>
</Tooltip>: null
}
</Toolbar>
<div className={classes.profileContent}>
<Avatar className={classes.profileAvatar} src={this.state.account ? this.state.account.avatar: ""}/>
<div className={classes.profileUserBox}>
<Typography variant="h4" color="inherit" dangerouslySetInnerHTML={
{__html: this.state.account?
this.state.account.display_name?
emojifyString(this.state.account.display_name, this.state.account.emojis, classes.pageProfileNameEmoji)
: this.state.account.username
: ""}}
className={classes.pageProfileNameEmoji}/>
<Typography variant="caption" color="inherit">{this.state.account ? '@' + this.state.account.acct: ""}</Typography>
<Typography paragraph color="inherit">{
this.state.account ?
this.state.account.note?
this.state.account.note
: "No bio provided by user."
: "No bio available."
}</Typography>
<Typography color={"inherit"}>
{this.state.account? this.state.account.followers_count: 0} followers | {this.state.account? this.state.account.following_count: 0} following | {this.state.account? this.state.account.statuses_count: 0} posts
</Typography>
</div>
<Divider/>
{
this.state.relationship?
<div>
<Button
variant="contained"
color="primary"
className={classes.pageProfileFollowButton}
onClick={() => this.toggleFollow()}
disabled={this.state.account? this.state.account.id === JSON.parse(localStorage.getItem('account') as string).id: false}
>
{this.state.relationship.following? "Unfollow": "Follow"}
</Button>
<LinkableButton to={`/compose?mention=${this.state.account? this.state.account.acct: ""}`} variant="contained" className={classes.pageProfileFollowButton}>Mention</LinkableButton>
<Button
variant="contained"
className={classes.pageProfileFollowButton}
disabled={this.state.account? this.state.account.id === JSON.parse(localStorage.getItem('account') as string).id: false}
onClick={() => this.toggleBlockDialog()}
>
{this.state.relationship.blocking? "Unblock": "Block"}
</Button>
</div>: null
}
</div>
</div>
<div className={classes.pageContentLayoutConstraints}>

11
src/utilities/appbar.tsx Normal file
View File

@ -0,0 +1,11 @@
import { isDarwinApp } from "./desktop";
/**
* Determine whether the title bar is being displayed.
* This might be useful in cases where styles are dependent on the title bar's visibility, such as heights.
*
* @returns Boolean dictating if the title bar is visible
*/
export function isAppbarExpanded(): boolean {
return isDarwinApp() || process.env.NODE_ENV === "development";
}