feat: comprehensive UX improvements — messages, analysis, moderation

Fix #1 (CRITICAL): Wire message_analyzed through Redis EventBroadcaster
- DG aiAnalyzer.ts: Add broadcastAnalysisCompleted() helper that publishes
  to both in-memory WS broadcaster AND Redis EventBroadcaster
- DG bootstrap.ts: Pass eventBroadcaster to startPendingAIAnalysisWorker()
- Fixes broken real-time chain so analysis results appear instantly

Fix #2: Collapsible AI Analysis with Rich Formatting
- MessageCard: AI analysis now collapsible with color-coded severity border
  (red/yellow/blue), summary line showing categories + confidence + severity
- Default collapsed for clean, expanded for warn/flagged

Fix #3: Toast Notifications for Flagged Content
- Wrap app in ToastProvider; ModerationAlertListener component listens for
  moderation_alert custom events and shows toast with emoji + details

Fix #4: Discord-style Message Grouping
- MessageFeed: Group consecutive messages from same user within 5 min
- MessageCard: Compact variant hides avatar, reduces padding for non-first

Fix #5: Moderation Action Buttons in UI
- BE: POST /api/messages/:id/moderate endpoint + publishCommand() for Redis
- FE: Delete/Warn buttons on flagged/warned cards with confirmation dialog

Fix #6: Search Results Include Full Data
- BE analysis.service.ts: SELECT all 26 message columns instead of just 11
- Search results now render with full MessageCard including images/analysis

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-02 12:17:51 +07:00
co-authored by Claude Opus 4.8
parent 74400376a0
commit c5c667b147
11 changed files with 470 additions and 91 deletions
+21 -2
View File
@@ -4,6 +4,7 @@ import { LivePanel } from "./features/live";
import { useMediaControl } from "./features/live/hooks/useMediaControl";
import { useVoiceControl } from "./features/live/hooks/useVoiceControl";
import { MessagesPanel } from "./features/messages";
import { ModerationAlertListener } from "./features/messages/components/ModerationAlertListener";
import {
mergeMessages,
useMessages,
@@ -93,8 +94,25 @@ export default function App() {
),
);
},
onMessageAnalyzed: (m) =>
messages.setMessages((prev) => mergeMessages(prev, [m as MessageRecord])),
onMessageAnalyzed: (m) => {
const msg = m as MessageRecord;
messages.setMessages((prev) => mergeMessages(prev, [msg]));
// Show toast for moderation alerts (warn/flagged)
const status = msg.ai_status;
if (status === "flagged" || status === "warn") {
const username = msg.username || msg.user_id || "unknown";
const severity = msg.ai_severity || "";
const categories = msg.ai_categories || "";
const brief =
msg.ai_analysis?.slice(0, 80) ??
`Message ${status === "flagged" ? "flagged" : "warned"} by AI`;
window.dispatchEvent(
new CustomEvent("moderation_alert", {
detail: { type: status, username, severity, categories, brief },
}),
);
}
},
onAttachmentUploaded: () =>
messages
.fetchMessages(monitorGuildId || undefined)
@@ -216,6 +234,7 @@ export default function App() {
activeTab={activeTab}
onTabChange={(tab) => patchUIState({ activeTab: tab })}
/>
<ModerationAlertListener />
</DashboardLayout>
);
}