Make the chat channel search functional
The conversation header search button was inert. Toggle a search field that filters the channel's root messages by body text; clearing or pressing Escape restores the full timeline. Search stays scoped to the open space, so results respect the membership the user already has.
This commit is contained in:
1 parent
bc0865ede7
commit
7c5978d5df
1 file changed
+33
-5
@@ -1,4 +1,4 @@
|
||||
import type { FormEvent, MutableRefObject } from "react";
|
||||
import { useState, type FormEvent, type MutableRefObject } from "react";
|
||||
import { AvatarStack, Icon, IconButton } from "../../shared/ui";
|
||||
import type {
|
||||
ApprovalRecord,
|
||||
@@ -53,13 +53,18 @@ export function ChatConversation({
|
||||
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
|
||||
composerRef: MutableRefObject<HTMLTextAreaElement | null>;
|
||||
}) {
|
||||
const [query, setQuery] = useState("");
|
||||
const trimmed = query.trim().toLowerCase();
|
||||
const visibleRoots = trimmed
|
||||
? rootMessages.filter((message) => message.bodyMd.toLowerCase().includes(trimmed))
|
||||
: rootMessages;
|
||||
return (
|
||||
<section style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", background: "var(--panel)" }}>
|
||||
{space && <ConversationHeader space={space} participants={participants} />}
|
||||
{space && <ConversationHeader space={space} participants={participants} query={query} onQueryChange={setQuery} />}
|
||||
<div style={{ flex: 1, overflow: "auto", padding: "20px 24px 8px" }}>
|
||||
{space && (
|
||||
<ChatTimeline
|
||||
rootMessages={rootMessages}
|
||||
rootMessages={visibleRoots}
|
||||
messagesInSpace={messagesInSpace}
|
||||
threadsInSpace={threadsInSpace}
|
||||
pendingApprovals={pendingApprovals}
|
||||
@@ -89,10 +94,21 @@ export function ChatConversation({
|
||||
function ConversationHeader({
|
||||
space,
|
||||
participants,
|
||||
query,
|
||||
onQueryChange,
|
||||
}: {
|
||||
space: ChatSpaceRecord;
|
||||
participants: string[];
|
||||
query: string;
|
||||
onQueryChange: (next: string) => void;
|
||||
}) {
|
||||
const [searchOpen, setSearchOpen] = useState(false);
|
||||
const toggleSearch = () => {
|
||||
setSearchOpen((open) => {
|
||||
if (open) onQueryChange("");
|
||||
return !open;
|
||||
});
|
||||
};
|
||||
return (
|
||||
<header className="row" style={{ height: 52, padding: "0 24px", borderBottom: "1px solid var(--border-soft)", flexShrink: 0 }}>
|
||||
<Icon name={space.visibility === "restricted" ? "lock" : "hash"} size={15} color="var(--text-3)" />
|
||||
@@ -114,8 +130,20 @@ function ConversationHeader({
|
||||
{space.topic}
|
||||
</span>
|
||||
<div style={{ marginLeft: "auto", display: "flex", gap: 8, alignItems: "center" }}>
|
||||
{participants.length > 0 && <AvatarStack names={participants.slice(0, 5)} bot />}
|
||||
<IconButton icon="search" label="Search" />
|
||||
{searchOpen && (
|
||||
<input
|
||||
className="input"
|
||||
autoFocus
|
||||
aria-label="Search messages"
|
||||
placeholder="Search messages"
|
||||
value={query}
|
||||
onChange={(event) => onQueryChange(event.target.value)}
|
||||
onKeyDown={(event) => { if (event.key === "Escape") toggleSearch(); }}
|
||||
style={{ width: 200, height: 28, padding: "0 10px" }}
|
||||
/>
|
||||
)}
|
||||
{!searchOpen && participants.length > 0 && <AvatarStack names={participants.slice(0, 5)} bot />}
|
||||
<IconButton icon="search" label="Search" onClick={toggleSearch} />
|
||||
<IconButton icon="more" label="More" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
Reference in new issue
Block a user