diff options
author | polwex <polwex@sortug.com> | 2025-09-17 15:56:00 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-09-17 15:56:00 +0700 |
commit | f0df4c7297a05bd592d8717b8997284c80fd0500 (patch) | |
tree | 2d38e079e971a2e98e78a0f7a3104f2bd3c5daeb /front/src/components/post/Loader.tsx | |
parent | 387af8fc1603805b02ce03f8adba4fa73a954f7c (diff) |
argh
Diffstat (limited to 'front/src/components/post/Loader.tsx')
-rw-r--r-- | front/src/components/post/Loader.tsx | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/front/src/components/post/Loader.tsx b/front/src/components/post/Loader.tsx new file mode 100644 index 0000000..f3c4715 --- /dev/null +++ b/front/src/components/post/Loader.tsx @@ -0,0 +1,160 @@ +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import spinner from "@/assets/triangles.svg"; +import { useEffect, useRef, useState } from "react"; +import useLocalState from "@/state/state"; +import type { PostID } from "@/types/trill"; +import type { Ship } from "@/types/urbit"; + +function PostData(props: { + host: Ship; + id: PostID; + rter?: Ship; + rtat?: number; + rtid?: PostID; + nest?: number; // nested quotes + className?: string; +}) { + const { api } = useLocalState(); + const { host, id, nest } = props; + const [enest, setEnest] = useState(nest); + useEffect(() => { + setEnest(nest); + }, [nest]); + + return function (Component: React.ElementType) { + // const [showNested, setShowNested] = useState(nest <= 3); + const handleShowNested = (e: React.MouseEvent) => { + e.stopPropagation(); + setEnest(enest! - 3); + }; + const [dead, setDead] = useState(false); + const [denied, setDenied] = useState(false); + const { isLoading, isError, data, refetch } = useQuery({ + queryKey: ["trill-thread", host, id], + queryFn: fetchNode, + }); + const queryClient = useQueryClient(); + const dataRef = useRef(data); + useEffect(() => { + dataRef.current = data; + }, [data]); + + async function fetchNode(): Promise<any> { + const res = await api!.scryPost(host, id, null, null); + if ("fpost" in res) return res; + else { + const existing = queryClient.getQueryData(["trill-thread", host, id]); + const existingData = existing || data; + if ("bugen" in res) { + // we peek for the actual node + peekTheNode(); + // if we have a cache we don't invalidate it + if (existingData && "fpost" in existingData) return existingData; + // if we don't have a cache then we show the loading screen + else return res; + } + if ("no-node" in res) { + if (existingData && "fpost" in existingData) return existingData; + else return res; + } + } + } + function peekTheNode() { + let timer; + peekNode({ ship: host, id }); + timer = setTimeout(() => { + const gotPost = dataRef.current && "fpost" in dataRef.current; + setDead(!gotPost); + // clearTimeout(timer); + }, 10_000); + } + + useEffect(() => { + const path = `${host}/${id}`; + if (path in peekedPosts) { + queryClient.setQueryData(["trill-thread", host, id], { + fpost: peekedPosts[path], + }); + } else if (path in deniedPosts) { + setDenied(true); + } + }, [peekedPosts]); + useEffect(() => { + const path = `${host}/${id}`; + if (path in deniedPosts) setDenied(true); + }, [deniedPosts]); + + useEffect(() => { + const l = lastThread; + if (l && l.thread == id) { + queryClient.setQueryData(["trill-thread", host, id], { fpost: l }); + } + }, [lastThread]); + function retryPeek(e: React.MouseEvent) { + e.stopPropagation(); + setDead(false); + peekTheNode(); + } + if (enest > 3) + return ( + <div className={props.className}> + <div className="lazy x-center not-found"> + <button className="x-center" onMouseUp={handleShowNested}> + Load more + </button> + </div> + </div> + ); + else + return data ? ( + dead ? ( + <div className={props.className}> + <div className="no-response x-center not-found"> + <p>{host} did not respond</p> + <button className="x-center" onMouseUp={retryPeek}> + Try again + </button> + </div> + </div> + ) : denied ? ( + <div className={props.className}> + <p className="x-center not-found"> + {host} denied you access to this post + </p> + </div> + ) : "no-node" in data || "bucun" in data ? ( + <div className={props.className}> + <p className="x-center not-found">Post not found</p> + </div> + ) : "bugen" in data ? ( + <div className={props.className}> + <div className="x-center not-found"> + <p className="x-center">Post not found, requesting...</p> + <img src={spinner} className="x-center s-100" alt="" /> + </div> + </div> + ) : "fpost" in data && data.fpost.contents === null ? ( + <div className={props.className}> + <p className="x-center not-found">Post deleted</p> + </div> + ) : ( + <Component + data={data.fpost} + refetch={refetch} + {...props} + nest={enest} + /> + ) + ) : // no data + isLoading || isError ? ( + <div className={props.className}> + <img className="x-center post-spinner" src={spinner} alt="" /> + </div> + ) : ( + <div className={props.className}> + <p>...</p> + </div> + ); + }; +} +export default PostData; |