about summary refs log tree commit diff
path: root/src/state/models/user-autocomplete-view.ts
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2022-11-17 14:35:12 -0600
committerPaul Frazee <pfrazee@gmail.com>2022-11-17 14:35:12 -0600
commit2b98714548d585ff14dd09252233144f48b5f4b7 (patch)
treef200f39732aad5f7da3b554f02d5f78ce96bee12 /src/state/models/user-autocomplete-view.ts
parent859087f21d148d52d707b0057458e7dd2cbbea0a (diff)
downloadvoidsky-2b98714548d585ff14dd09252233144f48b5f4b7.tar.zst
Add live search to autocomplete and only highlight known handles
Diffstat (limited to 'src/state/models/user-autocomplete-view.ts')
-rw-r--r--src/state/models/user-autocomplete-view.ts97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/state/models/user-autocomplete-view.ts b/src/state/models/user-autocomplete-view.ts
new file mode 100644
index 000000000..3d53e5db7
--- /dev/null
+++ b/src/state/models/user-autocomplete-view.ts
@@ -0,0 +1,97 @@
+import {makeAutoObservable, runInAction} from 'mobx'
+import * as GetFollows from '../../third-party/api/src/client/types/app/bsky/graph/getFollows'
+import * as SearchTypeahead from '../../third-party/api/src/client/types/app/bsky/actor/searchTypeahead'
+import {RootStoreModel} from './root-store'
+
+export class UserAutocompleteViewModel {
+  // state
+  isLoading = false
+  isActive = false
+  prefix = ''
+  _searchPromise: Promise<any> | undefined
+
+  // data
+  follows: GetFollows.OutputSchema['follows'] = []
+  searchRes: SearchTypeahead.OutputSchema['users'] = []
+  knownHandles: Set<string> = new Set()
+
+  constructor(public rootStore: RootStoreModel) {
+    makeAutoObservable(
+      this,
+      {
+        rootStore: false,
+        knownHandles: false,
+      },
+      {autoBind: true},
+    )
+  }
+
+  get suggestions() {
+    if (!this.isActive) {
+      return []
+    }
+    if (this.prefix) {
+      return this.searchRes.map(user => ({
+        handle: user.handle,
+        displayName: user.displayName,
+      }))
+    }
+    return this.follows.map(follow => ({
+      handle: follow.handle,
+      displayName: follow.displayName,
+    }))
+  }
+
+  // public api
+  // =
+
+  async setup() {
+    await this._getFollows()
+  }
+
+  setActive(v: boolean) {
+    this.isActive = v
+  }
+
+  async setPrefix(prefix: string) {
+    const origPrefix = prefix
+    this.prefix = prefix.trim()
+    if (this.prefix) {
+      await this._searchPromise
+      if (this.prefix !== origPrefix) {
+        return // another prefix was set before we got our chance
+      }
+      this._searchPromise = this._search()
+    } else {
+      this.searchRes = []
+    }
+  }
+
+  // internal
+  // =
+
+  private async _getFollows() {
+    const res = await this.rootStore.api.app.bsky.graph.getFollows({
+      user: this.rootStore.me.did || '',
+    })
+    runInAction(() => {
+      this.follows = res.data.follows
+      for (const f of this.follows) {
+        this.knownHandles.add(f.handle)
+      }
+    })
+  }
+
+  private async _search() {
+    const res = await this.rootStore.api.app.bsky.actor.searchTypeahead({
+      term: this.prefix,
+      limit: 8,
+    })
+    runInAction(() => {
+      this.searchRes = res.data.users
+      for (const u of this.searchRes) {
+        this.knownHandles.add(u.handle)
+      }
+    })
+  }
+}