From 74d84cb2f22600b6246343e9ea606cf0db7517f0 Mon Sep 17 00:00:00 2001 From: polwex Date: Wed, 19 Nov 2025 05:47:30 +0700 Subject: Big GUI improvements on Nostr rendering and fetching --- gui/src/components/trill/Thread.tsx | 219 ++++++++++++++++++++++++++++++++++++ gui/src/components/trill/User.tsx | 71 ++++++------ 2 files changed, 255 insertions(+), 35 deletions(-) create mode 100644 gui/src/components/trill/Thread.tsx (limited to 'gui/src/components/trill') diff --git a/gui/src/components/trill/Thread.tsx b/gui/src/components/trill/Thread.tsx new file mode 100644 index 0000000..a56ccf1 --- /dev/null +++ b/gui/src/components/trill/Thread.tsx @@ -0,0 +1,219 @@ +import useLocalState from "@/state/state"; +import Icon from "@/components/Icon"; +import spinner from "@/assets/triangles.svg"; +import Post from "@/components/post/Post"; +import { extractThread, toFlat } from "@/logic/trill/helpers"; +import type { FC, FullNode, Poast } from "@/types/trill"; +import Composer from "@/components/composer/Composer"; +import type { UserProfile } from "@/types/nostrill"; +import type { Ship } from "@/types/urbit"; +import { useEffect, useState } from "react"; + +export default function Thread({ + host, + id, + feed, + profile, +}: { + host: Ship; + id: string; + feed?: FC; + profile?: UserProfile; +}) { + const poast = feed?.feed[id]; + console.log({ poast }); + return ( + <> +
+
+ +
+

Thread

+
+ ~{host} + + #{id} +
+
+
+ {poast && poast.children.length === 0 ? ( + + ) : ( + + )} +
+ + ); +} +function Loader({ + host, + id, + profile, + poast, +}: { + host: Ship; + id: string; + poast?: Poast; + profile?: UserProfile; +}) { + const api = useLocalState((s) => s.api); + const [data, setData] = useState(); + const [error, setError] = useState(""); + console.log({ data }); + async function fetchThread() { + const res = await api!.scryThread(host, id); + if ("error" in res) setError(res.error); + else setData(res.ok); + } + useEffect(() => { + fetchThread(); + }, [host, id]); + + if (data) + return ( + <> + +
+ +
+ + ); + if (poast) + return ( + <> + +
+

Loading Replies...

+
+ Loading +
+
+ + ); + if (error) + return ( +
+

Error Loading Thread

+

{error}

+
+ ); + else + return ( +
+

Loading Thread...

+
+ Loading +
+
+ ); +} + +function Head({ poast, profile }: { poast: Poast; profile?: UserProfile }) { + return ( +
+ +
+ ); +} + +function ChildTree({ node }: { node: FullNode }) { + const profiles = useLocalState((s) => s.profiles); + const kids = Object.values(node.children || {}); + kids.sort((a, b) => b.time - a.time); + return ( + <> + {kids.map((k) => { + const profile = profiles.get(k.author); + return ( +
+ + +
+ ); + })} + + ); + function Grandchildren({ node }: { node: FullNode }) { + return ( +
+ +
+ ); + } +} +// function ChildTree({ node }: { node: FullNode }) { +// const { threadChildren, replies } = extractThread(node); +// return ( +// <> +//
+// {threadChildren.map((n) => { +// return ( +// +// ); +// })} +//
+//
+// {replies.map((n) => ( +// +// ))} +//
+// +// ); +// } + +// function ReplyThread({ node }: { node: FullNode }) { +// // const { threadChildren, replies } = extractThread(node); +// const { replies } = extractThread(node); +// return ( +//
+//
+// +//
+//
+// {replies.map((r) => ( +// +// ))} +//
+//
+// ); +// } + +// function OwnData(props: Props) { +// const { api } = useLocalState((s) => ({ +// api: s.api, +// })); +// const { host, id } = props; +// async function fetchThread() { +// return await api!.scryThread(host, id); +// } +// const { isLoading, isError, data, refetch } = useQuery({ +// queryKey: ["trill-thread", host, id], +// queryFn: fetchThread, +// }); +// return isLoading ? ( +//
+//
+//

Scrying Post, please wait...

+// +//
+//
+// ) : null; +// } +// function SomeoneElses(props: Props) { +// // const { api, following } = useLocalState((s) => ({ +// // api: s.api, +// // following: s.following, +// // })); +// return
ho
; +// } diff --git a/gui/src/components/trill/User.tsx b/gui/src/components/trill/User.tsx index b7b53d6..488b925 100644 --- a/gui/src/components/trill/User.tsx +++ b/gui/src/components/trill/User.tsx @@ -6,19 +6,17 @@ import Icon from "@/components/Icon"; import toast from "react-hot-toast"; import { useEffect, useState } from "react"; import type { FC } from "@/types/trill"; -import type { UserType } from "@/types/nostrill"; +import type { Ship } from "@/types/urbit"; function UserFeed({ - user, - userString, + patp, feed, isFollowLoading, setIsFollowLoading, isAccessLoading, setIsAccessLoading, }: { - user: UserType; - userString: string; + patp: Ship; feed: FC | undefined; isFollowLoading: boolean; setIsFollowLoading: (b: boolean) => void; @@ -40,27 +38,29 @@ function UserFeed({ console.log("fact", lastFact); console.log(isFollowLoading); if (!isFollowLoading) return; - const follow = lastFact?.fols; + if (!lastFact) return; + if (!("fols" in lastFact)) return; + const follow = lastFact.fols; if (!follow) return; if ("new" in follow) { - if (userString !== follow.new.user) return; - toast.success(`Now following ${userString}`); + if (patp !== follow.new.user) return; + toast.success(`Now following ${patp}`); setIsFollowLoading(false); addNotification({ type: "follow", - from: userString, - message: `You are now following ${userString}`, + from: patp, + message: `You are now following ${patp}`, }); } else if ("quit" in follow) { - toast.success(`Unfollowed ${userString}`); + toast.success(`Unfollowed ${patp}`); setIsFollowLoading(false); addNotification({ type: "unfollow", - from: userString, - message: `You unfollowed ${userString}`, + from: patp, + message: `You unfollowed ${patp}`, }); } - }, [lastFact, userString, isFollowLoading]); + }, [lastFact, patp, isFollowLoading]); const handleFollow = async () => { if (!api) return; @@ -68,13 +68,13 @@ function UserFeed({ setIsFollowLoading(true); try { if (!!feed) { - await api.unfollow(user); + await api.unfollow({ urbit: patp }); } else { - await api.follow(user); - toast.success(`Follow request sent to ${userString}`); + await api.follow({ urbit: patp }); + toast.success(`Follow request sent to ${patp}`); } } catch (error) { - toast.error(`Failed to ${!!feed ? "unfollow" : "follow"} ${userString}`); + toast.error(`Failed to ${!!feed ? "unfollow" : "follow"} ${patp}`); setIsFollowLoading(false); console.error("Follow error:", error); } @@ -82,31 +82,29 @@ function UserFeed({ const handleRequestAccess = async () => { if (!api) return; - if (!("urbit" in user)) return; - setIsAccessLoading(true); try { - const res = await api.peekFeed(user.urbit); - toast.success(`Access request sent to ${user.urbit}`); + const res = await api.peekFeed(patp); + toast.success(`Access request sent to ${patp}`); addNotification({ type: "access_request", - from: userString, - message: `Access request sent to ${userString}`, + from: patp, + message: `Access request sent to ${patp}`, }); if ("error" in res) toast.error(res.error); else { console.log("peeked", res.ok.feed); setFC(res.ok.feed); - if (res.ok.profile) addProfile(userString, res.ok.profile); + if (res.ok.profile) addProfile(patp, res.ok.profile); } } catch (error) { - toast.error(`Failed to request access from ${user.urbit}`); + toast.error(`Failed to request access from ${patp}`); console.error("Access request error:", error); } finally { setIsAccessLoading(false); } }; - console.log({ user, userString, feed, fc }); + console.log({ patp, feed, fc }); return ( <> @@ -149,15 +147,9 @@ function UserFeed({ {feed && hasFeed ? ( -
- - -
+ ) : fc ? ( -
- - -
+ ) : null} {!feed && !fc && ( @@ -178,3 +170,12 @@ function UserFeed({ } export default UserFeed; + +export function Inner({ feed, refetch }: { feed: FC; refetch: any }) { + return ( +
+ + +
+ ); +} -- cgit v1.2.3