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
+2
View File
@@ -21,6 +21,7 @@ import {
updateMessageAIAnalysis,
updateMessagesAIAnalysisBulk,
} from "./messageStore.js";
import { invalidateAnalyticsCache } from "./analyticsStore.js";
import type {
AnalysisQueueStatus,
MessageRecord,
@@ -374,6 +375,7 @@ async function processIndividualFallback(
const rows = await updateMessagesAIAnalysisBulk(updates);
for (const row of rows) {
getModerationBroadcaster()?.messageAnalyzed(row);
invalidateAnalyticsCache(row.guild_id);
scheduleAutoDelete(row);
}
+2 -3
View File
@@ -424,8 +424,8 @@ export async function getTopicTrends(input: {
try {
const since = Date.now() - hours * 3600_000;
// Only fetch messages that have ai_analysis (the ones that actually have topics)
// This dramatically reduces rows for large guilds
// Fetch all analyzed messages within the time window (no hard row cap).
// Messages without ai_analysis are excluded which naturally limits rows.
const rows = (await executeAll(
`
SELECT
@@ -438,7 +438,6 @@ export async function getTopicTrends(input: {
AND ai_analysis IS NOT NULL
${channelId ? `AND (channel_id = ? OR thread_id = ?)` : ""}
ORDER BY created_at DESC
LIMIT 2000
`,
channelId ? [guildId, since, channelId, channelId] : [guildId, since],
)) as MessageRecord[];
File diff suppressed because one or more lines are too long
+4
View File
@@ -16,6 +16,7 @@ import {
updateMessageAsEdited,
upsertMessageForCapture,
} from "./messageStore.js";
import { invalidateAnalyticsCache } from "./analyticsStore.js";
import type { AttachmentRecord, MessageRecord } from "./types.js";
const logger = createChildLogger("message-capture");
@@ -127,6 +128,9 @@ export async function captureMessage(
broadcaster.messageCreated(messageRecord);
}
// Invalidate analytics cache so real-time stats reflect the new message
invalidateAnalyticsCache(messageRecord.guild_id);
const attachmentUploadTasks: Promise<void>[] = [];
// Insert attachments before queuing analysis to avoid race condition