diff options
Diffstat (limited to 'front/src/components')
-rw-r--r-- | front/src/components/composer/Composer.tsx (renamed from front/src/components/feed/Composer.tsx) | 37 | ||||
-rw-r--r-- | front/src/components/composer/Snippets.tsx | 62 | ||||
-rw-r--r-- | front/src/components/feed/PostList.tsx | 3 | ||||
-rw-r--r-- | front/src/components/feed/Quote.tsx | 37 | ||||
-rw-r--r-- | front/src/components/layout/Sidebar.tsx | 2 | ||||
-rw-r--r-- | front/src/components/post/Body.tsx (renamed from front/src/components/feed/Body.tsx) | 2 | ||||
-rw-r--r-- | front/src/components/post/Card.tsx (renamed from front/src/components/feed/Card.tsx) | 0 | ||||
-rw-r--r-- | front/src/components/post/External.tsx (renamed from front/src/components/feed/External.tsx) | 0 | ||||
-rw-r--r-- | front/src/components/post/Footer.tsx (renamed from front/src/components/feed/Footer.tsx) | 9 | ||||
-rw-r--r-- | front/src/components/post/Header.tsx (renamed from front/src/components/feed/Header.tsx) | 9 | ||||
-rw-r--r-- | front/src/components/post/Loader.tsx (renamed from front/src/components/feed/PostData.tsx) | 0 | ||||
-rw-r--r-- | front/src/components/post/Media.tsx (renamed from front/src/components/feed/Media.tsx) | 0 | ||||
-rw-r--r-- | front/src/components/post/Post.tsx (renamed from front/src/components/feed/Post.tsx) | 15 | ||||
-rw-r--r-- | front/src/components/post/PostWrapper.tsx | 14 | ||||
-rw-r--r-- | front/src/components/post/Quote.tsx | 64 | ||||
-rw-r--r-- | front/src/components/post/RP.tsx (renamed from front/src/components/feed/RP.tsx) | 2 | ||||
-rw-r--r-- | front/src/components/post/Reactions.tsx (renamed from front/src/components/feed/Reactions.tsx) | 0 | ||||
-rw-r--r-- | front/src/components/post/StatsModal.tsx (renamed from front/src/components/feed/StatsModal.tsx) | 0 | ||||
-rw-r--r-- | front/src/components/post/wrappers/Nostr.tsx | 15 | ||||
-rw-r--r-- | front/src/components/post/wrappers/NostrIcon.tsx (renamed from front/src/components/feed/NostrIcon.tsx) | 0 | ||||
-rw-r--r-- | front/src/components/snippets/Snippets.tsx | 395 |
21 files changed, 209 insertions, 457 deletions
diff --git a/front/src/components/feed/Composer.tsx b/front/src/components/composer/Composer.tsx index 27da392..795188e 100644 --- a/front/src/components/feed/Composer.tsx +++ b/front/src/components/composer/Composer.tsx @@ -1,9 +1,11 @@ -import { openLock } from "@/logic/bunts"; -import { HASHTAGS_REGEX } from "@/logic/constants"; import useLocalState from "@/state/state"; -import type { Poast, SentPoast } from "@/types/trill"; +import type { Poast } from "@/types/trill"; import Sigil from "@/components/Sigil"; -import { useState } from "react"; +import { useState, type FormEvent } from "react"; +import type { ComposerData } from "@/types/ui"; +import Snippets, { ReplySnippet } from "./Snippets"; +import toast from "react-hot-toast"; +import { useLocation } from "wouter"; function Composer({ isAnon, @@ -12,10 +14,12 @@ function Composer({ isAnon?: boolean; replying?: Poast; }) { - const { api, keys } = useLocalState(); + const [loc, navigate] = useLocation(); + const { api, composerData } = useLocalState(); const our = api!.airlock.our!; const [input, setInput] = useState(replying ? `${replying}: ` : ""); - async function poast() { + async function poast(e: FormEvent<HTMLFormElement>) { + e.preventDefault(); // TODO // const parent = replying ? replying : null; // const tokens = tokenize(input); @@ -30,22 +34,33 @@ function Composer({ // tags: input.match(HASHTAGS_REGEX) || [], // }; // TODO make it user choosable - const pubkey = keys[0]!; - await api!.addPost(pubkey, input); + const res = await api!.addPost(input); + if (res) { + setInput(""); + toast.success("post sent"); + navigate(`/feed/${our}`); + } } const placeHolder = isAnon ? "> be me" : "What's going on in Urbit"; return ( - <div id="composer"> + <form id="composer" onSubmit={poast}> <div className="sigil"> <Sigil patp={our} size={48} /> </div> + + {composerData && composerData.type === "reply" && ( + <ReplySnippet post={composerData?.post} /> + )} <input value={input} onInput={(e) => setInput(e.currentTarget.value)} placeholder={placeHolder} /> - <button onClick={poast}>Post</button> - </div> + {composerData && composerData.type === "quote" && ( + <Snippets post={composerData?.post} /> + )} + <button type="submit">Post</button> + </form> ); } diff --git a/front/src/components/composer/Snippets.tsx b/front/src/components/composer/Snippets.tsx new file mode 100644 index 0000000..30498d0 --- /dev/null +++ b/front/src/components/composer/Snippets.tsx @@ -0,0 +1,62 @@ +import Quote from "@/components/post/Quote"; +import type { ComposerData, SPID } from "@/types/ui"; +import { NostrSnippet } from "../post/wrappers/Nostr"; + +export default Snippets; +function Snippets({ post }: { post: SPID }) { + return ( + <ComposerSnippet> + <PostSnippet post={post} /> + </ComposerSnippet> + ); +} + +export function ComposerSnippet({ + onClick, + children, +}: { + onClick?: any; + children: any; +}) { + function onc(e: React.MouseEvent) { + e.stopPropagation(); + onClick(); + } + return ( + <div className="composer-snippet"> + <div className="pop-snippet-icon cp" role="link" onClick={onc}></div> + {children} + </div> + ); +} +function PostSnippet({ post }: { post: SPID }) { + if ("trill" in post) return <Quote data={post.trill} nest={0} />; + else if ("nostr" in post) return <NostrSnippet {...post.nostr} />; + // else if ("twatter" in post) + // return ( + // <div id={`composer-${type}`}> + // <Tweet tweet={post.post} quote={true} /> + // </div> + // ); + // else if ("rumors" in post) + // return ( + // <div id={`composer-${type}`}> + // <div className="rumor-quote f1"> + // <img src={rumorIcon} alt="" /> + // <Body poast={post.post} refetch={() => {}} /> + // <span>{date_diff(post.post.time, "short")}</span> + // </div> + // </div> + // ); + else return <></>; +} + +export function ReplySnippet({ post }: { post: SPID }) { + if ("trill" in post) + return ( + <div id="reply"> + <Quote data={post.trill} nest={0} /> + </div> + ); + else return <div />; +} diff --git a/front/src/components/feed/PostList.tsx b/front/src/components/feed/PostList.tsx index 3d41ff8..b09a0e9 100644 --- a/front/src/components/feed/PostList.tsx +++ b/front/src/components/feed/PostList.tsx @@ -1,4 +1,4 @@ -import TrillPost from "./Post"; +import TrillPost from "@/components/post/Post"; import type { FC } from "@/types/trill"; // import { useEffect } from "react"; // import { useQueryClient } from "@tanstack/react-query"; @@ -22,6 +22,7 @@ function TrillFeed({ data, refetch }: { data: FC; refetch: Function }) { {Object.keys(data.feed) .sort() .reverse() + .slice(0, 50) .map((i) => ( <TrillPost key={i} poast={data.feed[i]} refetch={refetch} /> ))} diff --git a/front/src/components/feed/Quote.tsx b/front/src/components/feed/Quote.tsx deleted file mode 100644 index d71be40..0000000 --- a/front/src/components/feed/Quote.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import type { FullNode } from "@/types/trill"; -import { date_diff } from "@/logic/utils"; -import { useLocation } from "wouter"; -import Body from "./Body"; -import Sigil from "../Sigil"; -import { toFlat } from "./RP"; - -function Quote({ - data, - refetch, - nest, -}: { - data: FullNode; - refetch?: Function; - nest: number; -}) { - const [_, navigate] = useLocation(); - function gotoQuote(e: React.MouseEvent) { - e.stopPropagation(); - navigate(`/feed/${data.host}/${data.id}`); - } - return ( - <div onMouseUp={gotoQuote} className="quote-in-post"> - <header className="btw"> - ( - <div className="quote-author flex"> - <Sigil patp={data.author} size={20} /> - {data.author} - </div> - )<span>{date_diff(data.time, "short")}</span> - </header> - <Body poast={toFlat(data)} nest={nest} refetch={refetch!} /> - </div> - ); -} - -export default Quote; diff --git a/front/src/components/layout/Sidebar.tsx b/front/src/components/layout/Sidebar.tsx index 1568421..4055454 100644 --- a/front/src/components/layout/Sidebar.tsx +++ b/front/src/components/layout/Sidebar.tsx @@ -22,7 +22,7 @@ function SlidingMenu() { <div id="left-menu"> <div id="logo"> <img src={logo} /> - <h3> Nostril </h3> + <h3> Nostrill </h3> </div> <h3>Feeds</h3> <div className="opt" role="link" onClick={() => goto(`/feed/global`)}> diff --git a/front/src/components/feed/Body.tsx b/front/src/components/post/Body.tsx index 2f11962..2e4e2f8 100644 --- a/front/src/components/feed/Body.tsx +++ b/front/src/components/post/Body.tsx @@ -12,7 +12,7 @@ import Media from "./Media"; import JSONContent, { YoutubeSnippet } from "./External"; import { useLocation } from "wouter"; import Quote from "./Quote"; -import PostData from "./PostData"; +import PostData from "./Loader"; import Card from "./Card.tsx"; import type { Ship } from "@/types/urbit.ts"; diff --git a/front/src/components/feed/Card.tsx b/front/src/components/post/Card.tsx index 37f4911..37f4911 100644 --- a/front/src/components/feed/Card.tsx +++ b/front/src/components/post/Card.tsx diff --git a/front/src/components/feed/External.tsx b/front/src/components/post/External.tsx index 0ea1500..0ea1500 100644 --- a/front/src/components/feed/External.tsx +++ b/front/src/components/post/External.tsx diff --git a/front/src/components/feed/Footer.tsx b/front/src/components/post/Footer.tsx index 938a8c7..3b48241 100644 --- a/front/src/components/feed/Footer.tsx +++ b/front/src/components/post/Footer.tsx @@ -8,7 +8,9 @@ import { useLocation } from "wouter"; import { displayCount } from "@/logic/utils"; import { TrillReactModal, stringToReact } from "./Reactions"; import toast from "react-hot-toast"; -import NostrIcon from "./NostrIcon"; +import NostrIcon from "./wrappers/NostrIcon"; +// TODO abstract this somehow + function Footer({ poast, refetch }: PostProps) { const [_showMenu, setShowMenu] = useState(false); const [location, navigate] = useLocation(); @@ -17,14 +19,13 @@ function Footer({ poast, refetch }: PostProps) { const our = api!.airlock.our!; function doReply(e: React.MouseEvent) { e.stopPropagation(); - setComposerData({ type: "reply", post: { service: "trill", post: poast } }); - navigate("/composer"); + setComposerData({ type: "reply", post: { trill: poast } }); } function doQuote(e: React.MouseEvent) { e.stopPropagation(); setComposerData({ type: "quote", - post: { service: "trill", post: poast }, + post: { trill: poast }, }); navigate("/composer"); } diff --git a/front/src/components/feed/Header.tsx b/front/src/components/post/Header.tsx index 7658bfb..e541fa5 100644 --- a/front/src/components/feed/Header.tsx +++ b/front/src/components/post/Header.tsx @@ -1,8 +1,13 @@ 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(); + const profile = profiles.get(props.poast.author); + // console.log("profile", profile); + // console.log(props.poast.author.length, "length"); function go(e: React.MouseEvent) { e.stopPropagation(); } @@ -12,7 +17,9 @@ function Header(props: PostProps) { if (!sel) navigate(`/feed/${poast.host}/${poast.id}`); } const { poast } = props; - const name = ( + const name = profile ? ( + profile.name + ) : ( <div className="name cp"> <p className="p-only">{poast.author}</p> </div> diff --git a/front/src/components/feed/PostData.tsx b/front/src/components/post/Loader.tsx index f3c4715..f3c4715 100644 --- a/front/src/components/feed/PostData.tsx +++ b/front/src/components/post/Loader.tsx diff --git a/front/src/components/feed/Media.tsx b/front/src/components/post/Media.tsx index 04ea156..04ea156 100644 --- a/front/src/components/feed/Media.tsx +++ b/front/src/components/post/Media.tsx diff --git a/front/src/components/feed/Post.tsx b/front/src/components/post/Post.tsx index 1211a97..e61efb0 100644 --- a/front/src/components/feed/Post.tsx +++ b/front/src/components/post/Post.tsx @@ -9,6 +9,7 @@ import RP from "./RP"; import ShipModal from "../modals/ShipModal"; import type { Ship } from "@/types/urbit"; import Sigil from "../Sigil"; +import type { UserProfile } from "@/types/nostrill"; export interface PostProps { poast: Poast; @@ -17,11 +18,11 @@ export interface PostProps { rtat?: number; rtid?: PostID; nest?: number; - refetch: Function; + refetch?: Function; + profile?: UserProfile; } function Post(props: PostProps) { const { poast } = props; - console.log({ poast }); if (!poast || poast.contents === null) { return null; } @@ -45,7 +46,7 @@ function Post(props: PostProps) { export default Post; function TrillPost(props: PostProps) { - const { poast, fake } = props; + const { poast, profile, fake } = props; const { setModal } = useLocalState(); const [_, navigate] = useLocation(); function openThread(_e: React.MouseEvent) { @@ -57,8 +58,12 @@ function TrillPost(props: PostProps) { e.stopPropagation(); setModal(<ShipModal ship={poast.author} />); } - const avatar = ( - <div className="avatar-w sigil cp" role="link" onMouseUp={openModal}> + const avatar = profile ? ( + <div className="avatar cp" role="link" onMouseUp={openModal}> + <img src={profile.picture} /> + </div> + ) : ( + <div className="avatar sigil cp" role="link" onMouseUp={openModal}> <Sigil patp={poast.author} size={42} /> </div> ); diff --git a/front/src/components/post/PostWrapper.tsx b/front/src/components/post/PostWrapper.tsx new file mode 100644 index 0000000..c4e754f --- /dev/null +++ b/front/src/components/post/PostWrapper.tsx @@ -0,0 +1,14 @@ +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/front/src/components/post/Quote.tsx b/front/src/components/post/Quote.tsx new file mode 100644 index 0000000..28149f0 --- /dev/null +++ b/front/src/components/post/Quote.tsx @@ -0,0 +1,64 @@ +import type { FullNode, Poast } from "@/types/trill"; +import { date_diff } from "@/logic/utils"; +import { useLocation } from "wouter"; +import Body from "./Body"; +import Sigil from "../Sigil"; + +// function Quote({ +// data, +// refetch, +// nest, +// }: { +// data: FullNode; +// refetch?: Function; +// nest: number; +// }) { +// const [_, navigate] = useLocation(); +// function gotoQuote(e: React.MouseEvent) { +// e.stopPropagation(); +// navigate(`/feed/${data.host}/${data.id}`); +// } +// return ( +// <div onMouseUp={gotoQuote} className="quote-in-post"> +// <header className="btw"> +// ( +// <div className="quote-author flex"> +// <Sigil patp={data.author} size={20} /> +// {data.author} +// </div> +// )<span>{date_diff(data.time, "short")}</span> +// </header> +// <Body poast={toFlat(data)} nest={nest} refetch={refetch!} /> +// </div> +// ); +// } +function Quote({ + data, + refetch, + nest, +}: { + data: Poast; + refetch?: Function; + nest: number; +}) { + const [_, navigate] = useLocation(); + function gotoQuote(e: React.MouseEvent) { + e.stopPropagation(); + navigate(`/feed/${data.host}/${data.id}`); + } + return ( + <div onMouseUp={gotoQuote} className="quote-in-post"> + <header className="btw"> + ( + <div className="quote-author flex"> + <Sigil patp={data.author} size={20} /> + {data.author} + </div> + )<span>{date_diff(data.time, "short")}</span> + </header> + <Body poast={data} nest={nest} refetch={refetch!} /> + </div> + ); +} + +export default Quote; diff --git a/front/src/components/feed/RP.tsx b/front/src/components/post/RP.tsx index dc733cc..27fa02d 100644 --- a/front/src/components/feed/RP.tsx +++ b/front/src/components/post/RP.tsx @@ -1,7 +1,7 @@ import Post from "./Post"; import type { Ship } from "@/types/urbit"; import type { Poast, FullNode, ID } from "@/types/trill"; -import PostData from "./PostData"; +import PostData from "./Loader"; export default function (props: { host: string; id: string; diff --git a/front/src/components/feed/Reactions.tsx b/front/src/components/post/Reactions.tsx index 58662cd..58662cd 100644 --- a/front/src/components/feed/Reactions.tsx +++ b/front/src/components/post/Reactions.tsx diff --git a/front/src/components/feed/StatsModal.tsx b/front/src/components/post/StatsModal.tsx index 4720b2a..4720b2a 100644 --- a/front/src/components/feed/StatsModal.tsx +++ b/front/src/components/post/StatsModal.tsx diff --git a/front/src/components/post/wrappers/Nostr.tsx b/front/src/components/post/wrappers/Nostr.tsx new file mode 100644 index 0000000..bdc5ba9 --- /dev/null +++ b/front/src/components/post/wrappers/Nostr.tsx @@ -0,0 +1,15 @@ +import type { NostrMetadata, NostrPost } from "@/types/nostrill"; +import Post from "../Post"; +import useLocalState from "@/state/state"; + +export default NostrPost; +function NostrPost({ data }: { data: NostrPost }) { + const { profiles } = useLocalState(); + const profile = profiles.get(data.event.pubkey); + + return <Post poast={data.post} profile={profile} />; +} + +export function NostrSnippet({ eventId, pubkey, relay }: NostrMetadata) { + return <div>wtf</div>; +} diff --git a/front/src/components/feed/NostrIcon.tsx b/front/src/components/post/wrappers/NostrIcon.tsx index 0c368fb..0c368fb 100644 --- a/front/src/components/feed/NostrIcon.tsx +++ b/front/src/components/post/wrappers/NostrIcon.tsx diff --git a/front/src/components/snippets/Snippets.tsx b/front/src/components/snippets/Snippets.tsx deleted file mode 100644 index 68f5446..0000000 --- a/front/src/components/snippets/Snippets.tsx +++ /dev/null @@ -1,395 +0,0 @@ -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> - // )} - // ); -} |