feat: implement message search and clean up header buttons

Remove placeholder buttons (Threads, Inbox, Help) from channel and DM
headers. Add full-text message search with backend endpoints for both
space channels and DMs, supporting filters (from, has, before, after)
and pagination. Search popover with debounced input, highlighted matches,
and jump-to-message that scrolls with a highlight animation. Includes
messages/around endpoints for loading context when jumping to uncached
messages.
This commit is contained in:
Jannis Braun
2026-03-11 00:22:00 +01:00
parent f898690302
commit 5300e78d9d
10 changed files with 1037 additions and 33 deletions
+29
View File
@@ -48,6 +48,7 @@ interface ChatState {
removeReaction: (messageId: string, emoji: string) => void;
onReactionAdded: (messageId: string, reaction: any) => void;
onReactionRemoved: (messageId: string, userId: string, emoji: string) => void;
loadMessagesAround: (channelId: string, messageId: string) => Promise<void>;
setTyping: (channelId: string, userId: string, username: string) => void;
clearTyping: (channelId: string, userId: string) => void;
getMessages: (channelId: string) => MessageWithUser[];
@@ -198,6 +199,34 @@ export const useChatStore = create<ChatState>((set, get) => ({
}
},
loadMessagesAround: async (channelId: string, messageId: string) => {
const isDm = isDmChannel(channelId);
if (!isDm && !useSpaceStore.getState().channelOriginMap.has(channelId)) return;
try {
const origin = getChannelOrigin(channelId);
const client = getApiForOrigin(origin);
const messages = isDm
? await client.dm.messagesAround(channelId, messageId)
: await client.channels.messagesAround(channelId, messageId);
if (origin) {
for (const msg of messages) normalizeMessageAssets(msg, origin);
}
set((state) => {
const newMessages = new Map(state.messages);
newMessages.set(channelId, messages as MessageWithUser[]);
const newHasMore = new Map(state.hasMore);
newHasMore.set(channelId, true);
const newAccessTimes = new Map(state.channelAccessTimes);
newAccessTimes.set(channelId, Date.now());
return { messages: newMessages, hasMore: newHasMore, channelAccessTimes: newAccessTimes };
});
} catch (err) {
console.error('Failed to load messages around:', err);
}
},
sendMessage: async (channelId: string, content: string, attachmentIds?: string[]) => {
const replyToId = get().replyTo?.id;
const isDm = isDmChannel(channelId);