Skip to content

Commit 1789a18

Browse files
committed
search: further refactoring to make it more generic
1 parent ea80e0a commit 1789a18

File tree

3 files changed

+163
-132
lines changed

3 files changed

+163
-132
lines changed
Lines changed: 10 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,20 @@
1-
/*
2-
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3-
* License: MS-RSL – see LICENSE.md for details
4-
*/
5-
6-
/*
7-
Full text search that is better than a simple filter.
8-
*/
9-
10-
import { useFrameContext } from "@cocalc/frontend/frame-editors/frame-tree/frame-context";
11-
import type { EditorDescription } from "@cocalc/frontend/frame-editors/frame-tree/types";
12-
import { Card, Input } from "antd";
13-
import { path_split, separate_file_extension, set } from "@cocalc/util/misc";
14-
import { useEffect, useMemo, useState } from "react";
15-
import { throttle } from "lodash";
16-
import useSearchIndex from "./use-search-index";
17-
import ShowError from "@cocalc/frontend/components/error";
181
import StaticMarkdown from "@cocalc/frontend/editors/slate/static-markdown";
192
import { TimeAgo } from "@cocalc/frontend/components";
20-
import { useEditorRedux } from "@cocalc/frontend/app-framework";
21-
import type { ChatState } from "@cocalc/frontend/chat/store";
22-
23-
interface Props {
24-
font_size: number;
25-
desc;
26-
}
27-
28-
function Search({ font_size, desc }: Props) {
29-
const { project_id, path, actions, id } = useFrameContext();
30-
const useEditor = useEditorRedux<ChatState>({ project_id, path });
31-
const messages = useEditor("messages");
32-
const [indexedMessages, setIndexedMessages] = useState<any>(messages);
33-
const [search, setSearch] = useState<string>(desc.get("data-search") ?? "");
34-
const [result, setResult] = useState<any>(null);
35-
const saveSearch = useMemo(
36-
() =>
37-
throttle((search) => {
38-
if (!actions.isClosed()) {
39-
actions.set_frame_data({ id, search });
40-
}
41-
}, 250),
42-
[project_id, path],
43-
);
44-
45-
const { error, setError, index, doRefresh, fragmentKey } = useSearchIndex();
46-
47-
useEffect(() => {
48-
if (index == null) {
49-
return;
50-
}
51-
if (!search.trim()) {
52-
setResult([]);
53-
return;
54-
}
55-
(async () => {
56-
const result = await index.search({ term: search });
57-
setResult(result);
58-
})();
59-
}, [search, index]);
60-
61-
useEffect(() => {
62-
if (indexedMessages != messages) {
63-
setIndexedMessages(messages);
64-
doRefresh();
65-
}
66-
}, [messages]);
3+
import { createSearchEditor } from "@cocalc/frontend/frame-editors/generic/search";
674

5+
function Preview({ id, content }) {
686
return (
69-
<div className="smc-vfill">
70-
<Card
71-
title={
72-
<>
73-
Search Chatroom{" "}
74-
{separate_file_extension(path_split(path).tail).name}
75-
</>
76-
}
77-
style={{ fontSize: font_size }}
78-
>
79-
<ShowError
80-
error={error}
81-
setError={setError}
82-
style={{ marginBottom: "15px" }}
83-
/>
84-
<Input.Search
85-
autoFocus
86-
allowClear
87-
placeholder="Search messages..."
88-
value={search}
89-
onChange={(e) => {
90-
const search = e.target.value ?? "";
91-
setSearch(search);
92-
saveSearch(search);
93-
}}
94-
/>
95-
</Card>
96-
<div className="smc-vfill">
97-
<div style={{ overflow: "auto", padding: "15px" }}>
98-
{result?.hits?.map((hit) => (
99-
<SearchResult key={hit.id} hit={hit} actions={actions} fragmentKey={fragmentKey} />
100-
))}
101-
{result?.hits == null && search?.trim() && <div>No hits</div>}
102-
</div>
103-
</div>
104-
</div>
105-
);
106-
}
107-
108-
function SearchResult({ hit, actions, fragmentKey }) {
109-
const { document } = hit;
110-
return (
111-
<div
112-
style={{
113-
cursor: "pointer",
114-
margin: "10px 0",
115-
padding: "5px",
116-
border: "1px solid #ccc",
117-
background: "#f8f8f8",
118-
borderRadius: "5px",
119-
maxHeight: "100px",
120-
overflow: "hidden",
121-
}}
122-
onClick={() => {
123-
actions.gotoFragment({ [fragmentKey]: document.id });
124-
}}
125-
>
126-
<TimeAgo style={{ float: "right", color: "#888" }} date={parseFloat(document.id)} />
7+
<>
8+
<TimeAgo
9+
style={{ float: "right", color: "#888" }}
10+
date={parseFloat(id)}
11+
/>
12712
<StaticMarkdown
128-
value={document.content}
13+
value={content}
12914
style={{ marginBottom: "-10px" /* account for <p> */ }}
13015
/>
131-
</div>
16+
</>
13217
);
13318
}
13419

135-
export const search = {
136-
type: "search",
137-
short: "Search",
138-
name: "Search",
139-
icon: "comment",
140-
commands: set(["decrease_font_size", "increase_font_size"]),
141-
component: Search,
142-
} as EditorDescription;
20+
export const search = createSearchEditor({ Preview });
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3+
* License: MS-RSL – see LICENSE.md for details
4+
*/
5+
6+
/*
7+
Full text search that is better than a simple filter.
8+
*/
9+
10+
import { useFrameContext } from "@cocalc/frontend/frame-editors/frame-tree/frame-context";
11+
import type { EditorDescription } from "@cocalc/frontend/frame-editors/frame-tree/types";
12+
import { Card, Input } from "antd";
13+
import { path_split, separate_file_extension, set } from "@cocalc/util/misc";
14+
import { useEffect, useMemo, useState } from "react";
15+
import { throttle } from "lodash";
16+
import useSearchIndex from "./use-search-index";
17+
import ShowError from "@cocalc/frontend/components/error";
18+
import { useEditorRedux } from "@cocalc/frontend/app-framework";
19+
import type { ChatState } from "@cocalc/frontend/chat/store";
20+
21+
interface Props {
22+
font_size: number;
23+
desc;
24+
Preview?;
25+
}
26+
27+
function Search({ font_size, desc, Preview }: Props) {
28+
const { project_id, path, actions, id } = useFrameContext();
29+
const useEditor = useEditorRedux<ChatState>({ project_id, path });
30+
const messages = useEditor("messages");
31+
const [indexedMessages, setIndexedMessages] = useState<any>(messages);
32+
const [search, setSearch] = useState<string>(desc.get("data-search") ?? "");
33+
const [result, setResult] = useState<any>(null);
34+
const saveSearch = useMemo(
35+
() =>
36+
throttle((search) => {
37+
if (!actions.isClosed()) {
38+
actions.set_frame_data({ id, search });
39+
}
40+
}, 250),
41+
[project_id, path],
42+
);
43+
44+
const { error, setError, index, doRefresh, fragmentKey } = useSearchIndex();
45+
46+
useEffect(() => {
47+
if (index == null) {
48+
return;
49+
}
50+
if (!search.trim()) {
51+
setResult([]);
52+
return;
53+
}
54+
(async () => {
55+
const result = await index.search({ term: search });
56+
setResult(result);
57+
})();
58+
}, [search, index]);
59+
60+
useEffect(() => {
61+
if (indexedMessages != messages) {
62+
setIndexedMessages(messages);
63+
doRefresh();
64+
}
65+
}, [messages]);
66+
67+
return (
68+
<div className="smc-vfill">
69+
<Card
70+
title={
71+
<>
72+
Search Chatroom{" "}
73+
{separate_file_extension(path_split(path).tail).name}
74+
</>
75+
}
76+
style={{ fontSize: font_size }}
77+
>
78+
<ShowError
79+
error={error}
80+
setError={setError}
81+
style={{ marginBottom: "15px" }}
82+
/>
83+
<Input.Search
84+
autoFocus
85+
allowClear
86+
placeholder="Search messages..."
87+
value={search}
88+
onChange={(e) => {
89+
const search = e.target.value ?? "";
90+
setSearch(search);
91+
saveSearch(search);
92+
}}
93+
/>
94+
</Card>
95+
<div className="smc-vfill">
96+
<div style={{ overflow: "auto", padding: "15px" }}>
97+
{result?.hits?.map((hit) => (
98+
<SearchResult
99+
key={hit.id}
100+
hit={hit}
101+
actions={actions}
102+
fragmentKey={fragmentKey}
103+
Preview={Preview}
104+
/>
105+
))}
106+
{result?.hits == null && search?.trim() && <div>No hits</div>}
107+
</div>
108+
</div>
109+
</div>
110+
);
111+
}
112+
113+
function SearchResult({ hit, actions, fragmentKey, Preview }) {
114+
const { document } = hit;
115+
return (
116+
<div
117+
style={{
118+
cursor: "pointer",
119+
margin: "10px 0",
120+
padding: "5px",
121+
border: "1px solid #ccc",
122+
background: "#f8f8f8",
123+
borderRadius: "5px",
124+
maxHeight: "100px",
125+
overflow: "hidden",
126+
}}
127+
onClick={() => {
128+
actions.gotoFragment({ [fragmentKey]: document.id });
129+
}}
130+
>
131+
{Preview != null ? (
132+
<Preview id={document.id} content={document.content} />
133+
) : (
134+
<div>{document.content}</div>
135+
)}
136+
</div>
137+
);
138+
}
139+
140+
export function createSearchEditor({
141+
Preview,
142+
}: {
143+
Preview?;
144+
}): EditorDescription {
145+
return {
146+
type: "search",
147+
short: "Search",
148+
name: "Search",
149+
icon: "comment",
150+
commands: set(["decrease_font_size", "increase_font_size"]),
151+
component: (props) => <Search {...props} Preview={Preview} />,
152+
} as EditorDescription;
153+
}

0 commit comments

Comments
 (0)