summaryrefslogtreecommitdiff
path: root/gui/src/components/post
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/components/post
parente6e657be3a3b1dae426b46f3bc16f9a5cf4861c2 (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.tsx82
-rw-r--r--gui/src/components/post/Footer.tsx18
-rw-r--r--gui/src/components/post/Header.tsx7
-rw-r--r--gui/src/components/post/Post.tsx13
-rw-r--r--gui/src/components/post/PostWrapper.tsx14
-rw-r--r--gui/src/components/post/wrappers/Nostr.tsx14
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} />;
}