aboutsummaryrefslogtreecommitdiff
path: root/src/web/components/posts/Posts.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/web/components/posts/Posts.jsx')
-rw-r--r--src/web/components/posts/Posts.jsx163
1 files changed, 163 insertions, 0 deletions
diff --git a/src/web/components/posts/Posts.jsx b/src/web/components/posts/Posts.jsx
new file mode 100644
index 0000000..3944f67
--- /dev/null
+++ b/src/web/components/posts/Posts.jsx
@@ -0,0 +1,163 @@
+import React, {useState, useEffect, useRef} from "react";
+import {Route, Routes, useLocation, useNavigate} from "react-router-dom";
+
+
+import Button from "components/button/Button.jsx";
+import Submission from "components/submission/Submission.jsx";
+import Post from "components/post/Post.jsx";
+import {getSubreactFromLocation} from "helpers/Location.jsx";
+
+import styles from "./Posts.css";
+
+async function makePostsRequest(location, setPosts, setError, mounted) {
+ const init = {
+ method: "GET",
+ referrer: "same-origin",
+ }
+ const url = "/api/posts?" + new URLSearchParams({
+ page: 0,
+ amount: 25,
+ subreact: getSubreactFromLocation(location),
+ });
+ return fetch(url, init)
+ .then((response) => {
+ return response.json()
+ .catch(() => {
+ throw new Error("unexpected response from server")
+ })
+ })
+ .then((json) => {
+ if ("error" in json) {
+ throw new Error(json.error);
+ }
+ if (!mounted) {
+ return;
+ }
+ setPosts(json.posts);
+ })
+ .catch((error) => {
+ console.log(error);
+ if (!mounted) {
+ return;
+ }
+ setError(error.message);
+ });
+}
+
+function PostsError({navigate, error}) {
+ return (
+ <div className={styles.container + " " + styles.containerFade}>
+ <h1>Something's wrong!</h1>
+ <h2>Sorry, but an unexpected issue has forced us to stop early. Here are the technical details:</h2>
+ <h3>Error: {error}</h3>
+ <Button onClick={() => {navigate(0);}}>
+ Try again
+ </Button>
+ </div>
+ );
+}
+
+function PostsLoading() {
+ return (
+ <div className={styles.container + " " + styles.containerLoading}>
+ {[...Array(3)].map((_, i) =>
+ <Post placeholder key={i} />
+ )}
+ </div>
+ );
+}
+
+function PostsEmpty({navigate}) {
+ return (
+ <div className={styles.container + " " + styles.containerFade}>
+ <h1>There's nothing here?!?</h1>
+ <h2>Try changing subreacts.</h2>
+ <Button onClick={() => {navigate("/");}}>
+ Take me home
+ </Button>
+ </div>
+ );
+}
+
+function PostsStates({error, posts, navigate}) {
+ if (error !== null) { // Error message.
+ return (
+ <PostsError navigate={navigate} error={error} />
+ );
+ }
+
+ if (posts == null) {
+ return (
+ <PostsLoading />
+ )
+ }
+
+
+ if (posts.length == 0) {
+ return (
+ <PostsEmpty navigate={navigate} />
+ );
+ }
+
+ return (
+ posts.map(({uid, title, contents, time_updated, thumbnail, subreact}, i) =>
+ <Post key={i}
+ uid={uid}
+ title={title}
+ contents={contents}
+ timeUpdated={time_updated}
+ image={thumbnail}
+ subreact={subreact} />)
+ );
+}
+
+export default function Posts() {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const locationRef = useRef(location);
+
+ const [posts, setPosts] = useState(null);
+ const [error, setError] = useState(null);
+ const [submissionActive, setSubmissionActive] = useState(false);
+
+
+ // We make a request if we're at the homepage or if we're changing subreacts.
+ const shouldMakeRequest = () => {
+ if (posts === null) {
+ return true;
+ }
+ const [previous, current] =
+ [locationRef.current, location].map((loc) => getSubreactFromLocation(loc));
+ return previous != current;
+ };
+ // This is a bit ugly, we're caching off our previous location so that we know
+ // if we should update or not.
+ useEffect(() => {
+ let mounted = true;
+
+ if (shouldMakeRequest()) {
+ setPosts(null);
+ makePostsRequest(location, setPosts, setError, mounted);
+ }
+
+ locationRef.current = location;
+ return () => {
+ setError(null);
+ mounted = false;
+ }
+ }, [location]);
+
+ return (
+ <div className={styles.container}>
+ <div className={styles.containerPadding}>
+ <div className={styles.postContainer + (submissionActive ? " " + styles.postContainerActive : "")}
+ onClick={(submissionActive ? () => {setSubmissionActive(false);} : null)}>
+ <div className={(submissionActive ? styles.blockEvents : "")}>
+ <PostsStates error={error} posts={posts} navigate={navigate}/>
+ </div>
+ </div>
+ </div>
+ <Submission active={submissionActive} setActive={setSubmissionActive} />
+ </div>
+ );
+}