Skip to content

Commit d241625

Browse files
committed
address PR comments
1 parent b6eaf44 commit d241625

8 files changed

Lines changed: 736 additions & 270 deletions

File tree

apps/roam/src/components/AdvancedNodeSearchDialog/AdvancedSearchDialog.tsx

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
Spinner,
88
SpinnerSize,
99
} from "@blueprintjs/core";
10-
import MiniSearch from "minisearch";
1110
import posthog from "posthog-js";
1211
import { render as renderToast } from "roamjs-components/components/Toast";
1312
import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid";
@@ -24,7 +23,7 @@ import getDiscourseNodes, {
2423
type DiscourseNode,
2524
} from "~/utils/getDiscourseNodes";
2625
import { openSearchResultInMain } from "~/utils/advancedSearchFooterUtils";
27-
import { openDgSearchInSidebar } from "~/utils/openDgSearchInSidebar";
26+
import { mountAdvancedSearchInSidebar } from "./mountAdvancedSearchInSidebar";
2827
import {
2928
DEBOUNCE_MS,
3029
DEFAULT_SORT_CONFIG,
@@ -33,14 +32,16 @@ import {
3332
buildSearchIndex,
3433
formatMetadataDate,
3534
getSearchKeywords,
36-
searchIndexedNodes,
37-
sortSearchResults,
3835
stripTypePrefix,
3936
} from "./utils";
4037
import { DiscourseNodeTypeFilter } from "~/components/AdvancedNodeSearchDialog/DiscourseNodeTypeFilter";
4138
import { RenderRoamBlock, RenderRoamPage } from "~/utils/roamReactComponents";
4239
import { AdvancedSearchFooter } from "./AdvancedSearchFooter";
4340
import { AdvancedSearchDialogResultsList } from "./AdvancedSearchSidebarPanel";
41+
import {
42+
type SearchIndex,
43+
useAdvancedNodeSearchResults,
44+
} from "./useAdvancedNodeSearchResults";
4445

4546
type Props = Record<string, unknown>;
4647

@@ -90,14 +91,10 @@ const AdvancedNodeSearchDialog = ({
9091
const [isIndexLoading, setIsIndexLoading] = useState(false);
9192
const [indexError, setIndexError] = useState(false);
9293
const [activeIndex, setActiveIndex] = useState(0);
93-
const [results, setResults] = useState<SearchResult[]>([]);
94+
const [searchIndex, setSearchIndex] = useState<SearchIndex | null>(null);
9495
const [sort, setSort] = useState<SortConfig>(DEFAULT_SORT_CONFIG);
9596
const [discourseNodes, setDiscourseNodes] = useState<DiscourseNode[]>([]);
9697
const [selectedNodeTypeIds, setSelectedNodeTypeIds] = useState<string[]>([]);
97-
const miniSearchRef = useRef<MiniSearch<
98-
SearchResult & { id: string }
99-
> | null>(null);
100-
const allResultsRef = useRef<SearchResult[]>([]);
10198
const resultsPanelRef = useRef<HTMLDivElement | null>(null);
10299
const inputRef = useRef<HTMLInputElement | null>(null);
103100
const [insertTarget, setInsertTarget] = useState<InsertTarget | null>(null);
@@ -106,6 +103,15 @@ const AdvancedNodeSearchDialog = ({
106103
discourseNodes.map((node) => [node.type, node]),
107104
);
108105

106+
const results = useAdvancedNodeSearchResults({
107+
debouncedSearchTerm,
108+
selectedNodeTypeIds,
109+
sort,
110+
isIndexLoading,
111+
indexError,
112+
searchIndex,
113+
});
114+
109115
const activeResult = results[activeIndex] ?? null;
110116
const keywords = getSearchKeywords(debouncedSearchTerm);
111117

@@ -133,44 +139,16 @@ const AdvancedNodeSearchDialog = ({
133139
setActiveIndex(0);
134140
setSort(DEFAULT_SORT_CONFIG);
135141
setSelectedNodeTypeIds([]);
136-
setResults([]);
142+
setSearchIndex(null);
137143
setIndexError(false);
138144
}
139145
}, [isOpen]);
140146

141-
useEffect(() => {
142-
if (
143-
!isOpen ||
144-
isIndexLoading ||
145-
indexError ||
146-
!debouncedSearchTerm ||
147-
!miniSearchRef.current
148-
) {
149-
setResults([]);
150-
return;
151-
}
152-
153-
const scoredHits = searchIndexedNodes({
154-
miniSearch: miniSearchRef.current,
155-
allResults: allResultsRef.current,
156-
searchTerm: debouncedSearchTerm,
157-
typeFilter: selectedNodeTypeIds.length ? selectedNodeTypeIds : undefined,
158-
});
159-
160-
setResults(sortSearchResults({ hits: scoredHits, sort }));
161-
}, [
162-
debouncedSearchTerm,
163-
indexError,
164-
isIndexLoading,
165-
isOpen,
166-
selectedNodeTypeIds,
167-
sort,
168-
]);
169-
170147
useEffect(() => {
171148
let cancelled = false;
172149
setIsIndexLoading(true);
173150
setIndexError(false);
151+
setSearchIndex(null);
174152

175153
const discourseNodes = getDiscourseNodes().filter(
176154
(node) => node.backedBy === "user",
@@ -180,8 +158,7 @@ const AdvancedNodeSearchDialog = ({
180158
void buildSearchIndex(discourseNodes)
181159
.then(({ miniSearch, results: indexedResults }) => {
182160
if (cancelled) return;
183-
miniSearchRef.current = miniSearch;
184-
allResultsRef.current = indexedResults;
161+
setSearchIndex({ miniSearch, allResults: indexedResults });
185162
})
186163
.catch((error) => {
187164
console.error("Error building advanced node search index:", error);
@@ -258,7 +235,7 @@ const AdvancedNodeSearchDialog = ({
258235
if (contentState !== "results" || !results.length) return;
259236

260237
try {
261-
await openDgSearchInSidebar({
238+
await mountAdvancedSearchInSidebar({
262239
query: debouncedSearchTerm,
263240
results,
264241
selectedNodeTypeIds,

apps/roam/src/components/AdvancedNodeSearchDialog/AdvancedSearchSidebarPanel.tsx

Lines changed: 46 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useMemo, useRef, useState } from "react";
1+
import React, { useEffect, useMemo, useState } from "react";
22
import {
33
Button,
44
Icon,
@@ -8,16 +8,13 @@ import {
88
SpinnerSize,
99
Tag,
1010
} from "@blueprintjs/core";
11-
import MiniSearch from "minisearch";
1211
import getDiscourseNodes from "~/utils/getDiscourseNodes";
13-
import type { DockedSearchState } from "~/utils/openDgSearchInSidebar";
1412
import {
1513
DEBOUNCE_MS,
14+
type DockedSearchState,
1615
type SearchResult,
1716
buildSearchIndex,
1817
getSearchKeywords,
19-
searchIndexedNodes,
20-
sortSearchResults,
2118
} from "./utils";
2219
import { hasActiveTypeFilter } from "~/utils/discourseNodeTypeFilter";
2320
import { formatHexColor } from "~/components/settings/DiscourseNodeCanvasSettings";
@@ -27,6 +24,10 @@ import type { DiscourseNode } from "~/utils/getDiscourseNodes";
2724
import { getNodeTagStyles } from "~/utils/getDiscourseNodeColors";
2825
import { splitWithHighlights, stripTypePrefix } from "./utils";
2926
import { openSearchResultFromLinkEvent } from "~/utils/advancedSearchFooterUtils";
27+
import {
28+
type SearchIndex,
29+
useAdvancedNodeSearchResults,
30+
} from "./useAdvancedNodeSearchResults";
3031

3132
const renderHighlightedText = (
3233
text: string,
@@ -168,13 +169,6 @@ type AdvancedSearchDockedFiltersProps = {
168169
const getNodeIndicatorColor = (node: DiscourseNode): string =>
169170
formatHexColor(node.canvasSettings?.color) || "#6b7280";
170171

171-
type SidebarIndexCache = {
172-
miniSearch: MiniSearch<SearchResult & { id: string }>;
173-
results: SearchResult[];
174-
};
175-
176-
let cachedSidebarIndex: SidebarIndexCache | null = null;
177-
178172
const AdvancedSearchDockedFilters = ({
179173
discourseNodes,
180174
selectedNodeTypeIds,
@@ -193,15 +187,13 @@ const AdvancedSearchDockedFilters = ({
193187
if (!isTypeFilterActive && !showSort) return null;
194188

195189
return (
196-
<div className="dg-node-search-sidebar__filters mb-1 ml-[9px] mr-2 flex flex-col gap-1.5">
190+
<div className="dg-node-search-sidebar__filters mb-1 ml-2 mr-2 flex flex-col gap-1.5">
197191
{isTypeFilterActive && (
198192
<div className="mt-1 flex flex-wrap items-center gap-1">
199-
<span className="shrink-0 text-[0.8em] text-gray-500">
200-
Filtered to
201-
</span>
193+
<span className="shrink-0 text-xs text-gray-500">Filter:</span>
202194
{selectedNodes.map((node) => (
203195
<Tag
204-
className="!text-[0.8em]"
196+
className="!text-xs"
205197
key={node.type}
206198
minimal
207199
round
@@ -222,7 +214,7 @@ const AdvancedSearchDockedFilters = ({
222214
</div>
223215
)}
224216
{showSort && (
225-
<p className="text-[0.8em] text-gray-500">
217+
<p className="text-xs text-gray-500">
226218
Sorted by {SORT_FIELD_LABELS[sort.field]} (
227219
{sort.direction === "asc" ? "ascending" : "descending"})
228220
</p>
@@ -232,11 +224,17 @@ const AdvancedSearchDockedFilters = ({
232224
};
233225

234226
type AdvancedSearchSidebarPanelProps = {
227+
dgSearchId: string;
235228
dockedState: DockedSearchState;
229+
onPersistState: (state: DockedSearchState) => void;
230+
windowId: string;
236231
};
237232

238233
export const AdvancedSearchSidebarPanel = ({
234+
dgSearchId,
239235
dockedState,
236+
onPersistState,
237+
windowId,
240238
}: AdvancedSearchSidebarPanelProps) => {
241239
const {
242240
query,
@@ -247,21 +245,15 @@ export const AdvancedSearchSidebarPanel = ({
247245

248246
const [searchTerm, setSearchTerm] = useState(query);
249247
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(query);
250-
const [results, setResults] = useState<SearchResult[]>(dockedResults);
248+
const [searchIndex, setSearchIndex] = useState<SearchIndex | null>(null);
251249
const [isIndexLoading, setIsIndexLoading] = useState(true);
252250
const [indexError, setIndexError] = useState(false);
253251

254-
const miniSearchRef = useRef<MiniSearch<
255-
SearchResult & { id: string }
256-
> | null>(null);
257-
const allResultsRef = useRef<SearchResult[]>([]);
258-
259252
const discourseNodes = useMemo(
260253
() => getDiscourseNodes().filter((node) => node.backedBy === "user"),
261254
[],
262255
);
263256
const keywords = getSearchKeywords(debouncedSearchTerm);
264-
const isDockedQuery = debouncedSearchTerm.trim() === query.trim();
265257

266258
useEffect(() => {
267259
const timeout = setTimeout(
@@ -275,31 +267,13 @@ export const AdvancedSearchSidebarPanel = ({
275267
let cancelled = false;
276268
setIsIndexLoading(true);
277269
setIndexError(false);
278-
279-
const applyIndex = ({
280-
miniSearch,
281-
results: indexedResults,
282-
}: SidebarIndexCache): void => {
283-
if (cancelled) return;
284-
miniSearchRef.current = miniSearch;
285-
allResultsRef.current = indexedResults;
286-
setIsIndexLoading(false);
287-
};
288-
289-
if (cachedSidebarIndex) {
290-
applyIndex(cachedSidebarIndex);
291-
return () => {
292-
cancelled = true;
293-
};
294-
}
270+
setSearchIndex(null);
295271

296272
void buildSearchIndex(discourseNodes)
297273
.then(({ miniSearch, results: indexedResults }) => {
298-
cachedSidebarIndex = {
299-
miniSearch,
300-
results: indexedResults,
301-
};
302-
applyIndex(cachedSidebarIndex);
274+
if (cancelled) return;
275+
setSearchIndex({ miniSearch, allResults: indexedResults });
276+
setIsIndexLoading(false);
303277
})
304278
.catch((error) => {
305279
console.error(
@@ -317,32 +291,36 @@ export const AdvancedSearchSidebarPanel = ({
317291
};
318292
}, [discourseNodes]);
319293

320-
useEffect(() => {
321-
if (!debouncedSearchTerm) {
322-
setResults([]);
323-
return;
324-
}
325-
326-
if (isIndexLoading || indexError || !miniSearchRef.current) {
327-
if (!isDockedQuery) setResults([]);
328-
return;
329-
}
294+
const results = useAdvancedNodeSearchResults({
295+
debouncedSearchTerm,
296+
selectedNodeTypeIds,
297+
sort,
298+
isIndexLoading,
299+
indexError,
300+
searchIndex,
301+
dockedQuery: query,
302+
dockedResults,
303+
});
330304

331-
const scoredHits = searchIndexedNodes({
332-
miniSearch: miniSearchRef.current,
333-
allResults: allResultsRef.current,
334-
searchTerm: debouncedSearchTerm,
335-
typeFilter: selectedNodeTypeIds.length ? selectedNodeTypeIds : undefined,
305+
useEffect(() => {
306+
if (!debouncedSearchTerm) return;
307+
308+
onPersistState({
309+
query: debouncedSearchTerm,
310+
results,
311+
selectedNodeTypeIds,
312+
sort,
313+
windowId,
314+
dgSearchId,
336315
});
337-
338-
setResults(sortSearchResults({ hits: scoredHits, sort }));
339316
}, [
340317
debouncedSearchTerm,
341-
indexError,
342-
isDockedQuery,
343-
isIndexLoading,
318+
dgSearchId,
319+
onPersistState,
320+
results,
344321
selectedNodeTypeIds,
345322
sort,
323+
windowId,
346324
]);
347325

348326
const resultLabel =
@@ -376,7 +354,7 @@ export const AdvancedSearchSidebarPanel = ({
376354
/>
377355
) : (
378356
<>
379-
<span className="ml-[9px] text-[0.9em]">
357+
<span className="ml-2 text-xs">
380358
{isIndexLoading && !results.length
381359
? "Loading…"
382360
: debouncedSearchTerm

0 commit comments

Comments
 (0)