summaryrefslogtreecommitdiff
path: root/front/src/components/snippets
diff options
context:
space:
mode:
Diffstat (limited to 'front/src/components/snippets')
-rw-r--r--front/src/components/snippets/Snippets.tsx395
1 files changed, 395 insertions, 0 deletions
diff --git a/front/src/components/snippets/Snippets.tsx b/front/src/components/snippets/Snippets.tsx
new file mode 100644
index 0000000..68f5446
--- /dev/null
+++ b/front/src/components/snippets/Snippets.tsx
@@ -0,0 +1,395 @@
+import { fetchTweet, lurkTweet } from "@/logic/twatter/calls";
+import { pokeDister, scryDister, scryGangs } from "@/logic/requests/tlon";
+import { useEffect, useState } from "react";
+import Tweet from "@/sections/twatter/Tweet";
+import { toFlat } from "@/sections/feed/thread/helpers";
+import PostData from "@/sections/feed/PostData";
+import Post from "@/sections/feed/post/Post";
+import { FullNode, SortugRef } from "@/types/trill";
+import { useQuery, useQueryClient } from "@tanstack/react-query";
+import { subscribe, unsub } from "@/logic/requests/generic";
+import { AppData, GroupMetadata } from "@/types/tlon";
+import comet from "@/assets/icons/comet.svg";
+import Sigil from "@/ui/Sigil";
+import { PollLoader } from "@/sections/feed/poll/Show";
+import { parseThread, parseTweet } from "@/logic/twatter/parser";
+import { Tweet as TweetType } from "@/types/twatter";
+import { scryRadio } from "@/logic/requests/nostril";
+import useLocalState from "@/state/state";
+import { RadioTower, ScheduledRadio, radioLink } from "@/logic/requests/radio";
+import { Ship } from "@/types/urbit";
+import { RADIO } from "@/logic/constants";
+import { SigilOnly } from "../Avatar";
+import { date_diff } from "@/logic/utils";
+import ShipsModal from "../modals/ShipsModal";
+
+export function TrillSnippet({ r }: { r: SortugRef }) {
+ const { ship, path } = r;
+ return PostData({ host: ship, id: path.slice(1) })(TrillSnippetMarkup);
+}
+function TrillSnippetMarkup({
+ data,
+ refetch,
+}: {
+ data: FullNode;
+ refetch: Function;
+}) {
+ return (
+ <div className="trill-snippet">
+ <Post poast={toFlat(data)} refetch={refetch} />
+ </div>
+ );
+}
+// <div
+// onClick={() => {
+// if (pop) pop(link);
+// }}
+// className="chat-snippet trill-snippet"
+// >
+// Post not found
+// </div>
+// );
+
+export function TweetSnippet({
+ link,
+ giveBack,
+}: {
+ link: string;
+ giveBack?: Function;
+}) {
+ const id = link.split("/")[5];
+ const { isLoading, isError, data } = useQuery({
+ queryKey: ["twatter-thread", id],
+ queryFn: () => lurkTweet(id),
+ });
+ const [tw, setTw] = useState<TweetType>();
+ useEffect(() => {
+ if (data && "thread-lurk" in data) {
+ const js = JSON.parse(data["thread-lurk"]).data.tweetResult;
+ if (JSON.stringify(js) === "{}") return;
+ if (giveBack) giveBack(JSON.stringify(parseTweet(js.result)));
+ }
+ }, [data]);
+ if (isLoading || isError)
+ return (
+ <div className="tweet-snippet">
+ <p>Fetching Tweet from your Urbit...</p>
+ </div>
+ );
+ else {
+ if ("no-coki" in data)
+ return (
+ <div id="cookie-error" className="x-center">
+ <p className="">Your Twitter cookie isn't working correctly.</p>
+ <a href="/cookies">Check it out</a>
+ </div>
+ );
+ if ("fail" in data)
+ return (
+ <p>
+ Bad request. Please send some feedback (here) of what you were trying
+ to fetch.
+ </p>
+ );
+ if ("thread-lurk" in data) {
+ const js = JSON.parse(data["thread-lurk"]).data.tweetResult;
+ if (JSON.stringify(js) === "{}")
+ return null; // TODO wtf
+ else
+ return (
+ <div className="tweet-snippet">
+ <Tweet tweet={parseTweet(js.result)} quote={true} />
+ </div>
+ );
+ }
+ // else {
+ // const head = parseThread(JSON.parse(data.thread));
+ // const tweet = head.thread.tweets[0]
+ // giveBack(JSON.stringify(tweet))
+ // return (
+ // <div className="tweet-snippet">
+ // <Tweet tweet={tweet} quote={true} />
+ // </div>
+ // );
+ // }
+ }
+}
+
+export function AppSnippet({ r }: { r: SortugRef }) {
+ async function sub() {
+ if (!subn) {
+ const s = await subscribe(
+ "treaty",
+ "/treaties",
+ (data: { add: AppData }) => {
+ if ("ini" in data) {
+ const app = Object.values(data.ini).find((d) => d.desk === name);
+ setApp(app);
+ }
+ if ("add" in data && data.add.desk === name) setApp(data.add);
+ if (appData) unsub(subn);
+ },
+ );
+ setSub(s);
+ const res = await pokeDister(ship);
+ }
+ }
+ const { ship, path } = r;
+ const name = path.slice(1);
+ const [appData, setApp] = useState<AppData>();
+ const [subn, setSub] = useState<number>();
+ const { isLoading, data, isError } = useQuery({
+ queryKey: ["dister", ship],
+ queryFn: () => scryDister(ship),
+ });
+ if (isLoading || isError) return <div className="reference">...</div>;
+ else {
+ const app = Object.values(data.ini).find((d) => d.desk === name);
+ if (!app && !appData) sub();
+ const a = app
+ ? app
+ : appData
+ ? appData
+ : { title: name, image: comet, info: "", ship };
+ return (
+ <div className="reference app-ref">
+ <AppDiv app={a} />
+ </div>
+ );
+ }
+}
+function AppDiv({ app }: { app: Partial<AppData> }) {
+ return (
+ <>
+ <img src={app.image} alt="" />
+ <div className="text">
+ <p className="app-name">{app.title}</p>
+ <p className="app-info">{app.info}</p>
+ <p className="app-host">App from {app.ship}</p>
+ </div>
+ <p className="ref-ship">
+ <Sigil patp={app.ship} size={40} />
+ </p>
+ </>
+ );
+}
+
+export function TlonSnippet({ r }: { r: SortugRef }) {
+ if (r.type === "app") return <AppSnippet r={r} />;
+ if (r.type === "groups") return <GroupSnippet r={r} />;
+}
+export function GroupSnippet({ r }: { r: SortugRef }) {
+ const queryClient = useQueryClient();
+ async function sub() {
+ if (!subn) {
+ const path = `/gangs/index/${ship}`;
+ const s = await subscribe("groups", path, (data: any) => {
+ const key = `${ship}/${name}`;
+ const val = data[key];
+ queryClient.setQueryData(["gangs"], (old: any) => {
+ return { ...old, [key]: { preview: val } };
+ });
+ });
+ setSub(s);
+ }
+ }
+ const { ship, path } = r;
+ const name = path.slice(1);
+ const [groupData, setGroup] = useState<GroupMetadata>();
+ const [subn, setSub] = useState<number>();
+ const { isLoading, data, isError } = useQuery({
+ queryKey: ["gangs"],
+ queryFn: scryGangs,
+ });
+ if (isLoading || isError) return <div className="reference">...</div>;
+ else {
+ const group = data[`${ship}/${name}`];
+ if (!group && !groupData) sub();
+ const a =
+ group && group.preview
+ ? group.preview.meta
+ : groupData
+ ? groupData
+ : { title: name, image: comet, cover: "", description: "" };
+ return (
+ <div className="reference app-ref">
+ {a.image.startsWith("#") ? (
+ <div
+ className="group-color"
+ style={{ backgroundColor: a.image }}
+ ></div>
+ ) : (
+ <img src={a.image} alt="" />
+ )}
+ <div className="text">
+ <p className="app-name">{a.title}</p>
+ <p className="app-info">
+ {a.description.length > 25
+ ? a.description.substring(0, 25) + "..."
+ : a.description}
+ </p>
+ <p className="group-host">Group by {ship}</p>
+ </div>
+ {/* <p className="ref-ship">
+ <Sigil patp={ship} size={40} />
+ </p> */}
+ </div>
+ );
+ }
+}
+
+export function PollSnippet({ r }: { r: SortugRef }) {
+ return (
+ <div className="poll-snippet">
+ <PollLoader ship={r.ship} id={r.path.slice(1)} />
+ </div>
+ );
+}
+
+export function SnippetHandler(props: { r: SortugRef }) {
+ if (props.r.type === "trill") return <TrillSnippet r={props.r} />;
+ if (props.r.type === "trill-polls") return <PollSnippet r={props.r} />;
+ if (props.r.type === "app") return <AppSnippet r={props.r} />;
+ if (props.r.type === "groups") return <GroupSnippet r={props.r} />;
+}
+
+export function RadioSnippet({ ship }: { ship: Ship }) {
+ const { our } = useLocalState();
+ return ship === our ? <OwnRadio /> : <DudesRadio ship={ship} />;
+}
+
+function DudesRadio({ ship }: { ship }) {
+ function onc() {
+ radioLink(ship);
+ }
+ const { radioTowers } = useLocalState();
+ const tower = radioTowers.find((t) => t.location === ship);
+ if (!tower)
+ return (
+ <div role="link" onMouseUp={onc} className="radio-snippet">
+ <p className="img">{RADIO}</p>
+ <div className="radio-text">
+ <p>Radio data not published. Click and check.</p>;
+ </div>
+ </div>
+ );
+ else
+ return (
+ <div role="link" onMouseUp={onc} className="radio-snippet">
+ <p className="img">{RADIO}</p>
+ <div className="radio-text">
+ <p>Radio Session. Playing: {tower.description}</p>
+ <p>Started {new Date(tower.time).toLocaleString()}</p>
+ </div>
+ <div>
+ <SigilOnly p={ship} size={42} />
+ <span className="viewers">
+ {tower.viewers}
+ <span>👀</span>
+ </span>
+ </div>
+ </div>
+ );
+}
+
+function OwnRadio() {
+ const { currentRadio, our, setModal, radioTowers } = useLocalState();
+ const [scheduled, setS] = useState<ScheduledRadio | null>(null);
+ function onc() {
+ radioLink(our);
+ }
+ useEffect(() => {
+ scryRadio().then((r) => {
+ if (r) setS(r.radio);
+ });
+ }, []);
+ function showViewers() {
+ const modal = (
+ <ShipsModal
+ ships={currentRadio.viewers}
+ header={`People watching your %radio show`}
+ />
+ );
+ setModal(modal);
+ }
+ if (scheduled && scheduled.time > Date.now())
+ return (
+ <div role="link" onMouseUp={onc} className="radio-snippet">
+ <p className="img">{RADIO}</p>
+ <div className="radio-text">
+ <p>
+ Radio Session. Playing:
+ <a className="radio-link" href={scheduled.url}>
+ {scheduled.desc}
+ </a>
+ </p>
+ <p>Starting at {new Date(scheduled.time).toLocaleString()}</p>
+ </div>
+ <div>
+ <SigilOnly p={our} size={42} />
+ </div>
+ </div>
+ );
+ else if (!currentRadio)
+ return (
+ <div role="link" onMouseUp={onc} className="radio-snippet">
+ <p className="img">{RADIO}</p>
+ <div className="radio-text">
+ <p>Radio unavailable</p>
+ </div>
+ </div>
+ );
+ else
+ return (
+ <div role="link" onMouseUp={onc} className="radio-snippet">
+ <p className="img">{RADIO}</p>
+ <div className="radio-text">
+ <p>
+ Radio Session. Playing:
+ <a className="radio-link" href={currentRadio.stream}>
+ {currentRadio.description}
+ </a>
+ </p>
+ {/* <p>Started {date_diff(currentRadio.time, "long")}</p> */}
+ </div>
+ <div>
+ <SigilOnly p={our} size={42} />
+ <span onClick={showViewers} className="viewers">
+ {currentRadio?.viewers?.length || ""}
+ <span>👀</span>
+ </span>
+ </div>
+ </div>
+ );
+
+ // return (
+ // {scheduled > Date.now()
+ // ? (<>
+ // <p>
+ // Radio Session. Playing:
+ // <a className="radio-link" target="_blank" href={currentRadio.stream}>
+ // {currentRadio.description}
+ // </a>
+ // </p>
+
+ // <p>Starting at {new Date(scheduled).toLocaleString()}</p>
+ // </>
+
+ // ): scheduled !== 0()
+
+ // }
+ // <p>
+ // Radio Session. Playing:
+ // <a className="radio-link" target="_blank" href={currentRadio.stream}>
+ // {currentRadio.description}
+ // </a>
+ // </p>
+ // {scheduled && scheduled > Date.now() ? (
+ // <p>Starting at {new Date(scheduled).toLocaleString()}</p>
+ // ) : scheduled !== 0 ? (
+ // <p>Started {date_diff(new Date(scheduled), "long")}. Click to join.</p>
+ // ) : (
+ // <p>Unscheduled session. Click to join.</p>
+ // )}
+ // );
+}