From 7706acaafa89691dba33c216e6287a8405c4c302 Mon Sep 17 00:00:00 2001 From: polwex Date: Tue, 18 Nov 2025 08:32:45 +0700 Subject: gui fixes to nostr post rendering, added nostr-tools lib for primal compatibility --- gui/src/components/feed/PostList.tsx | 17 +++- gui/src/components/nostr/Feed.tsx | 115 +++++++++++++++++++++++++ gui/src/components/post/Footer.tsx | 1 - gui/src/components/post/Header.tsx | 10 +-- gui/src/components/post/Post.tsx | 4 +- gui/src/components/post/wrappers/NostrIcon.tsx | 15 +++- 6 files changed, 147 insertions(+), 15 deletions(-) create mode 100644 gui/src/components/nostr/Feed.tsx (limited to 'gui/src/components') diff --git a/gui/src/components/feed/PostList.tsx b/gui/src/components/feed/PostList.tsx index 0d01bd2..12b58b4 100644 --- a/gui/src/components/feed/PostList.tsx +++ b/gui/src/components/feed/PostList.tsx @@ -1,10 +1,12 @@ import TrillPost from "@/components/post/Post"; import type { FC } from "@/types/trill"; +import useLocalState from "@/state/state"; // import { useEffect } from "react"; // import { useQueryClient } from "@tanstack/react-query"; // import { toFull } from "../thread/helpers"; function TrillFeed({ data, refetch }: { data: FC; refetch: Function }) { + const { profiles } = useLocalState((s) => ({ profiles: s.profiles })); // const qc = useQueryClient(); // useEffect(() => { // Object.values(data.feed).forEach((poast) => { @@ -25,9 +27,18 @@ function TrillFeed({ data, refetch }: { data: FC; refetch: Function }) { .sort() .reverse() .slice(0, 50) - .map((i) => ( - - ))} + .map((i) => { + const poast = data.feed[i]; + const profile = profiles.get(poast.author); + return ( + + ); + })} ); } diff --git a/gui/src/components/nostr/Feed.tsx b/gui/src/components/nostr/Feed.tsx new file mode 100644 index 0000000..0e74cea --- /dev/null +++ b/gui/src/components/nostr/Feed.tsx @@ -0,0 +1,115 @@ +import PostList from "@/components/feed/PostList"; +import useLocalState from "@/state/state"; +import spinner from "@/assets/triangles.svg"; +import { useState } from "react"; +import { eventsToFc } from "@/logic/nostrill"; +import Icon from "@/components/Icon"; +import toast from "react-hot-toast"; + +export default function Nostr() { + const { nostrFeed, api, relays } = useLocalState((s) => ({ + nostrFeed: s.nostrFeed, + api: s.api, + relays: s.relays, + })); + console.log({ relays }); + const [isSyncing, setIsSyncing] = useState(false); + const feed = eventsToFc(nostrFeed); + console.log({ feed }); + const refetch = () => feed; + + const handleResync = async () => { + if (!api) return; + + setIsSyncing(true); + try { + await api.syncRelays(); + toast.success("Nostr feed sync initiated"); + } catch (error) { + toast.error("Failed to sync Nostr feed"); + console.error("Sync error:", error); + } finally { + setIsSyncing(false); + } + }; + + if (Object.keys(relays).length === 0) + return ( +
+
+ +

No Nostr Relays Set Up

+

+ You haven't set any Nostr Relays to sync data from. You can do so in + the Settings page. +

+

+ If you don't know of any, we recommend the following public relays: +

+
    +
  • wss://nos.lol
  • +
  • wss://relay.damus.io
  • +
+
+
+ ); + // Show empty state with resync option when no feed data + if (!feed || !feed.feed || Object.keys(feed.feed).length === 0) { + return ( +
+
+ +

No Nostr Posts

+

+ Your Nostr feed appears to be empty. Try syncing with your relays to + fetch the latest posts. +

+ +
+
+ ); + } + + // Show feed with resync button in header + return ( +
+
+
+

Nostr Feed

+ + {Object.keys(feed.feed).length} posts + +
+ +
+ +
+ ); +} diff --git a/gui/src/components/post/Footer.tsx b/gui/src/components/post/Footer.tsx index 87f45f3..41752fc 100644 --- a/gui/src/components/post/Footer.tsx +++ b/gui/src/components/post/Footer.tsx @@ -41,7 +41,6 @@ function Footer({ poast, refetch }: PostProps) { // Scroll to top where composer is located window.scrollTo({ top: 0, behavior: "smooth" }); } - console.log({ poast }); const childrenCount = poast.children ? poast.children.length ? poast.children.length diff --git a/gui/src/components/post/Header.tsx b/gui/src/components/post/Header.tsx index 0dfd5e4..b0822b4 100644 --- a/gui/src/components/post/Header.tsx +++ b/gui/src/components/post/Header.tsx @@ -1,11 +1,9 @@ import { date_diff } from "@/logic/utils"; import type { PostProps } from "./Post"; import { useLocation } from "wouter"; -import useLocalState from "@/state/state"; function Header(props: PostProps) { const [_, navigate] = useLocation(); - const profiles = useLocalState((s) => s.profiles); - const profile = profiles.get(props.poast.author); + const { profile } = props; // console.log("profile", profile); // console.log(props.poast.author.length, "length"); function go(e: React.MouseEvent) { @@ -21,13 +19,11 @@ function Header(props: PostProps) { const name = profile ? ( profile.name ) : ( -
-

{poast.author}

-
+

{poast.author}

); return (
-
+
{name}
diff --git a/gui/src/components/post/Post.tsx b/gui/src/components/post/Post.tsx index 2965040..2d9a09a 100644 --- a/gui/src/components/post/Post.tsx +++ b/gui/src/components/post/Post.tsx @@ -22,7 +22,7 @@ export interface PostProps { profile?: UserProfile; } function Post(props: PostProps) { - console.log("post", props); + // console.log("post", props); const { poast } = props; if (!poast || poast.contents === null) { return null; @@ -60,7 +60,7 @@ function TrillPost(props: PostProps) { setModal(); } const avatar = profile ? ( -
+
) : ( diff --git a/gui/src/components/post/wrappers/NostrIcon.tsx b/gui/src/components/post/wrappers/NostrIcon.tsx index 30fbfe9..f39d689 100644 --- a/gui/src/components/post/wrappers/NostrIcon.tsx +++ b/gui/src/components/post/wrappers/NostrIcon.tsx @@ -2,14 +2,23 @@ import Icon from "@/components/Icon"; import useLocalState from "@/state/state"; import toast from "react-hot-toast"; import type { Poast } from "@/types/trill"; +import { generateNevent } from "@/logic/nostr"; export default function ({ poast }: { poast: Poast }) { const { relays, api } = useLocalState((s) => ({ relays: s.relays, api: s.api, })); - async function sendToRelay(e: React.MouseEvent) { + async function handleClick(e: React.MouseEvent) { e.stopPropagation(); + if (poast.event) { + const nevent = generateNevent(poast.event); + console.log({ nevent }); + const href = `https://primal.net/e/${nevent}`; + window.open(href, "_blank"); + } else sendToRelay(e); + } + async function sendToRelay(e: React.MouseEvent) { // const urls = Object.keys(relays); await api!.relayPost(poast.host, poast.id, urls); @@ -18,8 +27,10 @@ export default function ({ poast }: { poast: Poast }) { // TODO round up all helpers return ( -
+
); } + +// npub1w8k2hk9kkv653cr4luqmx9tglldpn59vy7yqvlvex2xxmeygt96s4dlh8p -- cgit v1.2.3