"use client"; import { useState, useEffect, useCallback, useTransition } from "react"; import { Loader2, Search, CheckSquare, Square, Radio } from "lucide-react"; import { toast } from "sonner"; import { saveChannelSelections } from "../actions"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { ScrollArea } from "@/components/ui/scroll-area"; interface FetchedChannel { chatId: string; title: string; type: string; isForum: boolean; memberCount: number | null; alreadyLinked: boolean; existingChannelId: string | null; } interface ChannelPickerDialogProps { accountId: string | null; open: boolean; onOpenChange: (open: boolean) => void; } type FetchState = | { phase: "idle" } | { phase: "fetching"; requestId?: string } | { phase: "loaded"; channels: FetchedChannel[] } | { phase: "error"; message: string }; export function ChannelPickerDialog({ accountId, open, onOpenChange, }: ChannelPickerDialogProps) { const [isPending, startTransition] = useTransition(); const [fetchState, setFetchState] = useState({ phase: "idle" }); const [selected, setSelected] = useState>(new Set()); const [search, setSearch] = useState(""); // Start fetching when dialog opens useEffect(() => { if (!open || !accountId) { setFetchState({ phase: "idle" }); setSelected(new Set()); setSearch(""); return; } let mounted = true; const startFetch = async () => { setFetchState({ phase: "fetching" }); try { // POST to create a fetch request const postRes = await fetch( `/api/telegram/accounts/${accountId}/fetch-channels`, { method: "POST" } ); if (!postRes.ok) { let message = `Server error (${postRes.status})`; try { const err = await postRes.json(); message = err.error || message; } catch { // response wasn't JSON } if (mounted) setFetchState({ phase: "error", message }); return; } const { requestId } = await postRes.json(); if (mounted) setFetchState({ phase: "fetching", requestId }); // Poll for result const poll = async () => { for (let i = 0; i < 30; i++) { await new Promise((r) => setTimeout(r, 2000)); if (!mounted) return; const getRes = await fetch( `/api/telegram/accounts/${accountId}/fetch-channels?requestId=${requestId}` ); if (!getRes.ok) continue; const data = await getRes.json(); if (data.status === "COMPLETED") { if (mounted) { // Filter out already-linked channels const available = (data.channels as FetchedChannel[]).filter( (ch) => !ch.alreadyLinked ); setFetchState({ phase: "loaded", channels: available }); } return; } else if (data.status === "FAILED") { if (mounted) { setFetchState({ phase: "error", message: data.error || "Fetch failed", }); } return; } } if (mounted) { setFetchState({ phase: "error", message: "Fetch timed out" }); } }; await poll(); } catch (err) { if (mounted) { const message = err instanceof Error ? err.message : "Network error"; setFetchState({ phase: "error", message: `Network error: ${message}` }); } } }; startFetch(); return () => { mounted = false; }; }, [open, accountId]); const channels = fetchState.phase === "loaded" ? fetchState.channels : []; const filteredChannels = channels.filter((ch) => ch.title.toLowerCase().includes(search.toLowerCase()) ); const toggleChannel = (chatId: string) => { setSelected((prev) => { const next = new Set(prev); if (next.has(chatId)) { next.delete(chatId); } else { next.add(chatId); } return next; }); }; const selectAll = () => { setSelected(new Set(filteredChannels.map((ch) => ch.chatId))); }; const deselectAll = () => { setSelected(new Set()); }; const handleSave = () => { if (!accountId || selected.size === 0) return; const selectedChannels = channels .filter((ch) => selected.has(ch.chatId)) .map((ch) => ({ telegramId: ch.chatId, title: ch.title, isForum: ch.isForum, })); startTransition(async () => { const result = await saveChannelSelections(accountId, selectedChannels); if (result.success) { toast.success(`${selectedChannels.length} channel(s) linked as source`); onOpenChange(false); } else { toast.error(result.error); } }); }; return ( Select Source Channels Choose which channels to scan for archives. Already-linked channels are hidden. {fetchState.phase === "fetching" && (

Fetching channels from Telegram...

This may take a few seconds

)} {fetchState.phase === "error" && (

{fetchState.message}

)} {fetchState.phase === "loaded" && ( <> {channels.length === 0 ? (

All channels are already linked to this account.

) : ( <> {/* Search + bulk actions */}
setSearch(e.target.value)} className="pl-9" />

{filteredChannels.length} channel(s) available {selected.size > 0 && ` \u2014 ${selected.size} selected`}

{/* Channel list */}
{filteredChannels.map((ch) => ( ))}
)} )}
); }