diff options
Diffstat (limited to 'front/src/components/snippets')
-rw-r--r-- | front/src/components/snippets/Snippets.tsx | 395 |
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> + // )} + // ); +} |