diff --git a/services/frontend/src/App.tsx b/services/frontend/src/App.tsx
index fac480c..e90d1ea 100644
--- a/services/frontend/src/App.tsx
+++ b/services/frontend/src/App.tsx
@@ -11,9 +11,7 @@ import {
} from "./features/messages/hooks/useMessages";
import {
type ActiveSpeaker,
- type Channel,
getAppConfig,
- getTextChannels,
type MediaState,
type MessageRecord,
} from "./shared/api/client";
@@ -62,8 +60,6 @@ export default function App() {
!!localStorage.getItem("admin-password"),
);
const [monitorGuildId, setMonitorGuildId] = useState("");
- const [textChannels, setTextChannels] = useState([]);
- const [selectedMessageChannel, setSelectedMessageChannel] = useState("");
const audio = useAudioPlayback();
const activeTab = uiState.activeTab || "live";
@@ -117,7 +113,7 @@ export default function App() {
},
onAttachmentUploaded: () =>
messages
- .fetchMessages(monitorGuildId || undefined, selectedMessageChannel || undefined)
+ .fetchMessages(monitorGuildId || undefined)
.catch(() => undefined),
onMediaState: (state) => media.setMediaState(state as MediaState),
onVoiceRecordingUploaded: (d) =>
@@ -145,38 +141,22 @@ export default function App() {
voice.loadVoiceChannels(selectedVoiceGuild).catch(() => undefined);
}, [selectedVoiceGuild, voice.loadVoiceChannels]);
- // Load text channels when monitor guild changes (Messages tab)
- useEffect(() => {
- if (monitorGuildId)
- getTextChannels(monitorGuildId)
- .then(setTextChannels)
- .catch(() => undefined);
- }, [monitorGuildId]);
-
// Auto-fetch messages for the monitor guild
useEffect(() => {
if (monitorGuildId)
messages
- .fetchMessages(monitorGuildId, selectedMessageChannel || undefined)
+ .fetchMessages(monitorGuildId)
.catch(() => undefined);
}, [monitorGuildId, messages.fetchMessages]);
- // Re-fetch when channel filter changes
- useEffect(() => {
- if (monitorGuildId)
- messages
- .fetchMessages(monitorGuildId, selectedMessageChannel || undefined)
- .catch(() => undefined);
- }, [selectedMessageChannel, monitorGuildId, messages.fetchMessages]);
-
// Periodic refetch — keeps dashboard in sync even if WS events missed
useEffect(() => {
if (!monitorGuildId) return;
const interval = setInterval(() => {
- messages.fetchMessages(monitorGuildId, selectedMessageChannel || undefined).catch(() => undefined);
+ messages.fetchMessages(monitorGuildId).catch(() => undefined);
}, 15_000);
return () => clearInterval(interval);
- }, [monitorGuildId, messages.fetchMessages, selectedMessageChannel]);
+ }, [monitorGuildId, messages.fetchMessages]);
return (
) : (
diff --git a/services/frontend/src/features/messages/components/MessageFeed.tsx b/services/frontend/src/features/messages/components/MessageFeed.tsx
index 94dcfe8..60a543e 100644
--- a/services/frontend/src/features/messages/components/MessageFeed.tsx
+++ b/services/frontend/src/features/messages/components/MessageFeed.tsx
@@ -1,7 +1,5 @@
import { motion } from "framer-motion";
-import { Hash } from "lucide-react";
import { useEffect, useMemo, useRef } from "react";
-import { parseMetadata } from "../../../entities/message/types";
import type { MessageRecord } from "../../../shared/api/client";
import { cardItem, cardStagger } from "../../../shared/hooks/useFramerStagger";
import { ScrollArea, EmptyStateMascot } from "../../../shared/ui";
@@ -20,12 +18,12 @@ export interface MessageFeedProps {
/** Messages from the same user within 5 minutes are visually grouped. */
const GROUP_WINDOW_MS = 5 * 60 * 1000;
-interface UserMessageGroup {
+interface MessageGroup {
messages: MessageRecord[];
}
-function groupByUser(messages: MessageRecord[]): UserMessageGroup[] {
- const groups: UserMessageGroup[] = [];
+function groupMessages(messages: MessageRecord[]): MessageGroup[] {
+ const groups: MessageGroup[] = [];
for (const msg of messages) {
const lastGroup = groups[groups.length - 1];
if (
@@ -43,57 +41,6 @@ function groupByUser(messages: MessageRecord[]): UserMessageGroup[] {
return groups;
}
-interface ChannelSection {
- key: string;
- channelId: string;
- threadId: string | null;
- label: string;
- groups: UserMessageGroup[];
-}
-
-function computeChannelSections(messages: MessageRecord[]): ChannelSection[] {
- if (messages.length === 0) return [];
-
- // Group by (channel_id, thread_id)
- const map = new Map();
- for (const msg of messages) {
- const key = msg.thread_id
- ? `${msg.channel_id}:t:${msg.thread_id}`
- : msg.channel_id;
- const list = map.get(key);
- if (list) list.push(msg);
- else map.set(key, [msg]);
- }
-
- const sections: ChannelSection[] = [];
- for (const [key, channelMessages] of map) {
- const first = channelMessages[0];
- const meta = parseMetadata(first.metadata);
- const ch = meta?.channel;
-
- let label: string;
- if (ch?.threadName && ch?.channelName) {
- label = `# ${ch.channelName} › ${ch.threadName}`;
- } else if (ch?.channelName) {
- label = `# ${ch.channelName}`;
- } else if (first.thread_id) {
- label = `thread:${first.thread_id.slice(0, 8)}`;
- } else {
- label = `# ${first.channel_id.slice(0, 8)}`;
- }
-
- sections.push({
- key,
- channelId: first.channel_id,
- threadId: first.thread_id,
- label,
- groups: groupByUser(channelMessages),
- });
- }
-
- return sections;
-}
-
export function MessageFeed({
messages,
onReanalyze,
@@ -121,7 +68,7 @@ export function MessageFeed({
return () => observer.disconnect();
}, [onLoadMore, hasMore]);
- const sections = useMemo(() => computeChannelSections(messages), [messages]);
+ const groupedMessages = useMemo(() => groupMessages(messages), [messages]);
if (loading) {
return (
@@ -147,27 +94,13 @@ export function MessageFeed({
initial="initial"
animate="animate"
>
- {sections.map((section) => (
-
- {/* Channel/Thread section header */}
-
-
-
- {section.label}
-
-
-
-
- {section.groups.map((group) => (
-
-
-
- ))}
-
-
+ {groupedMessages.map((group) => (
+
+
+
))}
{/* Infinite-scroll sentinel */}
diff --git a/services/frontend/src/features/messages/hooks/useMessages.ts b/services/frontend/src/features/messages/hooks/useMessages.ts
index 198558f..80126ea 100644
--- a/services/frontend/src/features/messages/hooks/useMessages.ts
+++ b/services/frontend/src/features/messages/hooks/useMessages.ts
@@ -29,9 +29,8 @@ export function useMessages() {
const [cursor, setCursor] = useState(null);
const [hasMore, setHasMore] = useState(false);
const currentGuild = useRef(null);
- const currentChannel = useRef(undefined);
- const fetchMessages = useCallback(async (guildId?: string, channelId?: string) => {
+ const fetchMessages = useCallback(async (guildId?: string) => {
if (!guildId) {
setMessages([]);
setCursor(null);
@@ -39,13 +38,11 @@ export function useMessages() {
return [];
}
currentGuild.current = guildId;
- currentChannel.current = channelId;
setLoading(true);
setError(null);
try {
const result = await listMessages({
guildId,
- channelId,
limit: PAGE_SIZE,
});
if (currentGuild.current === guildId) {
@@ -69,7 +66,6 @@ export function useMessages() {
try {
const result = await listMessages({
guildId: currentGuild.current,
- channelId: currentChannel.current,
cursor,
limit: PAGE_SIZE,
});
@@ -113,7 +109,6 @@ export function useMessages() {
);
const { count } = await reanalyzeErrorBatch({
guildId: currentGuild.current ?? undefined,
- channelId: currentChannel.current,
});
return count;
}, []);
diff --git a/services/frontend/src/features/messages/index.tsx b/services/frontend/src/features/messages/index.tsx
index e7bcbf3..d682e80 100644
--- a/services/frontend/src/features/messages/index.tsx
+++ b/services/frontend/src/features/messages/index.tsx
@@ -1,7 +1,7 @@
import { motion } from "framer-motion";
-import { Filter, Hash, RotateCw, Search, X } from "lucide-react";
+import { Filter, RotateCw, Search, X } from "lucide-react";
import { useMemo, useState } from "react";
-import type { Channel, MessageRecord } from "../../shared/api/client";
+import type { MessageRecord } from "../../shared/api/client";
import { cardItem, cardStagger } from "../../shared/hooks/useFramerStagger";
import {
Badge,
@@ -11,7 +11,6 @@ import {
CardHeader,
CardTitle,
Input,
- Select,
Tabs,
TabsContent,
TabsList,
@@ -28,9 +27,6 @@ interface MessagesPanelProps {
onLoadMore?: () => void;
hasMore?: boolean;
loadingMore?: boolean;
- textChannels?: Channel[];
- selectedChannel?: string;
- onChannelChange?: (channelId: string) => void;
}
type AiFilter = "all" | "clean" | "flagged" | "error" | "pending";
@@ -43,9 +39,6 @@ export function MessagesPanel({
onLoadMore,
hasMore,
loadingMore,
- textChannels,
- selectedChannel,
- onChannelChange,
}: MessagesPanelProps) {
const [searchQuery, setSearchQuery] = useState("");
const [searchResults, setSearchResults] = useState([]);
@@ -125,23 +118,6 @@ export function MessagesPanel({
Messages are automatically captured from all text channels in the
monitored guild. Real-time updates arrive via WebSocket.
- {textChannels && textChannels.length > 0 && (
-
-
-
- )}