hyperspace-desktop-client-w.../src/pages/Public.tsx

190 lines
6.9 KiB
TypeScript
Raw Normal View History

import React, { Component } from 'react';
import { withStyles, CircularProgress, Typography, Paper, Button, Chip, Avatar, Slide} from '@material-ui/core';
import {styles} from './PageLayout.styles';
import Post from '../components/Post';
import { Status } from '../types/Status';
import Mastodon from 'megalodon';
import {withSnackbar} from 'notistack';
import ArrowUpwardIcon from '@material-ui/icons/ArrowUpward';
interface IPublicPageState {
posts?: [Status];
backlogPosts?: [Status] | null;
viewIsLoading: boolean;
viewDidLoad?: boolean;
viewDidError?: boolean;
viewDidErrorCode?: any;
}
class PublicPage extends Component<any, IPublicPageState> {
client: Mastodon;
streamListener: any;
constructor(props: any) {
super(props);
this.state = {
viewIsLoading: true,
backlogPosts: null
}
this.client = new Mastodon(localStorage.getItem('access_token') as string, localStorage.getItem('baseurl') as string + "/api/v1");
}
componentWillMount() {
this.streamListener = this.client.stream('/streaming/public');
this.streamListener.on('connect', () => {
this.client.get('/timelines/public', {limit: 40}).then((resp: any) => {
let statuses: [Status] = resp.data;
this.setState({
posts: statuses,
viewIsLoading: false,
viewDidLoad: true,
viewDidError: false
})
}).catch((resp: any) => {
this.setState({
viewIsLoading: false,
viewDidLoad: true,
viewDidError: true,
viewDidErrorCode: String(resp)
})
this.props.enqueueSnackbar("Failed to get posts.", {
variant: 'error',
});
})
});
this.streamListener.on('update', (status: Status) => {
let queue = this.state.backlogPosts;
if (queue !== null && queue !== undefined) { queue.push(status); } else { queue = [status] }
this.setState({ backlogPosts: queue });
})
this.streamListener.on('delete', (id: number) => {
let posts = this.state.posts;
if (posts) {
posts.forEach((post: Status) => {
if (posts && parseInt(post.id) === id) {
posts.splice(posts.indexOf(post), 1);
}
})
this.setState({ posts });
}
})
this.streamListener.on('error', (err: Error) => {
this.setState({
viewDidError: true,
viewDidErrorCode: err.message
})
this.props.enqueueSnackbar("An error occured.", {
variant: 'error',
});
})
this.streamListener.on('heartbeat', () => {
})
}
insertBacklog() {
window.scrollTo(0, 0);
let posts = this.state.posts;
let backlog = this.state.backlogPosts;
if (posts && backlog && backlog.length > 0) {
let push = backlog.concat(posts);
this.setState({ posts: push as [Status], backlogPosts: null })
}
}
loadMoreTimelinePieces() {
this.setState({ viewDidLoad: false, viewIsLoading: true})
if (this.state.posts) {
this.client.get('/timelines/home', { max_id: this.state.posts[this.state.posts.length - 1].id, limit: 20 }).then((resp: any) => {
let newPosts: [Status] = resp.data;
let posts = this.state.posts as [Status];
newPosts.forEach((post: Status) => {
posts.push(post);
});
this.setState({
viewIsLoading: false,
viewDidLoad: true,
posts
})
}).catch((err: Error) => {
this.setState({
viewIsLoading: false,
viewDidError: true,
viewDidErrorCode: err.message
})
this.props.enqueueSnackbar("Failed to get posts", {
variant: 'error',
});
})
}
}
render() {
const {classes} = this.props;
return (
<div className={classes.pageLayoutMaxConstraints}>
{
this.state.backlogPosts?
<div className={classes.pageTopChipContainer}>
<div className={classes.pageTopChips}>
<Slide direction="down" in={true}>
<Chip
avatar={
<Avatar>
<ArrowUpwardIcon/>
</Avatar>
}
2019-03-31 22:49:09 +02:00
label={`View ${this.state.backlogPosts.length} new post${this.state.backlogPosts.length > 1? "s": ""}`}
color="primary"
className={classes.pageTopChip}
onClick={() => this.insertBacklog()}
clickable
/>
</Slide>
</div>
</div>: null
}
{ this.state.posts?
<div>
{this.state.posts.map((post: Status) => {
return <Post key={post.id} post={post} client={this.client}/>
})}
<br/>
{
this.state.viewDidLoad && !this.state.viewDidError? <div style={{textAlign: "center"}} onClick={() => this.loadMoreTimelinePieces()}><Button variant="contained">Load more</Button></div>: null
}
</div>:
<span/>
}
{
this.state.viewDidError?
<Paper className={classes.errorCard}>
<Typography variant="h4">Bummer.</Typography>
<Typography variant="h6">Something went wrong when loading this timeline.</Typography>
<Typography>{this.state.viewDidErrorCode? this.state.viewDidErrorCode: ""}</Typography>
</Paper>:
<span/>
}
{
this.state.viewIsLoading?
<div style={{ textAlign: 'center' }}><CircularProgress className={classes.progress} color="primary" /></div>:
<span/>
}
</div>
);
}
}
export default withStyles(styles)(withSnackbar(PublicPage));