From 1b7106d511fe21c8fadb4dfdab26f480038a8170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 9 Oct 2026 08:17:34 -0400 Subject: [PATCH] Q188: search shows only the answer to the current words, and says while it is searching Co-Authored-By: Claude Opus 5.5 --- src/renderer/src/i18n/zh.ts | 2 ++ src/renderer/src/shared/SearchDialog.tsx | 35 ++++++++++++++++-------- tests/e2e/search-open.spec.ts | 11 ++++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/renderer/src/i18n/zh.ts b/src/renderer/src/i18n/zh.ts index 6665a2e3..054572bb 100644 --- a/src/renderer/src/i18n/zh.ts +++ b/src/renderer/src/i18n/zh.ts @@ -696,6 +696,8 @@ export const zh: Record = { "Working language": "工作语言", "That did not work": "操作未成功", "Working…": "处理中…", + "Searching…": "正在搜索…", + "Search failed: {reason}": "搜索失败:{reason}", "Not up to date.": "数据不是最新的。", "Showing data from {time}.": "当前显示 {time} 的数据。", "Updating failed: {reason} (attempt {count}).": "更新失败:{reason}(第 {count} 次尝试)。", diff --git a/src/renderer/src/shared/SearchDialog.tsx b/src/renderer/src/shared/SearchDialog.tsx index dd5530de..fff2f196 100644 --- a/src/renderer/src/shared/SearchDialog.tsx +++ b/src/renderer/src/shared/SearchDialog.tsx @@ -37,16 +37,22 @@ export function SearchDialog({ }) { const [query, setQuery] = useState(""); const [filter, setFilter] = useState("all"); - const [results, setResults] = useState([]); + // An answer belongs to the company, filter and words it was asked for; only the current question's answer is shown. + const [answer, setAnswer] = useState<{ key: string; results: SearchResultRecord[]; error: string | null } | null>(null); const orgId = snapshot.activeOrgId; const employee = useMemo(() => snapshot.employees.find((item) => item.orgId === orgId), [snapshot.employees, orgId]); - const canSearch = Boolean(orgId && employee && query.trim().length >= 2); - const visibleResults = canSearch ? results : []; + const trimmed = query.trim(); + const canSearch = Boolean(orgId && employee && trimmed.length >= 2); + const key = `${orgId}|${filter}|${trimmed}`; + const current = canSearch && answer?.key === key ? answer : null; + const visibleResults = current?.results ?? []; + const searching = canSearch && !current; useEffect(() => { - if (!open) return; - const trimmed = query.trim(); - if (!orgId || !employee || trimmed.length < 2) return; + if (!open || !orgId || !employee || trimmed.length < 2) return; + // A reply to a question that has since changed is dropped, so a slow old answer never replaces a newer one. + let live = true; + const asked = `${orgId}|${filter}|${trimmed}`; const timeout = window.setTimeout(() => { void window.agentCompany.search({ orgId, @@ -55,12 +61,17 @@ export function SearchDialog({ entityTypes: filter === "all" ? undefined : [filter], limit: 12, }).then((result) => { - if (!result.ok) throw new Error(result.error.message); - setResults(result.data); + if (!live) return; + setAnswer(result.ok ? { key: asked, results: result.data, error: null } : { key: asked, results: [], error: result.error.message }); + }, (reason: unknown) => { + if (live) setAnswer({ key: asked, results: [], error: reason instanceof Error ? reason.message : String(reason) }); }); }, 120); - return () => window.clearTimeout(timeout); - }, [employee, filter, open, orgId, query]); + return () => { + live = false; + window.clearTimeout(timeout); + }; + }, [employee, filter, open, orgId, trimmed]); useEffect(() => { if (!open) return; @@ -115,7 +126,9 @@ export function SearchDialog({ -
+
+ {searching &&
{t("Searching…")}
} + {current?.error &&
{t("Search failed: {reason}", { reason: current.error })}
} {visibleResults.map((result) => (