feat(search): support from:username with no body text
Typing "from:jared" with no additional text now shows all cached messages from that user across all rooms. - SearchInput: call onSearch() even when only from: tokens were extracted (no remaining body text), passing an empty string term - useLocalMessageSearch: introduce senderOnlyMode (empty term + senders set) which searches ALL rooms instead of encrypted-only, and skips text matching — just filters by sender - MessageSearch: define hasActiveSearch / senderOnlyMode flags; use them to enable local search and fix placeholder/loading/results conditions; adapt local results section header and description Server-side search is skipped in sender-only mode (Matrix search API requires a search_term); results come from the local event cache. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -213,12 +213,21 @@ export function MessageSearch({
|
||||
[msgSearchParams.rooms, searchPathSearchParams.global, allRooms, rooms],
|
||||
);
|
||||
|
||||
// Run synchronous client-side search over encrypted rooms immediately.
|
||||
// term === undefined → no search started
|
||||
// term === '' → sender-only search (from:user with no body text)
|
||||
// term === 'foo' → normal text search
|
||||
const hasActiveSearch =
|
||||
msgSearchParams.term !== undefined || !!msgSearchParams.senders?.length;
|
||||
const senderOnlyMode = !msgSearchParams.term && !!msgSearchParams.senders?.length;
|
||||
|
||||
// Run synchronous client-side search immediately.
|
||||
// In text-search mode: covers encrypted rooms only (server handles plaintext).
|
||||
// In sender-only mode: covers all rooms (server has no sender-only search).
|
||||
// cacheVersion in deps so it re-runs after "Load more" paginates new events.
|
||||
const localResult = useMemo(() => {
|
||||
if (!msgSearchParams.term) return null;
|
||||
if (!hasActiveSearch) return null;
|
||||
return searchLocalMessages({
|
||||
term: msgSearchParams.term,
|
||||
term: msgSearchParams.term ?? '',
|
||||
roomIds: localSearchRooms,
|
||||
senders: msgSearchParams.senders,
|
||||
});
|
||||
@@ -404,19 +413,19 @@ export function MessageSearch({
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{!msgSearchParams.term && status === 'pending' && (
|
||||
{!hasActiveSearch && (
|
||||
<PageHeroEmpty>
|
||||
<PageHeroSection>
|
||||
<PageHero
|
||||
icon={<Icon size="600" src={Icons.Message} />}
|
||||
title="Search Messages"
|
||||
subTitle="Find helpful messages in your community by searching with related keywords."
|
||||
subTitle="Find helpful messages in your community by searching with related keywords, or type from:@user to see all messages from someone."
|
||||
/>
|
||||
</PageHeroSection>
|
||||
</PageHeroEmpty>
|
||||
)}
|
||||
|
||||
{msgSearchParams.term && groups.length === 0 && status === 'success' && (
|
||||
{hasActiveSearch && !senderOnlyMode && groups.length === 0 && status === 'success' && (
|
||||
<Box direction="Column" gap="200">
|
||||
<Box
|
||||
className={ContainerColor({ variant: 'Warning' })}
|
||||
@@ -450,7 +459,7 @@ export function MessageSearch({
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{((msgSearchParams.term && status === 'pending') ||
|
||||
{((!senderOnlyMode && msgSearchParams.term && status === 'pending') ||
|
||||
(groups.length > 0 && vItems.length === 0)) && (
|
||||
<Box direction="Column" gap="100">
|
||||
{[...Array(8).keys()].map((key) => (
|
||||
@@ -460,6 +469,7 @@ export function MessageSearch({
|
||||
)}
|
||||
|
||||
{msgSearchParams.term &&
|
||||
!senderOnlyMode &&
|
||||
localResult &&
|
||||
localResult.encryptedRoomsCount > 0 &&
|
||||
vItems.length > 0 && (
|
||||
@@ -524,20 +534,24 @@ export function MessageSearch({
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{localResult && localResult.encryptedRoomsCount > 0 && (
|
||||
{localResult && (senderOnlyMode ? localResult.groups.length > 0 : localResult.encryptedRoomsCount > 0) && (
|
||||
<Box direction="Column" gap="300">
|
||||
<Box direction="Column" gap="200">
|
||||
<Box alignItems="Center" gap="200">
|
||||
<Icon size="200" src={Icons.Lock} />
|
||||
<Text size="H5">Encrypted Rooms</Text>
|
||||
<Text size="T200" style={{ opacity: 0.55 }}>
|
||||
{`${localResult.searchedRoomsCount} / ${localResult.encryptedRoomsCount} cached`}
|
||||
</Text>
|
||||
<Icon size="200" src={senderOnlyMode ? Icons.User : Icons.Lock} />
|
||||
<Text size="H5">{senderOnlyMode ? 'Messages from user' : 'Encrypted Rooms'}</Text>
|
||||
{!senderOnlyMode && (
|
||||
<Text size="T200" style={{ opacity: 0.55 }}>
|
||||
{`${localResult.searchedRoomsCount} / ${localResult.encryptedRoomsCount} cached`}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Text size="T300" priority="300">
|
||||
{localResult.groups.length > 0
|
||||
? `Showing locally cached messages from ${localResult.searchedRoomsCount} encrypted room${localResult.searchedRoomsCount !== 1 ? 's' : ''}. Load more history below to extend coverage.`
|
||||
: `No matches in your local cache. Load messages below to search further back.`}
|
||||
{senderOnlyMode
|
||||
? `Showing locally cached messages from this user across all rooms. Open more rooms or load history below to extend coverage.`
|
||||
: localResult.groups.length > 0
|
||||
? `Showing locally cached messages from ${localResult.searchedRoomsCount} encrypted room${localResult.searchedRoomsCount !== 1 ? 's' : ''}. Load more history below to extend coverage.`
|
||||
: `No matches in your local cache. Load messages below to search further back.`}
|
||||
</Text>
|
||||
<Line size="300" variant="Surface" />
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user