151 lines
4.8 KiB
TypeScript
151 lines
4.8 KiB
TypeScript
import "./Deck.css";
|
|
|
|
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
|
import { createContext, useEffect, useState } from "react";
|
|
import { FormattedMessage } from "react-intl";
|
|
import { Link, useLocation, useNavigate } from "react-router-dom";
|
|
|
|
import ErrorBoundary from "@/Components/ErrorBoundary";
|
|
import { LongFormText } from "@/Components/Event/LongFormText";
|
|
import Modal from "@/Components/Modal/Modal";
|
|
import { SpotlightThreadModal } from "@/Components/Spotlight/SpotlightThreadModal";
|
|
import Toaster from "@/Components/Toaster/Toaster";
|
|
import useLoginFeed from "@/Feed/LoginFeed";
|
|
import useLogin from "@/Hooks/useLogin";
|
|
import { useLoginRelays } from "@/Hooks/useLoginRelays";
|
|
import usePreferences from "@/Hooks/usePreferences";
|
|
import { useTheme } from "@/Hooks/useTheme";
|
|
import { ArticlesCol, MediaCol, NotesCol, NotificationsCol } from "@/Pages/Deck/Columns";
|
|
import NavSidebar from "@/Pages/Layout/NavSidebar";
|
|
import { mapPlanName } from "@/Pages/subscribe/utils";
|
|
import { trackEvent } from "@/Utils";
|
|
import { getCurrentSubscription } from "@/Utils/Subscription";
|
|
|
|
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<DeckScope | undefined>(undefined);
|
|
|
|
export function SnortDeckLayout() {
|
|
const location = useLocation();
|
|
const login = useLogin(s => ({
|
|
publicKey: s.publicKey,
|
|
subscriptions: s.subscriptions,
|
|
}));
|
|
const telemetry = usePreferences(s => s.telemetry);
|
|
const navigate = useNavigate();
|
|
const [deckState, setDeckState] = useState<DeckState>({
|
|
thread: undefined,
|
|
article: undefined,
|
|
});
|
|
const sub = getCurrentSubscription(login.subscriptions);
|
|
|
|
useLoginFeed();
|
|
useTheme();
|
|
useLoginRelays();
|
|
|
|
useEffect(() => {
|
|
if (!login.publicKey) {
|
|
navigate("/");
|
|
}
|
|
}, [login]);
|
|
|
|
useEffect(() => {
|
|
if (CONFIG.features.analytics && (telemetry ?? true)) {
|
|
trackEvent("pageview");
|
|
}
|
|
}, [location]);
|
|
|
|
if (!login.publicKey) return null;
|
|
const showDeck = CONFIG.showDeck || !(CONFIG.deckSubKind !== undefined && (sub?.type ?? -1) < CONFIG.deckSubKind);
|
|
if (!showDeck) {
|
|
return (
|
|
<div className="deck-layout">
|
|
<NavSidebar narrow={true} />
|
|
<div>
|
|
<div className="flex flex-col gap-2 m-2 bg-dark p br">
|
|
<div className="text-xl font-bold">
|
|
<FormattedMessage
|
|
defaultMessage="You must be a {tier} subscriber to access {app} deck"
|
|
id="IOu4Xh"
|
|
values={{
|
|
app: CONFIG.appNameCapitalized,
|
|
tier: mapPlanName(CONFIG.deckSubKind ?? -1),
|
|
}}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Link to="/subscribe">
|
|
<button>
|
|
<FormattedMessage defaultMessage="Subscribe" id="gczcC5" />
|
|
</button>
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
const cols = ["notes", "media", "notifications", "articles"] as Array<Cols>;
|
|
return (
|
|
<div className="deck-layout">
|
|
<DeckContext.Provider
|
|
value={{
|
|
...deckState,
|
|
setThread: (e?: NostrLink) => setDeckState({ thread: e }),
|
|
setArticle: (e?: TaggedNostrEvent) => setDeckState({ article: e }),
|
|
reset: () => setDeckState({}),
|
|
}}>
|
|
<NavSidebar narrow={true} />
|
|
<ErrorBoundary>
|
|
<div className="deck-cols">
|
|
{cols.map(c => {
|
|
switch (c) {
|
|
case "notes":
|
|
return <NotesCol />;
|
|
case "media":
|
|
return <MediaCol setThread={t => setDeckState({ thread: t })} />;
|
|
case "articles":
|
|
return <ArticlesCol />;
|
|
case "notifications":
|
|
return <NotificationsCol setThread={t => setDeckState({ thread: t })} />;
|
|
}
|
|
})}
|
|
</div>
|
|
{deckState.thread && (
|
|
<SpotlightThreadModal
|
|
thread={deckState.thread}
|
|
onClose={() => setDeckState({})}
|
|
onBack={() => setDeckState({})}
|
|
/>
|
|
)}
|
|
{deckState.article && (
|
|
<>
|
|
<Modal
|
|
id="deck-article"
|
|
onClose={() => setDeckState({})}
|
|
className="long-form"
|
|
onClick={() => setDeckState({})}>
|
|
<div onClick={e => e.stopPropagation()}>
|
|
<LongFormText ev={deckState.article} isPreview={false} />
|
|
</div>
|
|
</Modal>
|
|
</>
|
|
)}
|
|
<Toaster />
|
|
</ErrorBoundary>
|
|
</DeckContext.Provider>
|
|
</div>
|
|
);
|
|
}
|