feat(search): in:, before:/after:, has:, is:pinned operators (#106)
CI / Build & Quality Checks (push) Successful in 1m42s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Successful in 10m39s
CI / Build & Quality Checks (push) Successful in 1m42s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Successful in 10m39s
Typed operators alongside the existing from:, each one setting the filter the
buttons already set (so typing and clicking end in the same state, shown in
the filter bar):
in:general in:"voice lounge" room by name / alias / id (unique partial
name ok); a room outside this page's scope
switches to all rooms
after:2026-09-01 before:7d date range; relative h/d/w/m/y
has:link | image | video | file contains-link / media filters
is:pinned pinned-only filter
- searchOperators.ts: pure parser + room resolver (unit-tested); quoted
values; keys case-insensitive; only at a word start, so URLs and times
aren't mistaken for operators; anything it doesn't understand stays in the
searched text with a notice listing the valid operators.
- Notices: no matching room; operators with no words or from: ("Add a word to
search for, or a from:@user.") — then the text is kept in the box.
- from:bob now resolves to a known @bob:server (this homeserver first); the
old path sent "@bob", which matched nobody unless picked from autocomplete.
Verified in Chromium on a local Synapse: `raid in:"Thread Lab" from:bob`
sets rooms=!…Thread Lab, senders=@bob:localhost and finds bob's message;
dates, an unknown room, operators-only and has:gif give the expected params
and notices.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
06bb896a00
commit
75951f9040
@@ -24,6 +24,7 @@ import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { getMxIdLocalPart, getMxIdServer, mxcUrlToHttp } from '../../utils/matrix';
|
||||
import { UserAvatar } from '../../components/user-avatar';
|
||||
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||
import { ParsedSearch, hasOperators, parseSearchQuery } from './searchOperators';
|
||||
|
||||
// Matches "from:@?anything" anywhere — @ is optional
|
||||
const FROM_REGEX = /from:@?([^\s:][^\s]*)/gi;
|
||||
@@ -43,6 +44,9 @@ type SearchInputProps = {
|
||||
onSearch: (term: string) => void;
|
||||
onReset: () => void;
|
||||
onSenderAdd?: (userId: string) => void;
|
||||
/** [Gitea #106] Receives in:/before:/after:/has:/is: (and from:) as filters. */
|
||||
/** Returns false when the query wasn't run, so the text is kept as typed. */
|
||||
onQuery?: (parsed: ParsedSearch, raw: string) => boolean;
|
||||
recentSearches?: string[];
|
||||
onRecentSearch?: (term: string) => void;
|
||||
onClearRecentSearches?: () => void;
|
||||
@@ -54,6 +58,7 @@ export function SearchInput({
|
||||
onSearch,
|
||||
onReset,
|
||||
onSenderAdd,
|
||||
onQuery,
|
||||
recentSearches,
|
||||
onRecentSearch,
|
||||
onClearRecentSearches,
|
||||
@@ -167,6 +172,19 @@ export function SearchInput({
|
||||
|
||||
const rawValue = searchInput.value.trim();
|
||||
|
||||
if (onQuery) {
|
||||
const parsed = parseSearchQuery(rawValue);
|
||||
if (!parsed.term && !hasOperators(parsed)) return;
|
||||
// Recognised operators become filters (chips / controls); drop them
|
||||
// from the visible input like from: always did.
|
||||
const ran = onQuery(parsed, rawValue);
|
||||
if (ran && hasOperators(parsed) && searchInputRef.current) {
|
||||
searchInputRef.current.value = parsed.term;
|
||||
}
|
||||
closeAutocomplete();
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract from:user fragments and convert to sender filters
|
||||
const fromMatches = [...rawValue.matchAll(FROM_REGEX)];
|
||||
fromMatches.forEach((match) => {
|
||||
|
||||
Reference in New Issue
Block a user