hyperspace-desktop-client-w.../src/components/AppLayout/AppLayout.tsx

299 lines
13 KiB
TypeScript
Raw Normal View History

2019-03-26 02:37:02 +01:00
import React, { Component } from 'react';
2019-04-04 02:01:54 +02:00
import { Typography, AppBar, Toolbar, IconButton, InputBase, Avatar, ListItemText, Divider, List, ListItemIcon, Hidden, Drawer, ListSubheader, ListItemAvatar, withStyles, Menu, MenuItem, ClickAwayListener, Badge } from '@material-ui/core';
2019-03-26 02:37:02 +01:00
import MenuIcon from '@material-ui/icons/Menu';
import SearchIcon from '@material-ui/icons/Search';
import NotificationsIcon from '@material-ui/icons/Notifications';
import MailIcon from '@material-ui/icons/Mail';
import HomeIcon from '@material-ui/icons/Home';
import DomainIcon from '@material-ui/icons/Domain';
import PublicIcon from '@material-ui/icons/Public';
import GroupIcon from '@material-ui/icons/Group';
import SettingsIcon from '@material-ui/icons/Settings';
import InfoIcon from '@material-ui/icons/Info';
2019-04-04 02:01:54 +02:00
import EditIcon from '@material-ui/icons/Edit';
2019-03-26 02:37:02 +01:00
import SupervisedUserCircleIcon from '@material-ui/icons/SupervisedUserCircle';
import ExitToAppIcon from '@material-ui/icons/ExitToApp';
import {styles} from './AppLayout.styles';
2019-03-28 00:24:52 +01:00
import { UAccount } from '../../types/Account';
2019-04-04 02:01:54 +02:00
import {LinkableListItem, LinkableIconButton, LinkableFab} from '../../interfaces/overrides';
import Mastodon from 'megalodon';
import { Notification } from '../../types/Notification';
import {sendNotificationRequest} from '../../utilities/notifications';
2019-03-26 02:37:02 +01:00
interface IAppLayoutState {
2019-03-28 03:01:55 +01:00
acctMenuOpen: boolean;
2019-03-26 02:37:02 +01:00
drawerOpenOnMobile: boolean;
2019-03-28 00:24:52 +01:00
currentUser: UAccount;
notificationCount: number;
2019-03-26 02:37:02 +01:00
}
export class AppLayout extends Component<any, IAppLayoutState> {
client: Mastodon;
streamListener: any;
2019-03-26 02:37:02 +01:00
constructor(props: any) {
super(props);
2019-03-28 03:01:55 +01:00
let accountData = JSON.parse(localStorage.getItem('account') as string);
this.client = new Mastodon(localStorage.getItem('access_token') as string, localStorage.getItem('baseurl') as string + "/api/v1");
2019-03-26 02:37:02 +01:00
this.state = {
2019-03-28 00:24:52 +01:00
drawerOpenOnMobile: false,
2019-03-28 03:01:55 +01:00
acctMenuOpen: false,
currentUser: accountData,
notificationCount: 0
2019-03-26 02:37:02 +01:00
}
this.toggleDrawerOnMobile = this.toggleDrawerOnMobile.bind(this);
2019-03-28 03:01:55 +01:00
this.toggleAcctMenu = this.toggleAcctMenu.bind(this);
}
componentDidMount() {
this.streamListener = this.client.stream('/streaming/user');
this.streamListener.on('notification', (notif: Notification) => {
const notificationCount = this.state.notificationCount + 1;
this.setState({ notificationCount });
if (!document.hasFocus()) {
let primaryMessage = "";
let secondaryMessage = "";
switch(notif.type) {
case "favourite":
primaryMessage = (notif.account.display_name || "@" + notif.account.username) + " favorited your post.";
if (notif.status) {
const div = document.createElement('div');
div.innerHTML = notif.status.content;
secondaryMessage = (div.textContent || div.innerText || "").slice(0, 100) + "..."
}
break;
case "follow":
primaryMessage = (notif.account.display_name || "@" + notif.account.username) + " is now following you.";
break;
case "mention":
primaryMessage = (notif.account.display_name || "@" + notif.account.username) + " mentioned you in a post.";
if (notif.status) {
const div = document.createElement('div');
div.innerHTML = notif.status.content;
secondaryMessage = (div.textContent || div.innerText || "").slice(0, 100) + "..."
}
break;
case "reblog":
primaryMessage = (notif.account.display_name || "@" + notif.account.username) + " reblogged your post.";
if (notif.status) {
const div = document.createElement('div');
div.innerHTML = notif.status.content;
secondaryMessage = (div.textContent || div.innerText || "").slice(0, 100) + "..."
}
break;
}
sendNotificationRequest(primaryMessage, secondaryMessage);
}
});
}
2019-03-28 03:01:55 +01:00
toggleAcctMenu() {
this.setState({ acctMenuOpen: !this.state.acctMenuOpen });
2019-03-26 02:37:02 +01:00
}
toggleDrawerOnMobile() {
this.setState({
drawerOpenOnMobile: !this.state.drawerOpenOnMobile
})
}
2019-04-02 23:28:04 +02:00
searchForQuery(what: string) {
window.location.href = "/#/search?query=" + what;
window.location.reload;
}
2019-03-26 02:37:02 +01:00
titlebar() {
const { classes } = this.props;
if (process.env.NODE_ENV === "development") {
return (
<div className={classes.titleBarRoot}>
2019-03-28 21:46:19 +01:00
<Typography className={classes.titleBarText}>Careful: you're running in developer mode.</Typography>
2019-03-26 02:37:02 +01:00
</div>
);
} else if ((navigator.userAgent.includes("Hyperspace") || navigator.userAgent.includes("Electron")) && navigator.userAgent.includes("Macintosh")) {
return (
<div className={classes.titleBarRoot}>
<Typography className={classes.titleBarText}>Hyperspace</Typography>
</div>
);
}
}
appDrawer() {
const { classes } = this.props;
return (
<div>
<List>
<div className={classes.drawerDisplayMobile}>
2019-03-28 03:01:55 +01:00
<LinkableListItem button key="profile-mobile" to={`/profile/${this.state.currentUser.id}`}>
2019-03-26 02:37:02 +01:00
<ListItemAvatar>
2019-03-28 00:24:52 +01:00
<Avatar alt="You" src={this.state.currentUser.avatar_static}/>
2019-03-26 02:37:02 +01:00
</ListItemAvatar>
2019-03-28 00:24:52 +01:00
<ListItemText primary={this.state.currentUser.display_name} secondary={this.state.currentUser.acct}/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
<LinkableListItem button key="notifications-mobile" to="/notifications">
2019-03-26 02:37:02 +01:00
<ListItemIcon><NotificationsIcon/></ListItemIcon>
<ListItemText primary="Notifications"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
<LinkableListItem button key="messages-mobile" to="/messages">
2019-03-26 02:37:02 +01:00
<ListItemIcon><MailIcon/></ListItemIcon>
<ListItemText primary="Messages"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
<LinkableListItem button key="acctSwitch-module" to="/switchacct">
2019-03-26 02:37:02 +01:00
<ListItemIcon><SupervisedUserCircleIcon/></ListItemIcon>
<ListItemText primary="Switch account"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
<LinkableListItem button key="acctLogout-mobile" to="/logout">
2019-03-26 02:37:02 +01:00
<ListItemIcon><ExitToAppIcon/></ListItemIcon>
<ListItemText primary="Log out"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
2019-03-26 02:37:02 +01:00
<Divider/>
</div>
<ListSubheader>Timelines</ListSubheader>
2019-03-27 01:35:30 +01:00
<LinkableListItem button key="home" to="/home">
2019-03-26 02:37:02 +01:00
<ListItemIcon><HomeIcon/></ListItemIcon>
<ListItemText primary="Home"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
<LinkableListItem button key="local" to="/local">
2019-03-26 02:37:02 +01:00
<ListItemIcon><DomainIcon/></ListItemIcon>
<ListItemText primary="Local"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
<LinkableListItem button key="public" to="/public">
2019-03-26 02:37:02 +01:00
<ListItemIcon><PublicIcon/></ListItemIcon>
<ListItemText primary="Public"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
2019-03-26 02:37:02 +01:00
<Divider/>
<ListSubheader>More</ListSubheader>
2019-03-27 01:35:30 +01:00
<LinkableListItem button key="recommended" to="/recommended">
2019-03-26 02:37:02 +01:00
<ListItemIcon><GroupIcon/></ListItemIcon>
<ListItemText primary="Who to follow"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
<LinkableListItem button key="settings" to="/settings">
2019-03-26 02:37:02 +01:00
<ListItemIcon><SettingsIcon/></ListItemIcon>
<ListItemText primary="Settings"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
<LinkableListItem button key="info" to="/about">
2019-03-26 02:37:02 +01:00
<ListItemIcon><InfoIcon/></ListItemIcon>
<ListItemText primary="About"/>
2019-03-27 01:35:30 +01:00
</LinkableListItem>
2019-03-26 02:37:02 +01:00
</List>
</div>
);
}
render() {
2019-03-26 02:37:02 +01:00
const { classes } = this.props;
return (
<div className={classes.root}>
<div className={classes.stickyArea}>
{this.titlebar()}
<AppBar className={classes.appBar} position="static">
<Toolbar>
<IconButton
className={classes.appBarMenuButton}
color="inherit"
aria-label="Open drawer"
onClick={this.toggleDrawerOnMobile}
>
<MenuIcon/>
</IconButton>
<Typography className={classes.appBarTitle} variant="h6" color="inherit" noWrap>
Hyperspace
</Typography>
<div className={classes.appBarFlexGrow}/>
<div className={classes.appBarSearch}>
<div className={classes.appBarSearchIcon}>
<SearchIcon/>
</div>
<InputBase
placeholder="Search..."
classes={{
root: classes.appBarSearchInputRoot,
input: classes.appBarSearchInputInput
}}
2019-04-02 23:28:04 +02:00
onKeyUp={(event) => {
if (event.keyCode === 13) {
this.searchForQuery(event.currentTarget.value);
}
}}
2019-03-26 02:37:02 +01:00
/>
</div>
<div className={classes.appBarFlexGrow}/>
<div className={classes.appBarActionButtons}>
<LinkableIconButton color="inherit" to="/notifications" onClick={() => this.setState({ notificationCount: 0 })}>
<Badge badgeContent={this.state.notificationCount > 0? this.state.notificationCount: ""} color="secondary">
<NotificationsIcon />
</Badge>
2019-03-28 03:01:55 +01:00
</LinkableIconButton>
<LinkableIconButton color="inherit" to="/messages">
2019-03-26 02:37:02 +01:00
<MailIcon/>
2019-03-28 03:01:55 +01:00
</LinkableIconButton>
<IconButton id="acctMenuBtn" onClick={this.toggleAcctMenu}>
2019-03-28 00:24:52 +01:00
<Avatar className={classes.appBarAcctMenuIcon} alt="You" src={this.state.currentUser.avatar_static}/>
2019-03-26 02:37:02 +01:00
</IconButton>
2019-03-28 03:01:55 +01:00
<Menu
id="acct-menu"
anchorEl={document.getElementById("acctMenuBtn")}
open={this.state.acctMenuOpen}
className={classes.acctMenu}
>
<ClickAwayListener onClickAway={this.toggleAcctMenu}>
2019-03-30 22:13:49 +01:00
<div>
<LinkableListItem to={`/profile/${this.state.currentUser.id}`}>
<ListItemAvatar>
<Avatar alt="You" src={this.state.currentUser.avatar_static}/>
</ListItemAvatar>
2019-04-01 03:56:41 +02:00
<ListItemText primary={this.state.currentUser.display_name || this.state.currentUser.acct} secondary={'@' + this.state.currentUser.acct}/>
2019-03-30 22:13:49 +01:00
</LinkableListItem>
<Divider/>
{/* <MenuItem>Switch account</MenuItem> */}
2019-03-30 22:13:49 +01:00
<MenuItem>Log out</MenuItem>
</div>
2019-03-28 03:01:55 +01:00
</ClickAwayListener>
</Menu>
2019-03-26 02:37:02 +01:00
</div>
</Toolbar>
</AppBar>
<nav className={classes.drawer}>
<Hidden mdUp implementation="css">
<Drawer
container={this.props.container}
variant="temporary"
anchor={'left'}
open={this.state.drawerOpenOnMobile}
onClose={this.toggleDrawerOnMobile}
classes={{ paper: classes.drawerPaper }}
>
{this.appDrawer()}
</Drawer>
</Hidden>
<Hidden smDown implementation="css">
<Drawer
classes={{
paper: this.titlebar()? classes.drawerPaperWithTitleAndAppBar: classes.drawerPaperWithAppBar
}}
variant="permanent"
open
>
{this.appDrawer()}
</Drawer>
</Hidden>
</nav>
</div>
2019-04-04 02:01:54 +02:00
<LinkableFab to="/compose" className={classes.composeButton} color="secondary" aria-label="Compose">
<EditIcon/>
</LinkableFab>
2019-03-26 02:37:02 +01:00
</div>
);
}
}
export default withStyles(styles)(AppLayout);