@rpcbase/client 0.216.0 → 0.218.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,68 @@
1
1
  import {useState} from "react"
2
2
 
3
3
 
4
+ const POLICY_EFFECT = [
5
+ {
6
+ name: "Grant",
7
+ key: "grant",
8
+ },
9
+ {
10
+ name: "Deny",
11
+ key: "deny",
12
+ },
13
+ ]
14
+
4
15
  const TARGET_TYPES = [
5
16
  {
6
17
  name: "Collection:",
7
- key: "collection",
8
- description: "Applies to all documents within the collection.",
18
+ key: "collections",
19
+ description: "Applies to all documents within the collection",
20
+ },
21
+ {
22
+ name: "Documents:",
23
+ key: "documents",
24
+ description: "Applies only to certain specific documents",
25
+ },
26
+ {
27
+ name: "Field:",
28
+ key: "fields",
29
+ description: "Applies only specific fields of documents",
30
+ },
31
+ ]
32
+
33
+ const OPERATIONS = [
34
+ {
35
+ name: "Create",
36
+ key: "create",
37
+ },
38
+ {
39
+ name: "Read",
40
+ key: "read",
41
+ },
42
+ {
43
+ name: "Update",
44
+ key: "update",
45
+ },
46
+ {
47
+ name: "Delete",
48
+ key: "delete",
49
+ },
50
+ {
51
+ name: "All of the above",
52
+ key: "all",
53
+ },
54
+ ]
55
+
56
+
57
+ //
58
+ const APPLIES_TO = [
59
+ {
60
+ name: "Users",
61
+ key: "users",
9
62
  },
10
63
  {
11
- name: "Document:",
12
- key: "document",
13
- description: "Applies only to certain specific documents.",
64
+ name: "Groups",
65
+ key: "groups",
14
66
  },
15
67
  ]
16
68
 
@@ -24,7 +76,31 @@ export const PolicyEditor = () => {
24
76
  return (
25
77
  <div>
26
78
  <h6>Policy Editor</h6>
27
- <br />
79
+ <hr />
80
+
81
+ <h6>Effect:</h6>
82
+ <div className="mb-3" style={{maxWidth: 300}}>
83
+ {POLICY_EFFECT.map((type) => (
84
+ <div key={type.key} className="d-flex flex-row mb-1">
85
+ <input
86
+ className="form-check-input"
87
+ type="radio"
88
+ name="targetTypeOptions"
89
+ id={type.key}
90
+ value={type.key}
91
+ checked={targetType === type.key}
92
+ onChange={onChangeTargetType}
93
+ />
94
+ <label
95
+ className="form-check-label cursor-pointer ps-2"
96
+ htmlFor={type.key}
97
+ >
98
+ <b>{type.name}</b>
99
+ </label>
100
+ </div>
101
+ ))}
102
+ </div>
103
+
28
104
  <div className="d-flex flex-row">
29
105
  <div className="me-2">
30
106
  <h6>Target Type:</h6>
@@ -58,11 +134,30 @@ export const PolicyEditor = () => {
58
134
  </div>
59
135
  </div>
60
136
  </div>
61
- grant / deny
62
- <br />
63
- Scope: document, field
64
- <br />
65
- operation: create read write delete
137
+
138
+ <h6 className="mt-3">Operation:</h6>
139
+ <div className="" style={{maxWidth: 300}}>
140
+ {OPERATIONS.map((type) => (
141
+ <div key={type.key} className="d-flex flex-row mb-1">
142
+ <input
143
+ className="form-check-input"
144
+ type="radio"
145
+ name="targetTypeOptions"
146
+ id={type.key}
147
+ value={type.key}
148
+ checked={targetType === type.key}
149
+ onChange={onChangeTargetType}
150
+ />
151
+ <label
152
+ className="form-check-label cursor-pointer ps-2"
153
+ htmlFor={type.key}
154
+ >
155
+ <b>{type.name}</b>
156
+ </label>
157
+ </div>
158
+ ))}
159
+ </div>
160
+
66
161
  <br />
67
162
  to attributes / conditions add support for expiry date
68
163
  </div>
package/form/Input.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import React from "react"
2
2
  // import {NestedKeyOf} from "@rpcbase/client/types"
3
+ import _snakeCase from "lodash/snakeCase"
3
4
  import _get from "lodash/get"
4
5
  import {useFormContext} from "./hook-form"
5
6
 
@@ -33,7 +34,7 @@ export const Input = <T,>({
33
34
  }: InputProps<T> & React.InputHTMLAttributes<HTMLInputElement>) => {
34
35
  const form = useFormContext()
35
36
 
36
- const id = idProp || `input-${field}`
37
+ const id = idProp || _snakeCase(`input_${field}`)
37
38
 
38
39
  const error = _get(form.formState.errors, field)
39
40
 
package/hashState.js CHANGED
@@ -108,7 +108,7 @@ export const HashStateProvider = ({children}) => {
108
108
 
109
109
  useEffect(() => {
110
110
  apply_hash_state(hashState)
111
- window.__PRIVATE_HASH_STATE_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = hashState
111
+ window.__PRIVATE_HASH_STATE_DO_NOT_USE__ = hashState
112
112
  }, [hashState])
113
113
 
114
114
  const serializeHashState = useCallback((payload) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/client",
3
- "version": "0.216.0",
3
+ "version": "0.218.0",
4
4
  "scripts": {
5
5
  "build": "../../node_modules/.bin/wireit",
6
6
  "test": "../../node_modules/.bin/wireit"
@@ -18,6 +18,10 @@ export const SearchHistory = ({setSearchInput}) => {
18
18
  setSearchInput(item.query)
19
19
  }
20
20
 
21
+ if (!searchHistory.length > 0) {
22
+ return null
23
+ }
24
+
21
25
  return (
22
26
  <>
23
27
  <div className="search-history-header ps-3 pb-2 mt-2 fw-bold border-bottom">
@@ -6,13 +6,14 @@ import {getUid} from "@rpcbase/client/auth/getUid"
6
6
 
7
7
  import stripDiacritics from "react-bootstrap-typeahead/cjs/utils/stripDiacritics"
8
8
 
9
+
9
10
  const HISTORY_MAX_RESULTS = 10
10
11
 
11
12
  export const useSearchHistory = () => {
12
13
  const user_id = getUid()
13
14
 
14
15
  const [history, setHistory] = useState([])
15
- const historyQuery = useQuery("PersistedEvent", {_owners: {$in: [user_id]}, type: "search_query"})
16
+ const historyQuery = useQuery("SearchHistory", {_owners: {$in: [user_id]}})
16
17
 
17
18
  // Initial load history
18
19
  useEffect(() => {
@@ -0,0 +1,90 @@
1
+ import _pick from "lodash/pick"
2
+ import _snakeCase from "lodash/snakeCase"
3
+
4
+ import {useHashState} from "../../../hashState"
5
+
6
+ import {useActiveListItemIndex} from "../../helpers/useActiveListItemIndex"
7
+ import {useScrollSelectorIntoView} from "../../helpers/useScrollSelectorIntoView"
8
+
9
+ // import save_search from "rpc!server/search-indexer/save_search"
10
+
11
+ export const SearchResults = ({
12
+ id = "search-results",
13
+ query,
14
+ results,
15
+ searchContext,
16
+ onCompleteSearch,
17
+ children,
18
+ renderResultItem,
19
+ }) => {
20
+ const {serializeHashState} = useHashState()
21
+
22
+ const getItemId = (i) => `${id}-result-${i}`
23
+
24
+ const getLink = (item) =>{
25
+ return `#${_snakeCase(item)}`
26
+ }
27
+
28
+ const onSelectItem = (selectedIndex) => {
29
+ // console.log("ON SELEC", selectedIndex)
30
+ serializeHashState({link: getLink(results[selectedIndex])})
31
+ // if (window)
32
+
33
+ onCompleteSearch()
34
+ // go to selected item
35
+ // const targetItem = items[selectedIndex]
36
+ // applyTargetItem(targetItem)
37
+ }
38
+
39
+ const {activeItemIndex} = useActiveListItemIndex({items: results, onSelectItem})
40
+
41
+ useScrollSelectorIntoView(`#${getItemId(activeItemIndex)}`)
42
+
43
+ return (
44
+ <div>
45
+ <div className="list-group list-group-flush">
46
+ {results.map((item: any, index: number) => {
47
+
48
+ const key = getItemId(index)
49
+
50
+ const onClick = () => {
51
+ onSelectItem(index)
52
+ }
53
+
54
+ return (
55
+ <a
56
+ id={key}
57
+ key={key}
58
+ className={cx(
59
+ "list-group-item list-group-item-action d-flex justify-content-between align-items-start",
60
+ {active: activeItemIndex === index},
61
+ )}
62
+ href={getLink(item)}
63
+ onClick={onClick}
64
+ >
65
+ <div className="ms-2 me-auto">
66
+ {renderResultItem(item, index)}
67
+ {/* {Object.keys(item.highlight).map((highlightKey, j) => (
68
+ <div
69
+ key={`highlight-${j}`}
70
+ className="item-highlight"
71
+ dangerouslySetInnerHTML={{__html: item.highlight[highlightKey][0]}}
72
+ />
73
+ ))} */}
74
+ <small className="fst-italic">{"displaySub"}</small>
75
+ </div>
76
+ {/* <span className="badge text-bg-light rounded-pill">
77
+ {item.fuzzy_score?.toFixed(2)}
78
+ </span> */}
79
+ {/* <span className="badge bg-primary rounded-pill">{item._score.toFixed(2)}</span> */}
80
+ </a>
81
+ )
82
+
83
+ })}
84
+
85
+ {/* children can be components like search history, etc */}
86
+ {children}
87
+ </div>
88
+ </div>
89
+ )
90
+ }
@@ -1,5 +1,5 @@
1
1
  import assert from "assert"
2
- import {useEffect, useState, useContext, useRef, useCallback} from "react"
2
+ import {ReactNode, useEffect, useState, useRef, useCallback} from "react"
3
3
  import {Typeahead} from "react-bootstrap-typeahead"
4
4
  import Overlay from "react-bootstrap/Overlay"
5
5
  import _throttle from "lodash/throttle"
@@ -27,19 +27,22 @@ const MIN_SEARCH_LENGTH = 2
27
27
  const PLACEHOLDER = "Search..."
28
28
 
29
29
  export const Search = ({
30
+ id = "search-input",
30
31
  minLength = MIN_SEARCH_LENGTH,
31
32
  onSearch,
33
+ renderResultItem,
32
34
  }: {
35
+ id?: string,
33
36
  minLength?: number
34
- onSearch: (queryStr: string) => Array<any>
37
+ onSearch: (queryStr: string) => Promise<Array<any>>
38
+ renderResultItem: (item: any, index: number) => ReactNode
35
39
  }) => {
36
40
  // const envContext = useEnvContext()
37
41
  // const itemContext = useContext(ItemContext)
38
-
39
42
  const typeaheadRef = useRef()
40
43
  const wrapperRef = useRef()
41
44
 
42
- const [results, setResults] = useState([])
45
+ const [results, setResults] = useState<Array<any>>([])
43
46
  const [isLoading, setIsLoading] = useState(false)
44
47
  const [isFocused, setIsFocused] = useState(false)
45
48
  const [showResults, setShowResults] = useState(false)
@@ -116,17 +119,9 @@ export const Search = ({
116
119
  setIsLoading(true)
117
120
  }, 100)
118
121
 
119
- // const res = await search_anything({
120
- // group_id: envContext.groupId,
121
- // str,
122
- // })
123
-
124
- const results = await onSearch(queryStr)
125
-
126
- // assert(res.status === "ok")
127
- // console.log("search res", res)
122
+ const searchResults = await onSearch(queryStr)
128
123
 
129
- setResults(results)
124
+ setResults(searchResults)
130
125
  clearTimeout(tm)
131
126
  setIsLoading(false)
132
127
  },
@@ -216,16 +211,16 @@ export const Search = ({
216
211
  return (
217
212
  <>
218
213
  <div
219
- id="search-anything-wrapper"
214
+ id={id}
220
215
  ref={wrapperRef}
221
216
  className="d-flex mx-auto w-100"
222
217
  // style={{height: 32, position: "relative", maxWidth: 804}}
223
218
  style={{height: 32, position: "relative"}}
224
219
  >
225
220
  <Typeahead
226
- id="search-anything"
221
+ id={`${id}-typeahead`}
227
222
  ref={typeaheadRef}
228
- className={cx("search-anything-typeahead w-100", {"is-focused": isFocused})}
223
+ className={cx("search-typeahead w-100", {"is-focused": isFocused})}
229
224
  placeholder={PLACEHOLDER}
230
225
  inputProps={{
231
226
  placeholder: PLACEHOLDER,
@@ -275,7 +270,7 @@ export const Search = ({
275
270
  >
276
271
  {({style, ...overlayProps}) => (
277
272
  <div
278
- id="search-anything-overlay"
273
+ id={`${id}-overlay`}
279
274
  {...overlayProps}
280
275
  className="shadow-lg"
281
276
  style={{
@@ -286,10 +281,12 @@ export const Search = ({
286
281
  }}
287
282
  >
288
283
  <SearchResults
284
+ id={id}
289
285
  query={currentInputValue.trim()}
290
286
  results={results}
291
287
  onCompleteSearch={onCompleteSearch}
292
288
  searchContext={selected[0]}
289
+ renderResultItem={renderResultItem}
293
290
  >
294
291
  <SearchHistory setSearchInput={setSearchInput} />
295
292
  </SearchResults>
@@ -1,9 +1,12 @@
1
1
  import {useState, useEffect} from "react"
2
2
 
3
+
3
4
  export const useActiveListItemIndex = ({items, activeItem, onSelectItem}) => {
5
+
4
6
  const [activeItemIndex, setActiveItemIndex] = useState(() => {
5
7
  let initialIndex = items.findIndex((item) => item.obj?._id === activeItem?._id)
6
- if (!initialIndex) initialIndex = 0
8
+ if (initialIndex < 0) initialIndex = 0
9
+ console.log("INITIAL INDEX", initialIndex)
7
10
  return initialIndex
8
11
  })
9
12
 
@@ -11,6 +11,7 @@ type ContentViewContextType = {
11
11
  contentViewHeight: number;
12
12
  sidebarWidth: number;
13
13
  setSidebarWidth: Dispatch<SetStateAction<number>>;
14
+ headerHeight: number;
14
15
  };
15
16
 
16
17
  export const ContentViewContext = createContext<ContentViewContextType>(
@@ -13,9 +13,11 @@ const SIDEVIEW_WIDTH = "sideview_width"
13
13
 
14
14
  export const ContentView = memo(
15
15
  ({
16
+ headerHeight = 45,
16
17
  children,
17
18
  storageKeyPrefix: _storageKeyPrefix,
18
19
  }: {
20
+ headerHeight?: number;
19
21
  children: ReactNode;
20
22
  storageKeyPrefix?: string;
21
23
  }) => {
@@ -76,6 +78,7 @@ export const ContentView = memo(
76
78
  contentViewHeight,
77
79
  sidebarWidth,
78
80
  setSidebarWidth,
81
+ headerHeight,
79
82
  }}
80
83
  >
81
84
  <div
@@ -85,15 +88,15 @@ export const ContentView = memo(
85
88
  flexDirection: "row",
86
89
  }}
87
90
  >
88
- <div id="sidebar-container" />
91
+ <div id="sidebar-container" style={{top: headerHeight}} />
89
92
 
90
93
  <div
91
94
  ref={contentViewRef}
92
95
  key={`content-wrapper-${"content_view_"}`}
93
96
  style={{
94
97
  width: `calc(100vw - ${contentWidthOffset}px)`,
95
- height: "calc(100vh - 45px)",
96
- marginTop: 45,
98
+ height: `calc(100vh - ${headerHeight}px)`,
99
+ marginTop: headerHeight,
97
100
  overflowY: "scroll",
98
101
  marginLeft: sidebarWidth,
99
102
  }}
@@ -10,7 +10,6 @@ $img-border-radius: 3px;
10
10
  #header-container {
11
11
  border-bottom: 1px solid $black;
12
12
  background-color: $black !important;
13
- height: 45px;
14
13
 
15
14
  .nav-link {
16
15
  color: $gray-200;
@@ -28,7 +28,7 @@ import {ReactNode} from "react"
28
28
  // import EnvSettingsDropdown from "./components/EnvSettingsDropdown"
29
29
 
30
30
  import "./header.scss"
31
-
31
+ import {useContentViewContext} from "../ContentView/ContentViewContext"
32
32
 
33
33
  // const DefaultHeader = () => {
34
34
  // return (
@@ -136,14 +136,18 @@ import "./header.scss"
136
136
  // )
137
137
  // }
138
138
 
139
- export const HeaderContainer = ({children}: {children: ReactNode}) => {
139
+ export const HeaderContainer = ({children}: { children: ReactNode }) => {
140
+ const contentViewContext = useContentViewContext()
140
141
 
141
142
  if (!children) return null
142
143
 
143
144
  return (
144
145
  <nav
145
146
  id="header-container"
146
- className={"d-flex align-items-center fixed-top bg-dark flex-md-nowrap shadow p-0 text-light"}
147
+ className={
148
+ "d-flex align-items-center fixed-top bg-dark flex-md-nowrap shadow p-0 text-light"
149
+ }
150
+ style={{height: contentViewContext.headerHeight}}
147
151
  >
148
152
  {children}
149
153
  </nav>
@@ -2,7 +2,6 @@
2
2
 
3
3
  #sidebar-container {
4
4
  position: fixed;
5
- top: 45px;
6
5
  left: 0;
7
6
  max-height: calc(100vh - 45px);
8
7
 
@@ -1,6 +0,0 @@
1
-
2
- const getActions = (pathname) => {
3
- return []
4
- }
5
-
6
- export default getActions
@@ -1,188 +0,0 @@
1
- import assert from "assert"
2
- import _pick from "lodash/pick"
3
- import fuzzysort from "fuzzysort"
4
-
5
- import page from "../../../page"
6
- // import {withHashState} from "@rpcbase/client/hashState"
7
-
8
- // import COLLECTIONS from "config/collections"
9
- // import {useEnvContext} from "helpers/EnvContext"
10
- import {useActiveListItemIndex} from "../../helpers/useActiveListItemIndex"
11
- import {useScrollSelectorIntoView} from "../../helpers/useScrollSelectorIntoView"
12
-
13
- import useCombinedResultsActions from "./useCombinedResultsActions"
14
-
15
- // import save_search from "rpc!server/search-indexer/save_search"
16
- const save_search = async() => null
17
-
18
-
19
- const resolveTargetUrl = (encodeHashLink, item) => {
20
- let targetUrl
21
-
22
- // Message
23
- if (item._source.type === "message") {
24
- const pageUrl = "/channel"
25
- const channelId = item._source.channel_id
26
- const convId = item._source.conversation_id
27
- targetUrl = `${pageUrl}/${channelId}#${encodeHashLink({
28
- activeConversationId: convId, // conversation
29
- scrollToMessageId: item._id, // highlighted message
30
- })}`
31
- }
32
- // Other items
33
- else {
34
- const pageUrl = COLLECTIONS[item.meta.collection_name].base_url
35
- targetUrl = `${pageUrl}/${item.meta._id}#${encodeHashLink({
36
- highlightId: item._id,
37
- })}`
38
- }
39
-
40
- return targetUrl
41
- }
42
-
43
- const getKey = (i) => `search-anything-item-${i}`
44
-
45
- export const SearchResults = ({
46
- query,
47
- results,
48
- searchContext,
49
- onCompleteSearch,
50
- children,
51
- }) => {
52
- // const envContext = useEnvContext()
53
-
54
- const items = useCombinedResultsActions({query, results})
55
-
56
- // runs when a search result is selected by pressing enter
57
- const applyTargetItem = async(item) => {
58
- // user pressed enter with no results / nothing selected
59
- if (!item) {
60
- console.log("user pressed enter but there are no results")
61
- return
62
- }
63
-
64
- if (item.type === "action") {
65
- if (item.hashState) {
66
- serializeHashState(item.hashState)
67
- } else {
68
- console.error("unknown action", item)
69
- throw new Error("unknown action")
70
- }
71
- } else {
72
- const targetUrl = resolveTargetUrl(encodeHashLink, item)
73
-
74
- const res = await save_search({
75
- group_id: envContext.groupId,
76
- data: {
77
- query,
78
- search_context: _pick(searchContext, ["id", "col"]),
79
- result_url: targetUrl,
80
- },
81
- })
82
- assert(res.status === "ok")
83
-
84
- page(targetUrl)
85
- }
86
- }
87
-
88
- const onSelectItem = (selectedIndex) => {
89
- onCompleteSearch()
90
- // go to selected item
91
- const targetItem = items[selectedIndex]
92
- applyTargetItem(targetItem)
93
- }
94
-
95
- const {activeItemIndex} = useActiveListItemIndex({items, onSelectItem})
96
-
97
- useScrollSelectorIntoView(`#${getKey(activeItemIndex)}`)
98
-
99
- return (
100
- <div>
101
- <div className="list-group list-group-flush">
102
- {items.map((item, i) => {
103
- const key = getKey(i)
104
- // Render action
105
- if (item.type === "action") {
106
- let highlighted = fuzzysort.highlight(fuzzysort.single(query, item.key), "<b>", "</b>")
107
-
108
- if (!highlighted) highlighted = item.key
109
-
110
- const onClick = () => {
111
- onSelectItem(i)
112
- }
113
-
114
- return (
115
- <a
116
- id={key}
117
- key={key}
118
- className={cx(
119
- "list-group-item list-group-item-action d-flex justify-content-between align-items-start",
120
- {active: activeItemIndex === i},
121
- )}
122
- style={{cursor: "pointer"}}
123
- onClick={onClick}
124
- >
125
- <div className="ms-2 me-auto">
126
- <img
127
- style={{width: 20, height: 20, display: "inline"}}
128
- className="d-inline-flex me-2"
129
- src={`/static/icons/${item.icon}.svg`}
130
- />
131
- <div
132
- className="item-highlight"
133
- style={{display: "inline"}}
134
- dangerouslySetInnerHTML={{__html: highlighted}}
135
- />
136
- </div>
137
- <span className="badge text-bg-light rounded-pill">
138
- {item.fuzzy_score?.toFixed(2)}
139
- </span>
140
- </a>
141
- )
142
- }
143
- // Render search result
144
- else {
145
- const onClick = () => {
146
- onCompleteSearch()
147
- }
148
-
149
- const targetUrl = resolveTargetUrl(encodeHashLink, item)
150
- // TODO: when message, displaySub should be the name of the recipient / sender
151
- const displaySub = item._source.type === "message" ? "message" : item.meta.name
152
-
153
- return (
154
- <a
155
- id={key}
156
- key={key}
157
- className={cx(
158
- "list-group-item list-group-item-action d-flex justify-content-between align-items-start",
159
- {active: activeItemIndex === i},
160
- )}
161
- href={targetUrl}
162
- onClick={onClick}
163
- >
164
- <div className="ms-2 me-auto">
165
- {Object.keys(item.highlight).map((highlightKey, j) => (
166
- <div
167
- key={`highlight-${j}`}
168
- className="item-highlight"
169
- dangerouslySetInnerHTML={{__html: item.highlight[highlightKey][0]}}
170
- />
171
- ))}
172
- <small className="fst-italic">{displaySub}</small>
173
- </div>
174
- <span className="badge text-bg-light rounded-pill">
175
- {item.fuzzy_score?.toFixed(2)}
176
- </span>
177
- <span className="badge bg-primary rounded-pill">{item._score.toFixed(2)}</span>
178
- </a>
179
- )
180
- }
181
- })}
182
-
183
- {/* children can be components like search history, etc */}
184
- {children}
185
- </div>
186
- </div>
187
- )
188
- }
@@ -1,59 +0,0 @@
1
- /* @flow */
2
- import {useMemo} from "react"
3
- import useLocation from "react-use/lib/useLocation"
4
- import _isNil from "lodash/isNil"
5
- import fuzzysort from "fuzzysort"
6
-
7
- import getActions from "./getActions"
8
-
9
- // uses fuzzysort to match and sort results https://github.com/farzher/fuzzysort
10
- const getResultsWithActions = (query, results, actions) => {
11
- // we "fuzzy match sort" all actions and results (with both our fuzzy + elastic key)
12
- const rawItems = [...actions, ...results].map((item) => {
13
- let fuzzy_score
14
-
15
- // action item
16
- if (item.key) {
17
- fuzzy_score = fuzzysort.single(query, item.key)?.score
18
- }
19
- // search result
20
- else {
21
- // for each highlight compute score, take the highest one
22
- const scores = Object
23
- // TODO: why do some elastic search results not have a highlight?
24
- .keys(item?.highlight || {})
25
- .map((key) => fuzzysort.single(query, item.highlight[key][0])?.score)
26
- .filter((s) => !_isNil(s))
27
- .sort((a, b) => b - a)
28
-
29
- fuzzy_score = scores[0]
30
- }
31
-
32
- return {
33
- ...item,
34
- fuzzy_score,
35
- }
36
- })
37
-
38
- const sortedItems = rawItems
39
- .filter((item) => !_isNil(item.fuzzy_score))
40
- .sort((a, b) => b.fuzzy_score - a.fuzzy_score)
41
-
42
- return sortedItems
43
- }
44
-
45
- const useCombinedResultsActions = ({query, results}) => {
46
- const location = useLocation()
47
-
48
- const items = useMemo(() => {
49
- const actions = getActions(location.pathname)
50
-
51
- const newItems = getResultsWithActions(query, results, actions)
52
-
53
- return newItems
54
- }, [results, location.pathname])
55
-
56
- return items
57
- }
58
-
59
- export default useCombinedResultsActions