fix(dashboard): infinite scroll, auto-refetch, analytics cache invalidation, remove topic row cap

Frontend:
- Cursor pagination (100/page) replacing hardcoded 80 message limit
- Removed .slice(0,200) cap on mergeMessages
- IntersectionObserver infinite scroll with skeleton loading
- 15s periodic refetch for message list sync

Backend:
- Removed LIMIT 2000 from topic trends SQL query
- Added invalidateAnalyticsCache on message capture (messageCreated)
- Added invalidateAnalyticsCache on batch analysis completion

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-01 18:14:32 +07:00
co-authored by Claude Opus 4.8
parent 5078b34ac2
commit 4117f83c1b
8 changed files with 121 additions and 15 deletions
+12
View File
@@ -77,6 +77,15 @@ export default function App() {
useEffect(() => { if (monitorGuildId) voice.loadTextTargets(monitorGuildId).catch(() => undefined); }, [monitorGuildId, voice.loadTextTargets]);
useEffect(() => { if (selectedTextChannel) messages.fetchMessages(selectedTextChannel).catch(() => undefined); }, [selectedTextChannel, messages.fetchMessages]);
// Periodic refetch — ensures dashboard stays in sync even if WS events were missed
useEffect(() => {
if (!selectedTextChannel) return;
const interval = setInterval(() => {
messages.fetchMessages(selectedTextChannel).catch(() => undefined);
}, 15_000); // every 15s (longer than WS, shorter than stale cache)
return () => clearInterval(interval);
}, [selectedTextChannel, messages.fetchMessages]);
return (
<DashboardLayout activeTab={activeTab} wsStatus={socket.status} voiceStatus={voice.voiceStatus} onTabChange={(tab) => patchUIState({ activeTab: tab })}>
{activeTab === "live" ? (
@@ -105,6 +114,9 @@ export default function App() {
onGuildChange={(id) => patchUIState({ selectedTextGuild: id, selectedTextChannel: "" })}
onChannelChange={(id) => patchUIState({ selectedTextChannel: id })}
onReanalyze={messages.reanalyze}
onLoadMore={messages.loadMore}
hasMore={messages.hasMore}
loadingMore={messages.loadingMore}
/>
) : (
<AnalyticsErrorBoundary>