import { useEffect, useMemo, useState } from "react"; import type { DeckAccount, DeckColumn, DeckListsCache } from "../types/app"; import type { TwitterList } from "../lib/fetching/types"; interface AddColumnModalProps { accounts: DeckAccount[]; isOpen: boolean; onClose: () => void; onAdd: (column: Omit) => void; activeAccountId?: string; fetchLists: (accountId: string) => Promise; listsCache: DeckListsCache; } const columnOptions = [ { id: "foryou", label: "For You", description: "Twitter's AI-ranked firehose", }, { id: "following", label: "Following", description: "Chronological feed of people you follow", }, { id: "bookmarks", label: "Bookmarks", description: "Your saved deep dives" }, { id: "list", label: "List", description: "Curate a topic-specific stream" }, { id: "chat", label: "Chat", description: "Mentions & notifications" }, ] as const; export function AddColumnModal({ accounts, activeAccountId, isOpen, onClose, onAdd, fetchLists, listsCache, }: AddColumnModalProps) { const [kind, setKind] = useState("foryou"); const [accountId, setAccountId] = useState( activeAccountId || accounts[0]?.id || "", ); const [title, setTitle] = useState("For You"); const [listId, setListId] = useState(""); const [listOptions, setListOptions] = useState([]); const [listsLoading, setListsLoading] = useState(false); const [listsError, setListsError] = useState(); useEffect(() => { if (!isOpen) return; setAccountId(activeAccountId || accounts[0]?.id || ""); }, [isOpen, activeAccountId, accounts]); useEffect(() => { setTitle(defaultTitle(kind)); }, [kind]); useEffect(() => { if (!isOpen || kind !== "list" || !accountId) return; let mounted = true; async function loadLists() { try { setListsLoading(true); setListsError(undefined); const cached = listsCache[accountId]; if (cached) { setListOptions(cached); return; } const lists = await fetchLists(accountId); if (!mounted) return; setListOptions(lists); } catch (error) { setListsError( error instanceof Error ? error.message : "Failed to load lists", ); } finally { if (mounted) setListsLoading(false); } } loadLists(); return () => { mounted = false; }; }, [accountId, fetchLists, isOpen, kind, listsCache]); useEffect(() => { if (!isOpen) return; setListId(""); setListOptions([]); }, [accountId, isOpen, kind]); const selectedAccount = useMemo( () => accounts.find((account) => account.id === accountId), [accountId, accounts], ); const canSubmit = Boolean(selectedAccount && (kind !== "list" || listId)); const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); if (!canSubmit || !selectedAccount) return; onAdd({ accountId: selectedAccount.id, account: selectedAccount.username || "", kind, title: title.trim() || defaultTitle(kind), listId: listId || undefined, listName: kind === "list" ? listOptions.find((list) => list.id === listId)?.name : undefined, }); onClose(); }; useEffect(() => { if (!isOpen) return; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") onClose(); }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [isOpen, onClose]); if (!isOpen) return null; return (
{ if (event.target === event.currentTarget) { onClose(); } }} >

New column

Design your stream

{columnOptions.map((option) => ( ))}
{kind === "list" && ( )}
); } function defaultTitle(kind: DeckColumn["kind"]) { switch (kind) { case "following": return "Following"; case "bookmarks": return "Bookmarks"; case "list": return "List"; case "chat": return "Chat"; default: return "For You"; } }