From 04b3ebbf63991248e5ba8ef1e60ca5c9f1f7af7b Mon Sep 17 00:00:00 2001 From: Aryan Goharzad Date: Thu, 9 Feb 2023 17:33:35 -0500 Subject: [PATCH] Adds hardcoded suggested list (#178) * Adds hardcoded suggested list * Update suggested-actors-view to support page sizes smaller than the hardcoded list --------- Co-authored-by: Paul Frazee --- package.json | 2 +- src/lib/suggestedFollows.ts | 65 ++++++++++++++++++ src/state/models/suggested-actors-view.ts | 81 ++++++++++++++++++++--- yarn.lock | 8 +-- 4 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 src/lib/suggestedFollows.ts diff --git a/package.json b/package.json index 41b21df7d0..d0cf9f9ea2 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "e2e": "detox test --configuration ios.sim.debug --take-screenshots all" }, "dependencies": { - "@atproto/api": "^0.1.0", + "@atproto/api": "^0.1.1", "@atproto/lexicon": "^0.0.4", "@atproto/xrpc": "^0.0.4", "@bam.tech/react-native-image-resizer": "^3.0.4", diff --git a/src/lib/suggestedFollows.ts b/src/lib/suggestedFollows.ts new file mode 100644 index 0000000000..72b87e925f --- /dev/null +++ b/src/lib/suggestedFollows.ts @@ -0,0 +1,65 @@ +const productionSuggestedFollows = [ + 'john', + 'visakanv ', + 'saz ', + 'steph ', + 'ratzlaff ', + 'beth', + 'weisser ', + 'katherine ', + 'annagat', + 'josh', + 'lurkshark', + 'amir', + 'amyxzh', + 'danielle ', + 'jack-frazee', + 'vibes', + 'cat', + 'yuriy', + 'alvinreyes', + 'skoot', + 'patricia', + 'ara4n', + 'case', + 'armand', + 'ivan', + 'nicholas', + 'kelsey', + 'ericlee', + 'emily', + 'jake', + 'jennijuju', + 'ian5v', + 'bnewbold', + 'chris', + 'mtclai', + 'willscott', + 'michael', + 'kwkroeger', + 'broox', + 'iamrosewang', + 'jack-morrison', + 'pwang', + 'martin', + 'jack', + 'dan', + 'why', + 'divy', + 'jay', + 'paul', +].map(handle => `${handle}.bsky.social`) + +const stagingSuggestedFollows = ['arcalinea', 'paul', 'paul2'].map( + handle => `${handle}.staging.bsky.dev`, +) + +const devSuggestedFollows = ['alice', 'bob', 'carla'].map( + handle => `${handle}.test`, +) + +export { + productionSuggestedFollows, + stagingSuggestedFollows, + devSuggestedFollows, +} diff --git a/src/state/models/suggested-actors-view.ts b/src/state/models/suggested-actors-view.ts index 33bc521f09..9970e44232 100644 --- a/src/state/models/suggested-actors-view.ts +++ b/src/state/models/suggested-actors-view.ts @@ -1,12 +1,30 @@ import {makeAutoObservable, runInAction} from 'mobx' -import {AppBskyActorGetSuggestions as GetSuggestions} from '@atproto/api' +import { + AppBskyActorGetSuggestions as GetSuggestions, + AppBskyActorProfile as Profile, +} from '@atproto/api' import {RootStoreModel} from './root-store' import {cleanError} from '../../lib/strings' import {bundleAsync} from '../../lib/async/bundle' +import { + devSuggestedFollows, + productionSuggestedFollows, + stagingSuggestedFollows, +} from '../../lib/suggestedFollows' const PAGE_SIZE = 30 -export type SuggestedActor = GetSuggestions.Actor +export type SuggestedActor = GetSuggestions.Actor | Profile.View + +const getSuggestionList = ({serviceUrl}: {serviceUrl: string}) => { + if (serviceUrl.includes('localhost')) { + return devSuggestedFollows + } else if (serviceUrl.includes('staging')) { + return stagingSuggestedFollows + } else { + return productionSuggestedFollows + } +} export class SuggestedActorsViewModel { // state @@ -18,6 +36,8 @@ export class SuggestedActorsViewModel { hasMore = true loadMoreCursor?: string + private hardCodedSuggestions: SuggestedActor[] | undefined + // data suggestions: SuggestedActor[] = [] @@ -57,6 +77,9 @@ export class SuggestedActorsViewModel { if (!replace && !this.hasMore) { return } + if (replace) { + this.hardCodedSuggestions = undefined + } this._xLoading(replace) try { let items: SuggestedActor[] = this.suggestions @@ -66,13 +89,27 @@ export class SuggestedActorsViewModel { } let res do { - res = await this.rootStore.api.app.bsky.actor.getSuggestions({ - limit: this.pageSize, - cursor: this.loadMoreCursor, - }) - this.loadMoreCursor = res.data.cursor - this.hasMore = !!this.loadMoreCursor - items = items.concat(res.data.actors) + await this.fetchHardcodedSuggestions() + if (this.hardCodedSuggestions && this.hardCodedSuggestions.length > 0) { + // pull from the hard-coded suggestions + const newItems = this.hardCodedSuggestions.splice(0, this.pageSize) + items = items.concat(newItems) + this.hasMore = true + this.loadMoreCursor = undefined + } else { + // pull from the PDS' algo + res = await this.rootStore.api.app.bsky.actor.getSuggestions({ + limit: this.pageSize, + cursor: this.loadMoreCursor, + }) + this.loadMoreCursor = res.data.cursor + this.hasMore = !!this.loadMoreCursor + items = items.concat( + res.data.actors.filter( + actor => !items.find(i => i.did === actor.did), + ), + ) + } } while (items.length < this.pageSize && this.hasMore) runInAction(() => { this.suggestions = items @@ -83,6 +120,32 @@ export class SuggestedActorsViewModel { } }) + private async fetchHardcodedSuggestions() { + if (!this.hardCodedSuggestions) { + try { + const suggestionsList = getSuggestionList({ + serviceUrl: this.rootStore.session.currentSession?.service || '', + }) + const res = await this.rootStore.api.app.bsky.actor.getProfiles({ + actors: suggestionsList, + }) + runInAction(() => { + this.hardCodedSuggestions = res.data.profiles.filter( + profile => !profile.myState?.follow, + ) + }) + } catch (e) { + this.rootStore.log.error( + 'Failed to getProfiles() for suggested follows', + {e}, + ) + runInAction(() => { + this.hardCodedSuggestions = [] + }) + } + } + } + // state transitions // = diff --git a/yarn.lock b/yarn.lock index 5580df1cc9..4029b33819 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,10 +19,10 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@atproto/api@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.1.0.tgz#0677cdde0a8b943b904dbc3f7f7357b329eca48a" - integrity sha512-+vKAEEgh3GgLfK/QZGAoQKesmhYcCsuiiEzr1miCQAuf7zfTsbX27zHERIuEVT+TqTH98MNbd3uyzF/NAbYMwQ== +"@atproto/api@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.1.1.tgz#0215dd80b98b8698f9e08932d4cdaf52676cb56c" + integrity sha512-OMKLXuWxsdaHmmZ8XzwZ1uzWpq1sr9cuzZJLAbWJz0HRDQ3a0tg1uI80XvDYHFep0qgVoT2OKJNtB23Wp7nKoQ== dependencies: "@atproto/xrpc" "*" typed-emitter "^2.1.0"