diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-10-05 16:44:05 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-05 16:44:05 -0700 |
commit | bd7db8af26bfbf94a80972671ca714a143bee28e (patch) | |
tree | cf022bddc4f6b164bea51aeb3c57479d72b73355 /src | |
parent | 19f8389fc777c7ff41466748f1238f4e0a4b0619 (diff) | |
download | voidsky-bd7db8af26bfbf94a80972671ca714a143bee28e.tar.zst |
Improve typeahead search with inclusion of followed users (temporary solution) (#1612)
* Update follows cache to maintain some user info * Prioritize follows in composer autocomplete * Clean up logic and add new autocomplete to search * Update follow hook
Diffstat (limited to 'src')
20 files changed, 194 insertions, 139 deletions
diff --git a/src/lib/hooks/useFollowDid.ts b/src/lib/hooks/useFollowProfile.ts index 223adb047..6220daba8 100644 --- a/src/lib/hooks/useFollowDid.ts +++ b/src/lib/hooks/useFollowProfile.ts @@ -1,11 +1,11 @@ import React from 'react' - +import {AppBskyActorDefs} from '@atproto/api' import {useStores} from 'state/index' import {FollowState} from 'state/models/cache/my-follows' -export function useFollowDid({did}: {did: string}) { +export function useFollowProfile(profile: AppBskyActorDefs.ProfileViewBasic) { const store = useStores() - const state = store.me.follows.getFollowState(did) + const state = store.me.follows.getFollowState(profile.did) return { state, @@ -13,8 +13,10 @@ export function useFollowDid({did}: {did: string}) { toggle: React.useCallback(async () => { if (state === FollowState.Following) { try { - await store.agent.deleteFollow(store.me.follows.getFollowUri(did)) - store.me.follows.removeFollow(did) + await store.agent.deleteFollow( + store.me.follows.getFollowUri(profile.did), + ) + store.me.follows.removeFollow(profile.did) return { state: FollowState.NotFollowing, following: false, @@ -25,8 +27,14 @@ export function useFollowDid({did}: {did: string}) { } } else if (state === FollowState.NotFollowing) { try { - const res = await store.agent.follow(did) - store.me.follows.addFollow(did, res.uri) + const res = await store.agent.follow(profile.did) + store.me.follows.addFollow(profile.did, { + followRecordUri: res.uri, + did: profile.did, + handle: profile.handle, + displayName: profile.displayName, + avatar: profile.avatar, + }) return { state: FollowState.Following, following: true, @@ -41,6 +49,6 @@ export function useFollowDid({did}: {did: string}) { state: FollowState.Unknown, following: false, } - }, [store, did, state]), + }, [store, profile, state]), } } diff --git a/src/state/models/cache/my-follows.ts b/src/state/models/cache/my-follows.ts index 10f88c4a9..14dc9895d 100644 --- a/src/state/models/cache/my-follows.ts +++ b/src/state/models/cache/my-follows.ts @@ -1,7 +1,14 @@ import {makeAutoObservable} from 'mobx' -import {AppBskyActorDefs} from '@atproto/api' +import { + AppBskyActorDefs, + AppBskyGraphGetFollows as GetFollows, + moderateProfile, +} from '@atproto/api' import {RootStoreModel} from '../root-store' +const MAX_SYNC_PAGES = 10 +const SYNC_TTL = 60e3 * 10 // 10 minutes + type Profile = AppBskyActorDefs.ProfileViewBasic | AppBskyActorDefs.ProfileView export enum FollowState { @@ -10,6 +17,14 @@ export enum FollowState { Unknown, } +export interface FollowInfo { + did: string + followRecordUri: string | undefined + handle: string + displayName: string | undefined + avatar: string | undefined +} + /** * This model is used to maintain a synced local cache of the user's * follows. It should be periodically refreshed and updated any time @@ -17,9 +32,8 @@ export enum FollowState { */ export class MyFollowsCache { // data - followDidToRecordMap: Record<string, string | boolean> = {} + byDid: Record<string, FollowInfo> = {} lastSync = 0 - myDid?: string constructor(public rootStore: RootStoreModel) { makeAutoObservable( @@ -35,16 +49,45 @@ export class MyFollowsCache { // = clear() { - this.followDidToRecordMap = {} - this.lastSync = 0 - this.myDid = undefined + this.byDid = {} + } + + /** + * Syncs a subset of the user's follows + * for performance reasons, caps out at 1000 follows + */ + async syncIfNeeded() { + if (this.lastSync > Date.now() - SYNC_TTL) { + return + } + + let cursor + for (let i = 0; i < MAX_SYNC_PAGES; 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.hydrateMany(res.data.follows) + if (!res.data.cursor) { + break + } + cursor = res.data.cursor + } + + this.lastSync = Date.now() } getFollowState(did: string): FollowState { - if (typeof this.followDidToRecordMap[did] === 'undefined') { + if (typeof this.byDid[did] === 'undefined') { return FollowState.Unknown } - if (typeof this.followDidToRecordMap[did] === 'string') { + if (typeof this.byDid[did].followRecordUri === 'string') { return FollowState.Following } return FollowState.NotFollowing @@ -53,49 +96,41 @@ export class MyFollowsCache { async fetchFollowState(did: string): Promise<FollowState> { // TODO: can we get a more efficient method for this? getProfile fetches more data than we need -prf const res = await this.rootStore.agent.getProfile({actor: did}) - if (res.data.viewer?.following) { - this.addFollow(did, res.data.viewer.following) - } else { - this.removeFollow(did) - } + this.hydrate(did, res.data) return this.getFollowState(did) } getFollowUri(did: string): string { - const v = this.followDidToRecordMap[did] + const v = this.byDid[did] if (typeof v === 'string') { return v } throw new Error('Not a followed user') } - addFollow(did: string, recordUri: string) { - this.followDidToRecordMap[did] = recordUri + addFollow(did: string, info: FollowInfo) { + this.byDid[did] = info } removeFollow(did: string) { - this.followDidToRecordMap[did] = false + if (this.byDid[did]) { + this.byDid[did].followRecordUri = undefined + } } - /** - * Use this to incrementally update the cache as views provide information - */ - hydrate(did: string, recordUri: string | undefined) { - if (recordUri) { - this.followDidToRecordMap[did] = recordUri - } else { - this.followDidToRecordMap[did] = false + hydrate(did: string, profile: Profile) { + this.byDid[did] = { + did, + followRecordUri: profile.viewer?.following, + handle: profile.handle, + displayName: profile.displayName, + avatar: profile.avatar, } } - /** - * Use this to incrementally update the cache as views provide information - */ - hydrateProfiles(profiles: Profile[]) { + hydrateMany(profiles: Profile[]) { for (const profile of profiles) { - if (profile.viewer) { - this.hydrate(profile.did, profile.viewer.following) - } + this.hydrate(profile.did, profile) } } } diff --git a/src/state/models/content/profile.ts b/src/state/models/content/profile.ts index 26fa6008c..906f84c28 100644 --- a/src/state/models/content/profile.ts +++ b/src/state/models/content/profile.ts @@ -137,7 +137,7 @@ export class ProfileModel { runInAction(() => { this.followersCount++ this.viewer.following = res.uri - this.rootStore.me.follows.addFollow(this.did, res.uri) + this.rootStore.me.follows.hydrate(this.did, this) }) track('Profile:Follow', { username: this.handle, @@ -290,8 +290,8 @@ export class ProfileModel { this.labels = res.data.labels if (res.data.viewer) { Object.assign(this.viewer, res.data.viewer) - this.rootStore.me.follows.hydrate(this.did, res.data.viewer.following) } + this.rootStore.me.follows.hydrate(this.did, res.data) } async _createRichText() { 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/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 } diff --git a/src/state/models/feeds/posts.ts b/src/state/models/feeds/posts.ts index 2a7170325..2462689b1 100644 --- a/src/state/models/feeds/posts.ts +++ b/src/state/models/feeds/posts.ts @@ -316,7 +316,7 @@ export class PostsFeedModel { this.emptyFetches = 0 } - this.rootStore.me.follows.hydrateProfiles( + this.rootStore.me.follows.hydrateMany( res.feed.map(item => item.post.author), ) for (const item of res.feed) { diff --git a/src/state/models/invited-users.ts b/src/state/models/invited-users.ts index a28e0309a..cd3667062 100644 --- a/src/state/models/invited-users.ts +++ b/src/state/models/invited-users.ts @@ -61,7 +61,7 @@ export class InvitedUsers { profile => !profile.viewer?.following, ) }) - this.rootStore.me.follows.hydrateProfiles(this.profiles) + this.rootStore.me.follows.hydrateMany(this.profiles) } catch (e) { this.rootStore.log.error( 'Failed to fetch profiles for invited users', diff --git a/src/state/models/lists/likes.ts b/src/state/models/lists/likes.ts index 39882d73a..dd3cf18a3 100644 --- a/src/state/models/lists/likes.ts +++ b/src/state/models/lists/likes.ts @@ -126,7 +126,7 @@ export class LikesModel { _appendAll(res: GetLikes.Response) { this.loadMoreCursor = res.data.cursor this.hasMore = !!this.loadMoreCursor - this.rootStore.me.follows.hydrateProfiles( + this.rootStore.me.follows.hydrateMany( res.data.likes.map(like => like.actor), ) this.likes = this.likes.concat(res.data.likes) diff --git a/src/state/models/lists/reposted-by.ts b/src/state/models/lists/reposted-by.ts index a70375bdc..5d4fc107d 100644 --- a/src/state/models/lists/reposted-by.ts +++ b/src/state/models/lists/reposted-by.ts @@ -130,6 +130,6 @@ export class RepostedByModel { this.loadMoreCursor = res.data.cursor this.hasMore = !!this.loadMoreCursor this.repostedBy = this.repostedBy.concat(res.data.repostedBy) - this.rootStore.me.follows.hydrateProfiles(res.data.repostedBy) + this.rootStore.me.follows.hydrateMany(res.data.repostedBy) } } diff --git a/src/state/models/lists/user-followers.ts b/src/state/models/lists/user-followers.ts index 2962d6242..1f817c33c 100644 --- a/src/state/models/lists/user-followers.ts +++ b/src/state/models/lists/user-followers.ts @@ -115,6 +115,6 @@ export class UserFollowersModel { this.loadMoreCursor = res.data.cursor this.hasMore = !!this.loadMoreCursor this.followers = this.followers.concat(res.data.followers) - this.rootStore.me.follows.hydrateProfiles(res.data.followers) + this.rootStore.me.follows.hydrateMany(res.data.followers) } } diff --git a/src/state/models/lists/user-follows.ts b/src/state/models/lists/user-follows.ts index 56432a796..c9630eba8 100644 --- a/src/state/models/lists/user-follows.ts +++ b/src/state/models/lists/user-follows.ts @@ -115,6 +115,6 @@ export class UserFollowsModel { this.loadMoreCursor = res.data.cursor this.hasMore = !!this.loadMoreCursor this.follows = this.follows.concat(res.data.follows) - this.rootStore.me.follows.hydrateProfiles(res.data.follows) + this.rootStore.me.follows.hydrateMany(res.data.follows) } } diff --git a/src/state/models/ui/search.ts b/src/state/models/ui/search.ts index 4ab9db513..2b2036751 100644 --- a/src/state/models/ui/search.ts +++ b/src/state/models/ui/search.ts @@ -59,7 +59,7 @@ export class SearchUIModel { } while (profilesSearch.length) } - this.rootStore.me.follows.hydrateProfiles(profiles) + this.rootStore.me.follows.hydrateMany(profiles) runInAction(() => { this.profiles = profiles diff --git a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx index 51e3bc382..2b26918d0 100644 --- a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx +++ b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx @@ -89,7 +89,7 @@ export const ProfileCard = observer(function ProfileCardImpl({ </View> <FollowButton - did={profile.did} + profile={profile} labelStyle={styles.followButton} onToggleFollow={async isFollow => { if (isFollow) { diff --git a/src/view/com/notifications/InvitedUsers.tsx b/src/view/com/notifications/InvitedUsers.tsx index 89a0da47f..aaf358b87 100644 --- a/src/view/com/notifications/InvitedUsers.tsx +++ b/src/view/com/notifications/InvitedUsers.tsx @@ -75,7 +75,7 @@ function InvitedUser({ <FollowButton unfollowedType="primary" followedType="primary-light" - did={profile.did} + profile={profile} /> <Button testID="dismissBtn" diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx index 217d326e8..adb496f6d 100644 --- a/src/view/com/profile/FollowButton.tsx +++ b/src/view/com/profile/FollowButton.tsx @@ -1,25 +1,26 @@ import React from 'react' import {StyleProp, TextStyle, View} from 'react-native' import {observer} from 'mobx-react-lite' +import {AppBskyActorDefs} from '@atproto/api' import {Button, ButtonType} from '../util/forms/Button' import * as Toast from '../util/Toast' import {FollowState} from 'state/models/cache/my-follows' -import {useFollowDid} from 'lib/hooks/useFollowDid' +import {useFollowProfile} from 'lib/hooks/useFollowProfile' export const FollowButton = observer(function FollowButtonImpl({ unfollowedType = 'inverted', followedType = 'default', - did, + profile, onToggleFollow, labelStyle, }: { unfollowedType?: ButtonType followedType?: ButtonType - did: string + profile: AppBskyActorDefs.ProfileViewBasic onToggleFollow?: (v: boolean) => void labelStyle?: StyleProp<TextStyle> }) { - const {state, following, toggle} = useFollowDid({did}) + const {state, following, toggle} = useFollowProfile(profile) const onPress = React.useCallback(async () => { try { diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx index e0c8ad21a..d1aed8934 100644 --- a/src/view/com/profile/ProfileCard.tsx +++ b/src/view/com/profile/ProfileCard.tsx @@ -200,7 +200,7 @@ export const ProfileCardWithFollowBtn = observer( noBorder={noBorder} followers={followers} renderButton={ - isMe ? undefined : () => <FollowButton did={profile.did} /> + isMe ? undefined : () => <FollowButton profile={profile} /> } /> ) diff --git a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx index b9d66a6fe..41e4022d5 100644 --- a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx +++ b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx @@ -19,7 +19,7 @@ import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' import {Text} from 'view/com/util/text/Text' import {UserAvatar} from 'view/com/util/UserAvatar' -import {useFollowDid} from 'lib/hooks/useFollowDid' +import {useFollowProfile} from 'lib/hooks/useFollowProfile' import {Button} from 'view/com/util/forms/Button' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' @@ -83,7 +83,7 @@ export function ProfileHeaderSuggestedFollows({ return [] } - store.me.follows.hydrateProfiles(suggestions) + store.me.follows.hydrateMany(suggestions) return suggestions } catch (e) { @@ -218,7 +218,7 @@ const SuggestedFollow = observer(function SuggestedFollowImpl({ const {track} = useAnalytics() const pal = usePalette('default') const store = useStores() - const {following, toggle} = useFollowDid({did: profile.did}) + const {following, toggle} = useFollowProfile(profile) const moderation = moderateProfile(profile, store.preferences.moderationOpts) const onPress = React.useCallback(async () => { diff --git a/src/view/screens/SearchMobile.tsx b/src/view/screens/SearchMobile.tsx index b545a643d..b80c1667f 100644 --- a/src/view/screens/SearchMobile.tsx +++ b/src/view/screens/SearchMobile.tsx @@ -148,18 +148,18 @@ export const SearchScreen = withAuthRequired( style={pal.view} onScroll={onMainScroll} scrollEventThrottle={100}> - {query && autocompleteView.searchRes.length ? ( + {query && autocompleteView.suggestions.length ? ( <> - {autocompleteView.searchRes.map((profile, index) => ( + {autocompleteView.suggestions.map((suggestion, index) => ( <ProfileCard - key={profile.did} - testID={`searchAutoCompleteResult-${profile.handle}`} - profile={profile} + key={suggestion.did} + testID={`searchAutoCompleteResult-${suggestion.handle}`} + profile={suggestion} noBorder={index === 0} /> ))} </> - ) : query && !autocompleteView.searchRes.length ? ( + ) : query && !autocompleteView.suggestions.length ? ( <View> <Text style={[pal.textLight, styles.searchPrompt]}> No results found for {autocompleteView.prefix} diff --git a/src/view/shell/desktop/Search.tsx b/src/view/shell/desktop/Search.tsx index dfd4f50bf..53a58c39d 100644 --- a/src/view/shell/desktop/Search.tsx +++ b/src/view/shell/desktop/Search.tsx @@ -90,9 +90,9 @@ export const DesktopSearch = observer(function DesktopSearch() { {query !== '' && ( <View style={[pal.view, pal.borderDark, styles.resultsContainer]}> - {autocompleteView.searchRes.length ? ( + {autocompleteView.suggestions.length ? ( <> - {autocompleteView.searchRes.map((item, i) => ( + {autocompleteView.suggestions.map((item, i) => ( <ProfileCard key={item.did} profile={item} noBorder={i === 0} /> ))} </> |