diff options
Diffstat (limited to 'packages/tweetdeck/src/App.tsx')
| -rw-r--r-- | packages/tweetdeck/src/App.tsx | 311 |
1 files changed, 8 insertions, 303 deletions
diff --git a/packages/tweetdeck/src/App.tsx b/packages/tweetdeck/src/App.tsx index 924ff9a..44b6405 100644 --- a/packages/tweetdeck/src/App.tsx +++ b/packages/tweetdeck/src/App.tsx @@ -1,310 +1,15 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; -import "./styles/normalize.css"; import "./styles/index.css"; -import { Sidebar, type NewAccountInput } from "./components/Sidebar"; -import { ColumnBoard } from "./components/ColumnBoard"; -import { AddColumnModal } from "./components/AddColumnModal"; -import { usePersistentState } from "./hooks/usePersistentState"; -import type { - ColumnSnapshot, - ColumnState, - DeckAccount, - DeckColumn, - DeckListsCache, - FullscreenState, -} from "./types/app"; -import type { Tweet } from "./lib/fetching/types"; -import { generateId } from "./lib/utils/id"; -import { twitterClient } from "./lib/client/twitterClient"; -import { FullscreenColumn } from "./components/FullscreenColumn"; - -const ACCOUNTS_KEY = "tweetdeck.accounts"; -const COLUMNS_KEY = "tweetdeck.columns"; +import "./styles/normalize.css"; +import { Toaster } from "react-hot-toast"; +import Deck from "./pages/Deck"; +import Test from "./Test"; export function App() { - const [accounts, setAccounts] = usePersistentState<DeckAccount[]>( - ACCOUNTS_KEY, - [], - ); - const [columns, setColumns] = usePersistentState<DeckColumn[]>( - COLUMNS_KEY, - [], - ); - const [listsCache, setListsCache] = useState<DeckListsCache>({}); - const [activeAccountId, setActiveAccountId] = useState<string | undefined>( - () => accounts[0]?.id, - ); - const [isModalOpen, setModalOpen] = useState(false); - const [toast, setToast] = useState<string | null>(null); - const [fullscreen, setFullscreen] = useState<FullscreenState | null>(null); - const [columnSnapshots, setColumnSnapshots] = useState< - Record<string, ColumnSnapshot> - >({}); - - useEffect(() => { - if (!activeAccountId) { - const firstAccount = accounts[0]; - if (firstAccount) { - setActiveAccountId(firstAccount.id); - } - } - }, [accounts, activeAccountId]); - - useEffect(() => { - const acs = accounts.filter((a) => !a.avatar || !a.username); - console.log({ acs }); - const nacs = acs.map(async (acc) => { - const our = await twitterClient.own({ cookie: acc.cookie }); - const nacc = { - ...acc, - handle: our.username, - label: our.name, - avatar: our.avatar, - }; - return nacc; - }); - Promise.all(nacs) - .then((acs) => setAccounts(acs)) - .catch((err) => console.error(err)); - }, []); - - const handleAddAccount = useCallback( - (payload: NewAccountInput) => { - const label = `Session ${accounts.length + 1}`; - const account: DeckAccount = { - id: generateId(), - label, - accent: randomAccent(), - cookie: payload.cookie.trim(), - createdAt: Date.now(), - }; - setAccounts((prev) => [...prev, account]); - setActiveAccountId(account.id); - setToast(`${account.label} is ready`); - }, - [accounts.length, setAccounts], - ); - - const handleRemoveAccount = useCallback( - (accountId: string) => { - setAccounts((prev) => prev.filter((account) => account.id !== accountId)); - setColumns((prev) => - prev.filter((column) => column.accountId !== accountId), - ); - setListsCache((prev) => { - const next = { ...prev }; - delete next[accountId]; - return next; - }); - if (activeAccountId === accountId) { - setActiveAccountId(undefined); - } - }, - [activeAccountId, setAccounts, setColumns], - ); - - const handleAddColumn = useCallback( - (column: Omit<DeckColumn, "id">) => { - const nextColumn = { ...column, id: generateId() }; - setColumns((prev) => [...prev, nextColumn]); - setToast(`${nextColumn.title} added to deck`); - }, - [setColumns], - ); - - const handleRemoveColumn = useCallback( - (id: string) => { - setColumns((prev) => prev.filter((column) => column.id !== id)); - }, - [setColumns], - ); - - const fetchLists = useCallback( - async (accountId: string) => { - const account = accounts.find((acc) => acc.id === accountId); - if (!account) throw new Error("Account not found"); - if (listsCache[accountId]) return listsCache[accountId]; - console.log({ listsCache }); - const lists = await twitterClient.lists({ cookie: account.cookie }); - console.log({ lists }); - setListsCache((prev) => ({ ...prev, [accountId]: lists })); - return lists; - }, - [accounts, listsCache], - ); - - const handleColumnStateChange = useCallback( - (columnId: string, state: ColumnState) => { - setColumns((prev) => - prev.map((column) => - column.id === columnId ? { ...column, state } : column, - ), - ); - }, - [setColumns], - ); - - const handleColumnSnapshot = useCallback( - (columnId: string, snapshot: ColumnSnapshot) => { - setColumnSnapshots((prev) => { - const existing = prev[columnId]; - if ( - existing && - existing.tweets === snapshot.tweets && - existing.label === snapshot.label - ) { - return prev; - } - return { - ...prev, - [columnId]: { tweets: snapshot.tweets, label: snapshot.label }, - }; - }); - }, - [], - ); - - const openFullscreen = useCallback( - (payload: FullscreenState) => { - const snapshot = columnSnapshots[payload.column.id]; - setFullscreen({ - ...payload, - tweets: snapshot?.tweets ?? payload.tweets, - columnLabel: snapshot?.label ?? payload.columnLabel, - }); - }, - [columnSnapshots], - ); - - useEffect(() => { - if (!fullscreen) return; - const snapshot = columnSnapshots[fullscreen.column.id]; - if (!snapshot) return; - if ( - snapshot.tweets === fullscreen.tweets && - snapshot.label === fullscreen.columnLabel - ) { - return; - } - setFullscreen((prev) => { - if (!prev) return prev; - if (prev.column.id !== fullscreen.column.id) return prev; - const tweets = snapshot.tweets; - const index = Math.min(prev.index, Math.max(tweets.length - 1, 0)); - return { - ...prev, - tweets, - columnTitle: snapshot.label, - index, - }; - }); - }, [columnSnapshots, fullscreen]); - - const content = useMemo( - () => ( - <ColumnBoard - columns={columns} - accounts={accounts} - onRemove={handleRemoveColumn} - onStateChange={handleColumnStateChange} - onSnapshot={handleColumnSnapshot} - onEnterFullscreen={openFullscreen} - /> - ), - [ - accounts, - columns, - handleRemoveColumn, - handleColumnStateChange, - handleColumnSnapshot, - openFullscreen, - ], - ); - return ( - <div className="app-shell"> - <Sidebar - accounts={accounts} - activeAccountId={activeAccountId} - onActivate={(id) => setActiveAccountId(id)} - onAddAccount={handleAddAccount} - onRemoveAccount={handleRemoveAccount} - onAddColumn={() => setModalOpen(true)} - /> - - <main>{content}</main> - - <AddColumnModal - accounts={accounts} - activeAccountId={activeAccountId} - isOpen={isModalOpen} - onClose={() => setModalOpen(false)} - onAdd={handleAddColumn} - fetchLists={fetchLists} - listsCache={listsCache} - /> - - {toast && ( - <div className="toast" onAnimationEnd={() => setToast(null)}> - {toast} - </div> - )} - - {fullscreen && ( - <FullscreenColumn - state={fullscreen} - onExit={() => setFullscreen(null)} - onNavigate={(step) => { - setFullscreen((prev) => { - if (!prev) return prev; - if (!prev.tweets.length) return prev; - const nextIndex = Math.min( - prev.tweets.length - 1, - Math.max(0, prev.index + step), - ); - if (nextIndex === prev.index) return prev; - return { ...prev, index: nextIndex }; - }); - }} - hasPrevColumn={fullscreen.columnIndex > 0} - hasNextColumn={fullscreen.columnIndex < columns.length - 1} - onSwitchColumn={(direction) => { - setFullscreen((prev) => { - if (!prev) return prev; - const nextIndex = prev.columnIndex + direction; - if (nextIndex < 0) return prev; - if (nextIndex >= columns.length) { - setModalOpen(true); - return prev; - } - const nextColumn = columns[nextIndex]; - if (!nextColumn) return prev; - const snapshot = columnSnapshots[nextColumn.id]; - const account = accounts.find( - (acc) => acc.id === nextColumn.accountId, - ); - const tweets = snapshot?.tweets ?? []; - return { - column: nextColumn, - columnIndex: nextIndex, - columnLabel: snapshot?.label ?? nextColumn.title, - accent: account?.accent ?? prev.accent, - tweets, - index: 0, - }; - }); - }} - onAddColumn={() => setModalOpen(true)} - /> - )} - </div> + <> + <Test /> + <Toaster position="top-center" /> + </> ); } - -function randomAccent(): string { - const palette = ["#7f5af0", "#2cb67d", "#f25f4c", "#f0a500", "#19a7ce"]; - const pick = palette[Math.floor(Math.random() * palette.length)]; - return pick ?? "#7f5af0"; -} - export default App; |
