import "./Deck.css"; import { CSSProperties, createContext, useContext, useEffect, useState } from "react"; import { Outlet, useNavigate } from "react-router-dom"; import { FormattedMessage } from "react-intl"; import { NostrLink, TaggedNostrEvent } from "@snort/system"; import useLoginFeed from "@/Feed/LoginFeed"; import { useLoginRelays } from "@/Hooks/useLoginRelays"; import { useTheme } from "@/Hooks/useTheme"; import Articles from "@/Element/Articles"; import TimelineFollows from "@/Element/Feed/TimelineFollows"; import { transformTextCached } from "@/Hooks/useTextTransformCache"; import Icon from "@/Icons/Icon"; import NotificationsPage from "./Notifications"; import useImgProxy from "@/Hooks/useImgProxy"; import Modal from "@/Element/Modal"; import { Thread } from "@/Element/Event/Thread"; import { RootTabs } from "@/Element/RootTabs"; import { SpotlightMedia } from "@/Element/SpotlightMedia"; import { ThreadContext, ThreadContextWrapper } from "@/Hooks/useThreadContext"; import Toaster from "@/Toaster"; import useLogin from "@/Hooks/useLogin"; import { LongFormText } from "@/Element/Event/LongFormText"; import NavSidebar from "@/Pages/Layout/NavSidebar"; import ErrorBoundary from "@/Element/ErrorBoundary"; type Cols = "notes" | "articles" | "media" | "streams" | "notifications"; interface DeckState { thread?: NostrLink; article?: TaggedNostrEvent; } interface DeckScope { setThread: (e?: NostrLink) => void; setArticle: (e?: TaggedNostrEvent) => void; reset: () => void; } export const DeckContext = createContext(undefined); export function SnortDeckLayout() { const login = useLogin(); const navigate = useNavigate(); const [deckState, setDeckState] = useState({ thread: undefined, article: undefined, }); useLoginFeed(); useTheme(); useLoginRelays(); useEffect(() => { if (!login.publicKey) { navigate("/"); } }, [login]); if (!login.publicKey) return null; const cols = ["notes", "media", "notifications", "articles"] as Array; return (
setDeckState({ thread: e }), setArticle: (e?: TaggedNostrEvent) => setDeckState({ article: e }), reset: () => setDeckState({}), }}>
{cols.map(c => { switch (c) { case "notes": return ; case "media": return setDeckState({ thread: t })} />; case "articles": return ; case "notifications": return setDeckState({ thread: t })} />; } })}
{deckState.thread && ( <> setDeckState({})} className="thread-overlay thread"> setDeckState({})} />
setDeckState({})} disableSpotlight={true} />
)} {deckState.article && ( <> setDeckState({})} className="thread-overlay long-form" onClick={() => setDeckState({})}>
e.stopPropagation()}>
)}
); } function SpotlightFromThread({ onClose }: { onClose: () => void }) { const thread = useContext(ThreadContext); const parsed = thread.root ? transformTextCached(thread.root.id, thread.root.content, thread.root.tags) : []; const images = parsed.filter(a => a.type === "media" && a.mimeType?.startsWith("image/")); if (images.length === 0) return; return a.content)} idx={0} onClose={onClose} />; } function NotesCol() { return (
); } function ArticlesCol() { return (
); } function MediaCol({ setThread }: { setThread: (e: NostrLink) => void }) { const { proxy } = useImgProxy(); return (
{ const parsed = transformTextCached(e.id, e.content, e.tags); const images = parsed.filter(a => a.type === "media" && a.mimeType?.startsWith("image/")); return images.length > 0; }} noteRenderer={e => { const parsed = transformTextCached(e.id, e.content, e.tags); const images = parsed.filter(a => a.type === "media" && a.mimeType?.startsWith("image/")); return (
setThread(NostrLink.fromEvent(e))}>
); }} />
); } function NotificationsCol({ setThread }: { setThread: (e: NostrLink) => void }) { return (
); }