about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
Diffstat (limited to 'src/state')
-rw-r--r--src/state/index.ts3
-rw-r--r--src/state/lib/api.ts3
-rw-r--r--src/state/models/user-autocomplete-view.ts97
3 files changed, 101 insertions, 2 deletions
diff --git a/src/state/index.ts b/src/state/index.ts
index 1ff3d3b1d..fd81bc842 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -1,5 +1,6 @@
 import {autorun} from 'mobx'
 import {sessionClient as AtpApi} from '../third-party/api'
+import type {SessionServiceClient} from '../third-party/api/src/index'
 import {RootStoreModel} from './models/root-store'
 import * as libapi from './lib/api'
 import * as storage from './lib/storage'
@@ -8,7 +9,7 @@ export const IS_PROD_BUILD = true
 export const LOCAL_DEV_SERVICE = 'http://localhost:2583'
 export const STAGING_SERVICE = 'https://pds.staging.bsky.dev'
 export const PROD_SERVICE = 'https://bsky.social'
-export const DEFAULT_SERVICE = IS_PROD_BUILD ? PROD_SERVICE : LOCAL_DEV_SERVICE
+export const DEFAULT_SERVICE = PROD_SERVICE
 const ROOT_STATE_STORAGE_KEY = 'root'
 const STATE_FETCH_INTERVAL = 15e3
 
diff --git a/src/state/lib/api.ts b/src/state/lib/api.ts
index ba2fcd3bb..5f147e01f 100644
--- a/src/state/lib/api.ts
+++ b/src/state/lib/api.ts
@@ -20,6 +20,7 @@ export async function post(
   store: RootStoreModel,
   text: string,
   replyTo?: Post.PostRef,
+  knownHandles?: Set<string>,
 ) {
   let reply
   if (replyTo) {
@@ -39,7 +40,7 @@ export async function post(
       }
     }
   }
-  const entities = extractEntities(text)
+  const entities = extractEntities(text, knownHandles)
   return await store.api.app.bsky.feed.post.create(
     {did: store.me.did || ''},
     {
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)
+      }
+    })
+  }
+}