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) => (