diff --git a/src/app/(app)/telegram/_components/topics-drawer.tsx b/src/app/(app)/telegram/_components/topics-drawer.tsx index 0ccd0b2..8bc41e8 100644 --- a/src/app/(app)/telegram/_components/topics-drawer.tsx +++ b/src/app/(app)/telegram/_components/topics-drawer.tsx @@ -1,8 +1,8 @@ "use client"; -import { useState, useEffect, useCallback, useTransition } from "react"; +import { useState, useEffect, useCallback, useMemo, useTransition } from "react"; import { toast } from "sonner"; -import { Loader2 } from "lucide-react"; +import { Loader2, Search } from "lucide-react"; import { Dialog, DialogContent, @@ -10,7 +10,7 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { ScrollArea } from "@/components/ui/scroll-area"; +import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { setTopicFetchEnabled } from "../actions"; @@ -29,6 +29,10 @@ interface TopicsDrawerProps { onOpenChange: (open: boolean) => void; } +function topicLabel(t: TopicRow): string { + return t.topicName ?? `Topic ${t.topicId}`; +} + export function TopicsDrawer({ channelId, channelTitle, @@ -37,6 +41,7 @@ export function TopicsDrawer({ }: TopicsDrawerProps) { const [topics, setTopics] = useState([]); const [loading, setLoading] = useState(false); + const [filter, setFilter] = useState(""); const [, startTransition] = useTransition(); const [pendingId, setPendingId] = useState(null); @@ -57,6 +62,13 @@ export function TopicsDrawer({ if (open && channelId) fetchTopics(); }, [open, channelId, fetchTopics]); + const handleOpenChange = (next: boolean) => { + // Reset the filter on close so the next open starts clean (the drawer is + // opened per-channel, so close-then-open is the normal channel switch). + if (!next) setFilter(""); + onOpenChange(next); + }; + const handleToggle = (topic: TopicRow, enabled: boolean) => { // Optimistic update setTopics((prev) => @@ -66,7 +78,7 @@ export function TopicsDrawer({ startTransition(async () => { const result = await setTopicFetchEnabled(topic.id, enabled); if (!result.success) { - toast.error(result.error); + toast.error(result.error ?? "Failed to update topic"); // Revert on failure setTopics((prev) => prev.map((t) => @@ -78,20 +90,43 @@ export function TopicsDrawer({ }); }; + const filtered = useMemo(() => { + const q = filter.trim().toLowerCase(); + if (!q) return topics; + return topics.filter((t) => topicLabel(t).toLowerCase().includes(q)); + }, [topics, filter]); + return ( - + - - - Topics{channelTitle ? `: ${channelTitle}` : ""} - - - Enabled topics are scanned and their files fetched. Disable a topic to - stop fetching new files from it โ€” already-fetched files are kept. - + +
+ + Topics{channelTitle ? `: ${channelTitle}` : ""} + + + Enabled topics are scanned and their files fetched. Disable a topic + to stop fetching new files from it โ€” already-fetched files are kept. + +
+ {topics.length > 0 && ( +
+ + setFilter(e.target.value)} + className="pl-9 h-9" + /> +
+ )}
- + {/* Native overflow scroll: the Radix ScrollArea viewport does not get a + bounded height inside this flex-column, max-h, vertically-centred + dialog, so the list overflowed instead of scrolling. flex-1 + + min-h-0 + overflow-y-auto is the canonical, touch-friendly fix. */} +
{loading ? (
@@ -105,18 +140,24 @@ export function TopicsDrawer({ No topics discovered yet โ€” they'll appear here after the next scan.

+ ) : filtered.length === 0 ? ( +

+ No topics match "{filter}". +

) : ( - topics.map((topic) => ( + filtered.map((topic) => (

- {topic.topicName ?? `Topic ${topic.topicId}`} + {topicLabel(topic)}

- {topic.fetchEnabled ? "Fetching enabled" : "Fetching disabled"} + {topic.fetchEnabled + ? "Fetching enabled" + : "Fetching disabled"} {topic.lastScannedAt ? ` ยท last scanned ${new Date( topic.lastScannedAt @@ -128,15 +169,13 @@ export function TopicsDrawer({ checked={topic.fetchEnabled} disabled={pendingId === topic.id} onCheckedChange={(checked) => handleToggle(topic, checked)} - aria-label={`Toggle fetching for ${ - topic.topicName ?? topic.topicId - }`} + aria-label={`Toggle fetching for ${topicLabel(topic)}`} />

)) )}
- +
);