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, { StreamListener } 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 { client: Mastodon; streamListener: StreamListener; 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"); this.streamListener = this.client.stream('/streaming/public'); } componentWillMount() { 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.unshift(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', () => { }) } componentWillUnmount() { this.streamListener.stop(); } 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/public', { 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 (
{ this.state.backlogPosts?
} label={`View ${this.state.backlogPosts.length} new post${this.state.backlogPosts.length > 1? "s": ""}`} color="primary" className={classes.pageTopChip} onClick={() => this.insertBacklog()} clickable />
: null } { this.state.posts?
{this.state.posts.map((post: Status) => { return })}
{ this.state.viewDidLoad && !this.state.viewDidError?
this.loadMoreTimelinePieces()}>
: null }
: } { this.state.viewDidError? Bummer. Something went wrong when loading this timeline. {this.state.viewDidErrorCode? this.state.viewDidErrorCode: ""} : } { this.state.viewIsLoading?
: }
); } } export default withStyles(styles)(withSnackbar(PublicPage));