diff options
| author | polwex <polwex@sortug.com> | 2025-11-19 05:47:30 +0700 |
|---|---|---|
| committer | polwex <polwex@sortug.com> | 2025-11-19 05:47:30 +0700 |
| commit | 74d84cb2f22600b6246343e9ea606cf0db7517f0 (patch) | |
| tree | 0d68285c8e74e6543645e17ab2751d543c1ff9a6 /gui/src/components/post | |
| parent | e6e657be3a3b1dae426b46f3bc16f9a5cf4861c2 (diff) | |
Big GUI improvements on Nostr rendering and fetchingpolwex/iris
Diffstat (limited to 'gui/src/components/post')
| -rw-r--r-- | gui/src/components/post/Body.tsx | 82 | ||||
| -rw-r--r-- | gui/src/components/post/Footer.tsx | 18 | ||||
| -rw-r--r-- | gui/src/components/post/Header.tsx | 7 | ||||
| -rw-r--r-- | gui/src/components/post/Post.tsx | 13 | ||||
| -rw-r--r-- | gui/src/components/post/PostWrapper.tsx | 14 | ||||
| -rw-r--r-- | gui/src/components/post/wrappers/Nostr.tsx | 14 |
6 files changed, 89 insertions, 59 deletions
diff --git a/gui/src/components/post/Body.tsx b/gui/src/components/post/Body.tsx index b4f1bb2..ca5aa6e 100644 --- a/gui/src/components/post/Body.tsx +++ b/gui/src/components/post/Body.tsx @@ -6,7 +6,6 @@ import type { Media as MediaType, ExternalContent, } from "@/types/trill"; -import Icon from "@/components/Icon"; import type { PostProps } from "./Post"; import Media from "./Media"; import JSONContent, { YoutubeSnippet } from "./External"; @@ -15,6 +14,7 @@ import Quote from "./Quote"; import PostData from "./Loader"; import Card from "./Card.tsx"; import type { Ship } from "@/types/urbit.ts"; +import { extractURLs } from "@/logic/nostrill.ts"; function Body(props: PostProps) { const text = props.poast.contents.filter((c) => { @@ -95,41 +95,63 @@ function TextBlock({ block }: { block: Block }) { ) ) : null; } + function Inlin({ i }: { i: Inline }) { const [_, navigate] = useLocation(); function gotoShip(e: React.MouseEvent, ship: Ship) { e.stopPropagation(); navigate(`/feed/${ship}`); } - return "text" in i ? ( - <span>{i.text}</span> - ) : "italic" in i ? ( - <i>{i.italic}</i> - ) : "bold" in i ? ( - <strong>{i.bold}</strong> - ) : "strike" in i ? ( - <span>{i.strike}</span> - ) : "underline" in i ? ( - <span>{i.underline}</span> - ) : "sup" in i ? ( - <sup>{i.sup}</sup> - ) : "sub" in i ? ( - <sub>{i.sub}</sub> - ) : "ship" in i ? ( - <span - className="mention" - role="link" - onMouseUp={(e) => gotoShip(e, i.ship)} - > - {i.ship} - </span> - ) : "codespan" in i ? ( - <code>{i.codespan}</code> - ) : "link" in i ? ( - <LinkParser {...i.link} /> - ) : "break" in i ? ( - <br /> - ) : null; + if ("text" in i) { + const tokens = extractURLs(i.text); + return ( + <> + {tokens.text.map((t, i) => + "text" in t ? ( + <span key={t.text + i}>{t.text}</span> + ) : ( + <a key={t.link.href + i} href={t.link.href}> + {t.link.show} + </a> + ), + )} + {tokens.pics.map((p, i) => ( + <img key={p + i} src={p} /> + ))} + {tokens.vids.map((p, i) => ( + <video key={p + i} src={p} controls /> + ))} + </> + ); + } else { + return "italic" in i ? ( + <i>{i.italic}</i> + ) : "bold" in i ? ( + <strong>{i.bold}</strong> + ) : "strike" in i ? ( + <span>{i.strike}</span> + ) : "underline" in i ? ( + <span>{i.underline}</span> + ) : "sup" in i ? ( + <sup>{i.sup}</sup> + ) : "sub" in i ? ( + <sub>{i.sub}</sub> + ) : "ship" in i ? ( + <span + className="mention" + role="link" + onMouseUp={(e) => gotoShip(e, i.ship)} + > + {i.ship} + </span> + ) : "codespan" in i ? ( + <code>{i.codespan}</code> + ) : "link" in i ? ( + <LinkParser {...i.link} /> + ) : "break" in i ? ( + <br /> + ) : null; + } } function LinkParser({ href, show }: { href: string; show: string }) { diff --git a/gui/src/components/post/Footer.tsx b/gui/src/components/post/Footer.tsx index 41752fc..d4732ce 100644 --- a/gui/src/components/post/Footer.tsx +++ b/gui/src/components/post/Footer.tsx @@ -7,9 +7,10 @@ import { displayCount } from "@/logic/utils"; import { TrillReactModal, stringToReact } from "./Reactions"; import toast from "react-hot-toast"; import NostrIcon from "./wrappers/NostrIcon"; +import type { SPID } from "@/types/ui"; // TODO abstract this somehow -function Footer({ poast, refetch }: PostProps) { +function Footer({ user, poast, refetch }: PostProps) { const [_showMenu, setShowMenu] = useState(false); const [location, navigate] = useLocation(); const [reposting, _setReposting] = useState(false); @@ -22,11 +23,16 @@ function Footer({ poast, refetch }: PostProps) { }), ); const our = api!.airlock.our!; + function getComposerData(): SPID { + return "urbit" in user + ? { trill: poast } + : { nostr: { post: poast, pubkey: user.nostr, eventId: poast.hash } }; + } function doReply(e: React.MouseEvent) { console.log("do reply"); e.stopPropagation(); e.preventDefault(); - setComposerData({ type: "reply", post: { trill: poast } }); + setComposerData({ type: "reply", post: getComposerData() }); // Scroll to top where composer is located window.scrollTo({ top: 0, behavior: "smooth" }); // Focus will be handled by the composer component @@ -36,7 +42,7 @@ function Footer({ poast, refetch }: PostProps) { e.preventDefault(); setComposerData({ type: "quote", - post: { trill: poast }, + post: getComposerData(), }); // Scroll to top where composer is located window.scrollTo({ top: 0, behavior: "smooth" }); @@ -50,7 +56,7 @@ function Footer({ poast, refetch }: PostProps) { async function cancelRP(e: React.MouseEvent) { e.stopPropagation(); e.preventDefault(); - const r = await api!.deletePost(poast.host, poast.id); + const r = await api!.deletePost(user, poast.id); if (r) toast.success("Repost deleted"); // refetch(); if (location.includes(poast.id)) navigate("/"); @@ -59,8 +65,8 @@ function Footer({ poast, refetch }: PostProps) { // TODO update backend because contents are only markdown now e.stopPropagation(); e.preventDefault(); - const pid = { ship: poast.host, id: poast.id }; - const r = await api!.addRP(pid); + const id = "urbit" in user ? poast.id : poast.hash; + const r = await api!.addRP(user, id); if (r) { toast.success("Your repost was published"); } diff --git a/gui/src/components/post/Header.tsx b/gui/src/components/post/Header.tsx index 5898eba..21c4f6c 100644 --- a/gui/src/components/post/Header.tsx +++ b/gui/src/components/post/Header.tsx @@ -13,13 +13,16 @@ function Header(props: PostProps) { function openThread(e: React.MouseEvent) { e.stopPropagation(); const sel = window.getSelection()?.toString(); - if (!sel) navigate(`/t/${poast.host}/${poast.id}`); + const id = "urbit" in props.user ? poast.id : poast.hash; + if (!sel) navigate(`/t/${poast.host}/${id}`); } const { poast } = props; const name = profile ? ( profile.name + ) : "urbit" in props.user ? ( + <p className="p-only">{props.user.urbit}</p> ) : ( - <p className="p-only">{poast.author}</p> + <p className="p-only">{props.user.nostr}</p> ); return ( <header> diff --git a/gui/src/components/post/Post.tsx b/gui/src/components/post/Post.tsx index 7413e70..9df1993 100644 --- a/gui/src/components/post/Post.tsx +++ b/gui/src/components/post/Post.tsx @@ -9,10 +9,11 @@ import RP from "./RP"; import UserModal from "../modals/UserModal"; import type { Ship } from "@/types/urbit"; import Sigil from "../Sigil"; -import type { UserProfile } from "@/types/nostrill"; +import type { UserProfile, UserType } from "@/types/nostrill"; export interface PostProps { poast: Poast; + user: UserType; fake?: boolean; rter?: Ship; rtat?: number; @@ -52,12 +53,18 @@ function TrillPost(props: PostProps) { const [_, navigate] = useLocation(); function openThread(_e: React.MouseEvent) { const sel = window.getSelection()?.toString(); - if (!sel) navigate(`/feed/${poast.host}/${poast.id}`); + const id = "urbit" in props.user ? poast.id : poast.hash; + const path = `/t/${poast.host}/${id}`; + if (poast.hash.includes("000000")) { + console.log("bad hash", poast); + return; + } + if (!sel) navigate(path); } function openModal(e: React.MouseEvent) { e.stopPropagation(); - setModal(<UserModal userString={poast.author} />); + setModal(<UserModal user={props.user} />); } const avatar = profile ? ( <div className="avatar sigil cp" role="link" onMouseUp={openModal}> diff --git a/gui/src/components/post/PostWrapper.tsx b/gui/src/components/post/PostWrapper.tsx deleted file mode 100644 index c4e754f..0000000 --- a/gui/src/components/post/PostWrapper.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import useLocalState from "@/state/state"; -import type { NostrPost, PostWrapper } from "@/types/nostrill"; - -export default Post; -function Post(pw: PostWrapper) { - if ("nostr" in pw) return <NostrPost post={pw.nostr} />; - else return <TrillPost post={pw.urbit.post} nostr={pw.urbit.nostr} />; -} - -function NostrPost({ post, event, relay }: NostrPost) { - const { profiles } = useLocalState(); - const profile = profiles.get(event.pubkey); - return <></>; -} diff --git a/gui/src/components/post/wrappers/Nostr.tsx b/gui/src/components/post/wrappers/Nostr.tsx index 2782fb8..7e96354 100644 --- a/gui/src/components/post/wrappers/Nostr.tsx +++ b/gui/src/components/post/wrappers/Nostr.tsx @@ -1,4 +1,4 @@ -import type { NostrMetadata, NostrPost } from "@/types/nostrill"; +import type { NostrPost } from "@/types/nostrill"; import Post from "../Post"; import useLocalState from "@/state/state"; @@ -7,9 +7,15 @@ function NostrPost({ data }: { data: NostrPost }) { const { profiles } = useLocalState((s) => ({ profiles: s.profiles })); const profile = profiles.get(data.event.pubkey); - return <Post poast={data.post} profile={profile} />; + return ( + <Post + user={{ urbit: data.post.author }} + poast={data.post} + profile={profile} + /> + ); } -export function NostrSnippet({ eventId, pubkey, relay }: NostrMetadata) { - return <div>wtf</div>; +export function NostrSnippet({ eventId, pubkey, relay, post }: NostrPost) { + return <Post user={{ nostr: pubkey }} poast={post} />; } |
