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 Masonry from "react-masonry-css"; import { getUserDefaultBool } from "../utilities/settings"; import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward"; interface IPublicPageState { posts?: [Status]; backlogPosts?: [Status] | null; viewIsLoading: boolean; viewDidLoad?: boolean; viewDidError?: boolean; viewDidErrorCode?: any; isMasonryLayout?: boolean; } class PublicPage extends Component { client: Mastodon; streamListener: StreamListener; constructor(props: any) { super(props); this.state = { viewIsLoading: true, backlogPosts: null, isMasonryLayout: getUserDefaultBool("isMasonryLayout") }; 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.isMasonryLayout ? ( {this.state.posts.map((post: Status) => { return (
); })}
) : (
{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));