about summary refs log tree commit diff
path: root/src/state/queries
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2023-11-14 19:51:23 -0600
committerGitHub <noreply@github.com>2023-11-14 17:51:23 -0800
commitd1cb74febea9725b028dca372e1b7d7e157ad24d (patch)
tree6d08f29a709c024e01b543b179bf33b28859eb3c /src/state/queries
parentab1ce078ec16866c691eac81f7b1517bb26399c9 (diff)
downloadvoidsky-d1cb74febea9725b028dca372e1b7d7e157ad24d.tar.zst
Desktop user autocomplete search (#1906)
* Fix notification provider order, add comments

* Remove log

* Add actor typeahead handling

* Trim down desktop search styles and hooks

* Clean up moderation
Diffstat (limited to 'src/state/queries')
-rw-r--r--src/state/queries/actor-autocomplete.ts62
1 files changed, 59 insertions, 3 deletions
diff --git a/src/state/queries/actor-autocomplete.ts b/src/state/queries/actor-autocomplete.ts
index 62c4781c4..1bfa13f81 100644
--- a/src/state/queries/actor-autocomplete.ts
+++ b/src/state/queries/actor-autocomplete.ts
@@ -1,8 +1,12 @@
+import React from 'react'
 import {AppBskyActorDefs, BskyAgent} from '@atproto/api'
-import {useQuery} from '@tanstack/react-query'
-import {useSession} from '../session'
-import {useMyFollowsQuery} from './my-follows'
+import {useQuery, useQueryClient} from '@tanstack/react-query'
 import AwaitLock from 'await-lock'
+import Fuse from 'fuse.js'
+
+import {logger} from '#/logger'
+import {useSession} from '#/state/session'
+import {useMyFollowsQuery} from '#/state/queries/my-follows'
 
 export const RQKEY = (prefix: string) => ['actor-autocomplete', prefix]
 
@@ -22,6 +26,58 @@ export function useActorAutocompleteQuery(prefix: string) {
   })
 }
 
+export function useActorSearch() {
+  const queryClient = useQueryClient()
+  const {agent} = useSession()
+  const {data: follows} = useMyFollowsQuery()
+
+  const followsSearch = React.useMemo(() => {
+    if (!follows) return undefined
+
+    return new Fuse(follows, {
+      includeScore: true,
+      keys: ['displayName', 'handle'],
+    })
+  }, [follows])
+
+  return React.useCallback(
+    async ({query}: {query: string}) => {
+      let searchResults: AppBskyActorDefs.ProfileViewBasic[] = []
+
+      if (followsSearch) {
+        const results = followsSearch.search(query)
+        searchResults = results.map(({item}) => item)
+      }
+
+      try {
+        const res = await queryClient.fetchQuery({
+          // cached for 1 min
+          staleTime: 60 * 1000,
+          queryKey: ['search', query],
+          queryFn: () =>
+            agent.searchActorsTypeahead({
+              term: query,
+              limit: 8,
+            }),
+        })
+
+        if (res.data.actors) {
+          for (const actor of res.data.actors) {
+            if (!searchResults.find(item => item.handle === actor.handle)) {
+              searchResults.push(actor)
+            }
+          }
+        }
+      } catch (e) {
+        logger.error('useActorSearch: searchActorsTypeahead failed', {error: e})
+      }
+
+      return searchResults
+    },
+    [agent, followsSearch, queryClient],
+  )
+}
+
 export class ActorAutocomplete {
   // state
   isLoading = false