about summary refs log tree commit diff
path: root/src/state/models/discovery
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/models/discovery')
-rw-r--r--src/state/models/discovery/foafs.ts36
-rw-r--r--src/state/models/discovery/onboarding.ts1
-rw-r--r--src/state/models/discovery/suggested-actors.ts4
-rw-r--r--src/state/models/discovery/user-autocomplete.ts115
4 files changed, 84 insertions, 72 deletions
diff --git a/src/state/models/discovery/foafs.ts b/src/state/models/discovery/foafs.ts
index 580145f65..4a647dcfe 100644
--- a/src/state/models/discovery/foafs.ts
+++ b/src/state/models/discovery/foafs.ts
@@ -1,8 +1,4 @@
-import {
-  AppBskyActorDefs,
-  AppBskyGraphGetFollows as GetFollows,
-  moderateProfile,
-} from '@atproto/api'
+import {AppBskyActorDefs} from '@atproto/api'
 import {makeAutoObservable, runInAction} from 'mobx'
 import sampleSize from 'lodash.samplesize'
 import {bundleAsync} from 'lib/async/bundle'
@@ -43,35 +39,13 @@ export class FoafsModel {
     try {
       this.isLoading = true
 
-      // fetch & hydrate up to 1000 follows
-      {
-        let cursor
-        for (let i = 0; i < 10; i++) {
-          const res: GetFollows.Response =
-            await this.rootStore.agent.getFollows({
-              actor: this.rootStore.me.did,
-              cursor,
-              limit: 100,
-            })
-          res.data.follows = res.data.follows.filter(
-            profile =>
-              !moderateProfile(
-                profile,
-                this.rootStore.preferences.moderationOpts,
-              ).account.filter,
-          )
-          this.rootStore.me.follows.hydrateProfiles(res.data.follows)
-          if (!res.data.cursor) {
-            break
-          }
-          cursor = res.data.cursor
-        }
-      }
+      // fetch some of the user's follows
+      await this.rootStore.me.follows.syncIfNeeded()
 
       // grab 10 of the users followed by the user
       runInAction(() => {
         this.sources = sampleSize(
-          Object.keys(this.rootStore.me.follows.followDidToRecordMap),
+          Object.keys(this.rootStore.me.follows.byDid),
           10,
         )
       })
@@ -100,7 +74,7 @@ export class FoafsModel {
       for (let i = 0; i < results.length; i++) {
         const res = results[i]
         if (res.status === 'fulfilled') {
-          this.rootStore.me.follows.hydrateProfiles(res.value.data.follows)
+          this.rootStore.me.follows.hydrateMany(res.value.data.follows)
         }
         const profile = profiles.data.profiles[i]
         const source = this.sources[i]
diff --git a/src/state/models/discovery/onboarding.ts b/src/state/models/discovery/onboarding.ts
index 8ad321ed9..3638e7f0d 100644
--- a/src/state/models/discovery/onboarding.ts
+++ b/src/state/models/discovery/onboarding.ts
@@ -81,6 +81,7 @@ export class OnboardingModel {
   }
 
   finish() {
+    this.rootStore.me.mainFeed.refresh() // load the selected content
     this.step = 'Home'
     track('Onboarding:Complete')
   }
diff --git a/src/state/models/discovery/suggested-actors.ts b/src/state/models/discovery/suggested-actors.ts
index afa5e74e3..d270267ee 100644
--- a/src/state/models/discovery/suggested-actors.ts
+++ b/src/state/models/discovery/suggested-actors.ts
@@ -76,7 +76,7 @@ export class SuggestedActorsModel {
           !moderateProfile(actor, this.rootStore.preferences.moderationOpts)
             .account.filter,
       )
-      this.rootStore.me.follows.hydrateProfiles(actors)
+      this.rootStore.me.follows.hydrateMany(actors)
 
       runInAction(() => {
         if (replace) {
@@ -118,7 +118,7 @@ export class SuggestedActorsModel {
         actor: actor,
       })
     const {suggestions: moreSuggestions} = res.data
-    this.rootStore.me.follows.hydrateProfiles(moreSuggestions)
+    this.rootStore.me.follows.hydrateMany(moreSuggestions)
     // dedupe
     const toInsert = moreSuggestions.filter(
       s => !this.suggestions.find(s2 => s2.did === s.did),
diff --git a/src/state/models/discovery/user-autocomplete.ts b/src/state/models/discovery/user-autocomplete.ts
index 461073e45..25ce859d2 100644
--- a/src/state/models/discovery/user-autocomplete.ts
+++ b/src/state/models/discovery/user-autocomplete.ts
@@ -4,6 +4,8 @@ import AwaitLock from 'await-lock'
 import {RootStoreModel} from '../root-store'
 import {isInvalidHandle} from 'lib/strings/handles'
 
+type ProfileViewBasic = AppBskyActorDefs.ProfileViewBasic
+
 export class UserAutocompleteModel {
   // state
   isLoading = false
@@ -12,9 +14,8 @@ export class UserAutocompleteModel {
   lock = new AwaitLock()
 
   // data
-  follows: AppBskyActorDefs.ProfileViewBasic[] = []
-  searchRes: AppBskyActorDefs.ProfileViewBasic[] = []
   knownHandles: Set<string> = new Set()
+  _suggestions: ProfileViewBasic[] = []
 
   constructor(public rootStore: RootStoreModel) {
     makeAutoObservable(
@@ -27,29 +28,35 @@ export class UserAutocompleteModel {
     )
   }
 
-  get suggestions() {
+  get follows(): ProfileViewBasic[] {
+    return Object.values(this.rootStore.me.follows.byDid).map(item => ({
+      did: item.did,
+      handle: item.handle,
+      displayName: item.displayName,
+      avatar: item.avatar,
+    }))
+  }
+
+  get suggestions(): ProfileViewBasic[] {
     if (!this.isActive) {
       return []
     }
-    if (this.prefix) {
-      return this.searchRes.map(user => ({
-        handle: user.handle,
-        displayName: user.displayName,
-        avatar: user.avatar,
-      }))
-    }
-    return this.follows.map(follow => ({
-      handle: follow.handle,
-      displayName: follow.displayName,
-      avatar: follow.avatar,
-    }))
+    return this._suggestions
   }
 
   // public api
   // =
 
   async setup() {
-    await this._getFollows()
+    await this.rootStore.me.follows.syncIfNeeded()
+    runInAction(() => {
+      for (const did in this.rootStore.me.follows.byDid) {
+        const info = this.rootStore.me.follows.byDid[did]
+        if (!isInvalidHandle(info.handle)) {
+          this.knownHandles.add(info.handle)
+        }
+      }
+    })
   }
 
   setActive(v: boolean) {
@@ -57,7 +64,7 @@ export class UserAutocompleteModel {
   }
 
   async setPrefix(prefix: string) {
-    const origPrefix = prefix.trim()
+    const origPrefix = prefix.trim().toLocaleLowerCase()
     this.prefix = origPrefix
     await this.lock.acquireAsync()
     try {
@@ -65,9 +72,27 @@ export class UserAutocompleteModel {
         if (this.prefix !== origPrefix) {
           return // another prefix was set before we got our chance
         }
-        await this._search()
+
+        // reset to follow results
+        this._computeSuggestions([])
+
+        // ask backend
+        const res = await this.rootStore.agent.searchActorsTypeahead({
+          term: this.prefix,
+          limit: 8,
+        })
+        this._computeSuggestions(res.data.actors)
+
+        // update known handles
+        runInAction(() => {
+          for (const u of res.data.actors) {
+            this.knownHandles.add(u.handle)
+          }
+        })
       } else {
-        this.searchRes = []
+        runInAction(() => {
+          this._computeSuggestions([])
+        })
       }
     } finally {
       this.lock.release()
@@ -77,28 +102,40 @@ export class UserAutocompleteModel {
   // internal
   // =
 
-  async _getFollows() {
-    const res = await this.rootStore.agent.getFollows({
-      actor: this.rootStore.me.did || '',
-    })
-    runInAction(() => {
-      this.follows = res.data.follows.filter(f => !isInvalidHandle(f.handle))
-      for (const f of this.follows) {
-        this.knownHandles.add(f.handle)
+  _computeSuggestions(searchRes: AppBskyActorDefs.ProfileViewBasic[] = []) {
+    if (this.prefix) {
+      const items: ProfileViewBasic[] = []
+      for (const item of this.follows) {
+        if (prefixMatch(this.prefix, item)) {
+          items.push(item)
+        }
+        if (items.length >= 8) {
+          break
+        }
       }
-    })
+      for (const item of searchRes) {
+        if (!items.find(item2 => item2.handle === item.handle)) {
+          items.push({
+            did: item.did,
+            handle: item.handle,
+            displayName: item.displayName,
+            avatar: item.avatar,
+          })
+        }
+      }
+      this._suggestions = items
+    } else {
+      this._suggestions = this.follows
+    }
   }
+}
 
-  async _search() {
-    const res = await this.rootStore.agent.searchActorsTypeahead({
-      term: this.prefix,
-      limit: 8,
-    })
-    runInAction(() => {
-      this.searchRes = res.data.actors
-      for (const u of this.searchRes) {
-        this.knownHandles.add(u.handle)
-      }
-    })
+function prefixMatch(prefix: string, info: ProfileViewBasic): boolean {
+  if (info.handle.includes(prefix)) {
+    return true
+  }
+  if (info.displayName?.toLocaleLowerCase().includes(prefix)) {
+    return true
   }
+  return false
 }