summaryrefslogtreecommitdiff
path: root/gui/src/pages
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-11-19 05:47:30 +0700
committerpolwex <polwex@sortug.com>2025-11-19 05:47:30 +0700
commit74d84cb2f22600b6246343e9ea606cf0db7517f0 (patch)
tree0d68285c8e74e6543645e17ab2751d543c1ff9a6 /gui/src/pages
parente6e657be3a3b1dae426b46f3bc16f9a5cf4861c2 (diff)
Big GUI improvements on Nostr rendering and fetchingpolwex/iris
Diffstat (limited to 'gui/src/pages')
-rw-r--r--gui/src/pages/Feed.tsx56
-rw-r--r--gui/src/pages/Thread.tsx187
-rw-r--r--gui/src/pages/User.tsx40
3 files changed, 74 insertions, 209 deletions
diff --git a/gui/src/pages/Feed.tsx b/gui/src/pages/Feed.tsx
index bb001d4..16a5ea1 100644
--- a/gui/src/pages/Feed.tsx
+++ b/gui/src/pages/Feed.tsx
@@ -8,13 +8,13 @@ import { useState } from "react";
import Composer from "@/components/composer/Composer";
import { ErrorPage } from "@/pages/Error";
import NostrFeed from "@/components/nostr/Feed";
+import { consolidateFeeds, disaggregate } from "@/logic/nostrill";
-type FeedType = "global" | "following" | "nostr";
+type FeedType = "urbit" | "following" | "nostr";
function Loader() {
const params = useParams();
- console.log({ params });
if (!params.taip) return <FeedPage t="nostr" />;
- if (params.taip === "global") return <FeedPage t={"global"} />;
+ // if (params.taip === "urbit") return <FeedPage t={"urbit"} />;
if (params.taip === "following") return <FeedPage t={"following"} />;
if (params.taip === "nostr") return <FeedPage t={"nostr"} />;
// else if (param === FeedType.Rumors) return <Rumors />;
@@ -27,10 +27,10 @@ function FeedPage({ t }: { t: FeedType }) {
<>
<div id="top-tabs">
<div
- className={active === "global" ? "active" : ""}
- onClick={() => setActive("global")}
+ className={active === "urbit" ? "active" : ""}
+ onClick={() => setActive("urbit")}
>
- Global
+ Urbit
</div>
<div
className={active === "following" ? "active" : ""}
@@ -47,8 +47,8 @@ function FeedPage({ t }: { t: FeedType }) {
</div>
<div id="feed-proper">
<Composer />
- {active === "global" ? (
- <Global />
+ {active === "urbit" ? (
+ <Urbit />
) : active === "following" ? (
<Following />
) : active === "nostr" ? (
@@ -58,40 +58,22 @@ function FeedPage({ t }: { t: FeedType }) {
</>
);
}
-// {active === "global" ? (
-// <Global />
-// ) : active === "following" ? (
-// <Global />
-// ) : (
-// <Global />
-// )}
-function Global() {
- // const { api } = useLocalState();
- // const { isPending, data, refetch } = useQuery({
- // queryKey: ["globalFeed"],
- // queryFn: () => {
- // return api!.scryFeed(null, null);
- // },
- // });
- // console.log(data, "scry feed data");
- // if (isPending) return <img className="x-center" src={spinner} />;
- // else if ("bucun" in data) return <p>Error</p>;
- // else return <Inner data={data} refetch={refetch} />;
- return <p>Error</p>;
+function Urbit() {
+ const following = useLocalState((s) => s.following);
+ const feed = disaggregate(following, "urbit");
+ return (
+ <div>
+ <PostList data={feed} refetch={() => {}} />
+ </div>
+ );
}
function Following() {
- const following = useLocalState((s) => s.following2);
- console.log({ following });
-
- // console.log(data, "scry feed data");
- // if (isPending) return <img className="x-center" src={spinner} />;
- // else if ("bucun" in data) return <p>Error</p>;
- // else return <Inner data={data} refetch={refetch} />;
-
+ const following = useLocalState((s) => s.following);
+ const feed = consolidateFeeds(following);
return (
<div>
- <PostList data={following} refetch={() => {}} />
+ <PostList data={feed} refetch={() => {}} />
</div>
);
}
diff --git a/gui/src/pages/Thread.tsx b/gui/src/pages/Thread.tsx
index fc215f2..a3d2234 100644
--- a/gui/src/pages/Thread.tsx
+++ b/gui/src/pages/Thread.tsx
@@ -1,167 +1,52 @@
import { useParams } from "wouter";
-import { useQuery } from "@tanstack/react-query";
import useLocalState from "@/state/state";
-import Icon from "@/components/Icon";
-import spinner from "@/assets/triangles.svg";
import { ErrorPage } from "@/pages/Error";
import "@/styles/trill.css";
import "@/styles/feed.css";
-import Post from "@/components/post/Post";
-import { extractThread, toFlat } from "@/logic/trill/helpers";
-import type { FullNode } from "@/types/trill";
-import Composer from "@/components/composer/Composer";
+import { stringToUser } from "@/logic/nostrill";
+import TrillThread from "@/components/trill/Thread";
+import NostrThread from "@/components/nostr/Thread";
+import { decodeNostrKey } from "@/logic/nostr";
+
+export default function ThreadLoader() {
+ const { profiles, following } = useLocalState((s) => ({
+ profiles: s.profiles,
+ following: s.following,
+ }));
-export default function Thread() {
const params = useParams<{ host: string; id: string }>();
const { host, id } = params;
- const { api } = useLocalState((s) => ({ api: s.api }));
-
- async function fetchThread() {
- return await api!.scryThread(host, id);
- }
- const { isPending, data, error } = useQuery({
- queryKey: ["thread", params.host, params.id],
- queryFn: fetchThread,
- enabled: !!api && !!params.host && !!params.id,
- });
-
- console.log({ data });
- if (!params.host || !params.id) {
- return <ErrorPage msg="Invalid thread URL" />;
- }
- if (isPending) {
+ const uuser = stringToUser(host);
+ if ("error" in uuser) return <ErrorPage msg={uuser.error} />;
+ const feed = following.get(host);
+ const profile = profiles.get(host);
+ if ("urbit" in uuser.ok)
return (
- <main>
- <div className="thread-header">
- <h2>Loading Thread...</h2>
- </div>
- <div className="loading-container">
- <img className="x-center" src={spinner} alt="Loading" />
- </div>
- </main>
+ <TrillThread
+ feed={feed}
+ profile={profile}
+ host={uuser.ok.urbit}
+ id={id}
+ />
);
- }
-
- if (error) {
+ if ("nostr" in uuser.ok)
return (
- <main>
- <div className="thread-header">
- <h2>Error Loading Thread</h2>
- </div>
- <ErrorPage msg={error.message || "Failed to load thread"} />
- </main>
+ <NostrThread
+ feed={feed}
+ profile={profile}
+ host={uuser.ok.nostr}
+ id={id}
+ />
);
- }
-
- if (!data || "error" in data) {
- return (
- <main>
- <div className="thread-header">
- <h2>Thread Not Found</h2>
- </div>
- <ErrorPage
- msg={data?.error || "This thread doesn't exist or isn't accessible"}
- />
- </main>
- );
- }
- console.log({ data });
- // TODO make Composer a modal when in Thread mode
- return (
- <main>
- <div className="thread-header">
- <div className="thread-nav">
- <button
- className="back-btn"
- onClick={() => window.history.back()}
- title="Go back"
- >
- <Icon name="reply" size={16} />
- <span>Back to Feed</span>
- </button>
- </div>
- <h2>Thread</h2>
- <div className="thread-info">
- <span className="thread-host">~{params.host}</span>
- <span className="thread-separator">•</span>
- <span className="thread-id">#{params.id}</span>
- </div>
- </div>
-
- <div id="feed-proper">
- <Composer />
- <div id="thread-head">
- <Post poast={toFlat(data.ok)} />
- </div>
- <div id="thread-children">
- <ChildTree node={data.ok} />
- </div>
- </div>
- </main>
- );
+ else return <ErrorPage msg="weird" />;
}
-function ChildTree({ node }: { node: FullNode }) {
- const { threadChildren, replies } = extractThread(node);
- return (
- <>
- <div id="tail">
- {threadChildren.map((n) => {
- return <Post key={n.id} poast={toFlat(n)} />;
- })}
- </div>
- <div id="replies">
- {replies.map((n) => (
- <ReplyThread key={n.id} node={n} />
- ))}
- </div>
- </>
- );
+export function NostrThreadLoader() {
+ const params = useParams<{ id: string }>();
+ const { id } = params;
+ if (!id) return <ErrorPage msg="No thread id passed" />;
+ const dec = decodeNostrKey(id);
+ if (!dec) return <ErrorPage msg="Unknown thread id format" />;
+ return <NostrThread id={dec} host="" />;
}
-
-function ReplyThread({ node }: { node: FullNode }) {
- // const { threadChildren, replies } = extractThread(node);
- const { replies } = extractThread(node);
- return (
- <div className="trill-reply-thread">
- <div className="head">
- <Post poast={toFlat(node)} />
- </div>
- <div className="tail">
- {replies.map((r) => (
- <Post key={r.id} poast={toFlat(r)} />
- ))}
- </div>
- </div>
- );
-}
-
-// 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 ? (
-// <div className={props.className}>
-// <div className="x-center not-found">
-// <p className="x-center">Scrying Post, please wait...</p>
-// <img src={spinner} className="x-center s-100" alt="" />
-// </div>
-// </div>
-// ) : null;
-// }
-// function SomeoneElses(props: Props) {
-// // const { api, following } = useLocalState((s) => ({
-// // api: s.api,
-// // following: s.following,
-// // }));
-// return <div>ho</div>;
-// }
diff --git a/gui/src/pages/User.tsx b/gui/src/pages/User.tsx
index 1611037..80fff05 100644
--- a/gui/src/pages/User.tsx
+++ b/gui/src/pages/User.tsx
@@ -1,18 +1,12 @@
-// import spinner from "@/assets/icons/spinner.svg";
-import Composer from "@/components/composer/Composer";
-import PostList from "@/components/feed/PostList";
import Profile from "@/components/profile/Profile";
import useLocalState, { useStore } from "@/state/state";
-import Icon from "@/components/Icon";
-import toast from "react-hot-toast";
-import { useEffect, useState } from "react";
-import type { FC } from "@/types/trill";
+import { useState } from "react";
import type { UserType } from "@/types/nostrill";
import { isValidPatp } from "urbit-ob";
import { ErrorPage } from "@/pages/Error";
import { useParams } from "wouter";
-import { isValidNostrKey } from "@/logic/nostr";
-import TrillFeed from "@/components/trill/User";
+import { decodeNostrKey } from "@/logic/nostr";
+import TrillFeed, { Inner } from "@/components/trill/User";
import NostrFeed from "@/components/nostr/User";
function UserLoader() {
@@ -22,9 +16,12 @@ function UserLoader() {
if (!userString) return <ErrorPage msg="no such user" />;
else if (isValidPatp(userString))
return <UserFeed user={{ urbit: userString }} userString={userString} />;
- else if (isValidNostrKey(userString))
- return <UserFeed user={{ nostr: userString }} userString={userString} />;
- else return <ErrorPage msg="no such user" />;
+ else {
+ const nostrKey = decodeNostrKey(userString);
+ if (nostrKey)
+ return <UserFeed user={{ nostr: nostrKey }} userString={userString} />;
+ else return <ErrorPage msg="no such user" />;
+ }
}
function UserFeed({
@@ -49,9 +46,8 @@ function UserFeed({
: false;
// auto updating on SSE doesn't work if we do shallow
const { following } = useStore();
- const feed = following.get(userString);
- const hasFeed = !feed ? false : Object.entries(feed).length > 0;
- const refetch = () => feed;
+ const userString2 = "urbit" in user ? user.urbit : user.nostr;
+ const feed = following.get(userString2);
const [isFollowLoading, setIsFollowLoading] = useState(false);
const [isAccessLoading, setIsAccessLoading] = useState(false);
@@ -60,11 +56,10 @@ function UserFeed({
<div id="user-page">
<Profile user={user} userString={userString} isMe={isMe} />
{isMe ? (
- <MyFeed />
+ <MyFeed our={api!.airlock.our!} />
) : "urbit" in user ? (
<TrillFeed
- user={user}
- userString={userString}
+ patp={user.urbit}
feed={feed}
isFollowLoading={isFollowLoading}
setIsFollowLoading={setIsFollowLoading}
@@ -73,7 +68,7 @@ function UserFeed({
/>
) : "nostr" in user ? (
<NostrFeed
- user={user}
+ pubkey={user.nostr}
userString={userString}
feed={feed}
isFollowLoading={isFollowLoading}
@@ -88,6 +83,9 @@ function UserFeed({
export default UserLoader;
-function MyFeed() {
- return <></>;
+function MyFeed({ our }: { our: string }) {
+ const following = useLocalState((s) => s.following);
+ const feed = following.get(our);
+ if (!feed) return <ErrorPage msg="Critical error" />;
+ return <Inner feed={feed} refetch={() => {}} />;
}