diff --git a/package.json b/package.json index 1a56c871..dcf3b517 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "build:apk": "eas build -p android --profile dev-android-apk" }, "dependencies": { - "@atproto/api": "^0.6.12", + "@atproto/api": "^0.6.13", "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@emoji-mart/react": "^1.1.1", diff --git a/src/state/models/discovery/onboarding.ts b/src/state/models/discovery/onboarding.ts index 09c9eac0..8ad321ed 100644 --- a/src/state/models/discovery/onboarding.ts +++ b/src/state/models/discovery/onboarding.ts @@ -2,10 +2,12 @@ import {makeAutoObservable} from 'mobx' import {RootStoreModel} from '../root-store' import {hasProp} from 'lib/type-guards' import {track} from 'lib/analytics/analytics' +import {SuggestedActorsModel} from './suggested-actors' export const OnboardingScreenSteps = { Welcome: 'Welcome', RecommendedFeeds: 'RecommendedFeeds', + RecommendedFollows: 'RecommendedFollows', Home: 'Home', } as const @@ -16,7 +18,11 @@ export class OnboardingModel { // state step: OnboardingStep = 'Home' // default state to skip onboarding, only enabled for new users by calling start() + // data + suggestedActors: SuggestedActorsModel + constructor(public rootStore: RootStoreModel) { + this.suggestedActors = new SuggestedActorsModel(this.rootStore) makeAutoObservable(this, { rootStore: false, hydrate: false, @@ -56,6 +62,11 @@ export class OnboardingModel { this.step = 'RecommendedFeeds' return this.step } else if (this.step === 'RecommendedFeeds') { + this.step = 'RecommendedFollows' + // prefetch recommended follows + this.suggestedActors.loadMore(true) + return this.step + } else if (this.step === 'RecommendedFollows') { this.finish() return this.step } else { diff --git a/src/state/models/discovery/suggested-actors.ts b/src/state/models/discovery/suggested-actors.ts index 0b3d3695..afa5e74e 100644 --- a/src/state/models/discovery/suggested-actors.ts +++ b/src/state/models/discovery/suggested-actors.ts @@ -19,6 +19,7 @@ export class SuggestedActorsModel { loadMoreCursor: string | undefined = undefined error = '' hasMore = false + lastInsertedAtIndex = -1 // data suggestions: SuggestedActor[] = [] @@ -110,6 +111,24 @@ export class SuggestedActorsModel { } }) + async insertSuggestionsByActor(actor: string, indexToInsertAt: number) { + // fetch suggestions + const res = + await this.rootStore.agent.app.bsky.graph.getSuggestedFollowsByActor({ + actor: actor, + }) + const {suggestions: moreSuggestions} = res.data + this.rootStore.me.follows.hydrateProfiles(moreSuggestions) + // dedupe + const toInsert = moreSuggestions.filter( + s => !this.suggestions.find(s2 => s2.did === s.did), + ) + // insert + this.suggestions.splice(indexToInsertAt + 1, 0, ...toInsert) + // update index + this.lastInsertedAtIndex = indexToInsertAt + } + // state transitions // = diff --git a/src/view/com/auth/Onboarding.tsx b/src/view/com/auth/Onboarding.tsx index 6ea8cd79..a36544a0 100644 --- a/src/view/com/auth/Onboarding.tsx +++ b/src/view/com/auth/Onboarding.tsx @@ -7,6 +7,7 @@ import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' import {Welcome} from './onboarding/Welcome' import {RecommendedFeeds} from './onboarding/RecommendedFeeds' +import {RecommendedFollows} from './onboarding/RecommendedFollows' export const Onboarding = observer(function OnboardingImpl() { const pal = usePalette('default') @@ -28,6 +29,9 @@ export const Onboarding = observer(function OnboardingImpl() { {store.onboarding.step === 'RecommendedFeeds' && ( )} + {store.onboarding.step === 'RecommendedFollows' && ( + + )} ) diff --git a/src/view/com/auth/onboarding/RecommendedFeeds.tsx b/src/view/com/auth/onboarding/RecommendedFeeds.tsx index b39714ef..24fc9eef 100644 --- a/src/view/com/auth/onboarding/RecommendedFeeds.tsx +++ b/src/view/com/auth/onboarding/RecommendedFeeds.tsx @@ -96,7 +96,7 @@ export const RecommendedFeeds = observer(function RecommendedFeedsImpl({ - Done + Next @@ -215,6 +215,7 @@ const mStyles = StyleSheet.create({ marginBottom: 16, marginHorizontal: 16, marginTop: 16, + alignItems: 'center', }, buttonText: { textAlign: 'center', diff --git a/src/view/com/auth/onboarding/RecommendedFollows.tsx b/src/view/com/auth/onboarding/RecommendedFollows.tsx new file mode 100644 index 00000000..f2710d2a --- /dev/null +++ b/src/view/com/auth/onboarding/RecommendedFollows.tsx @@ -0,0 +1,204 @@ +import React from 'react' +import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native' +import {observer} from 'mobx-react-lite' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {TabletOrDesktop, Mobile} from 'view/com/util/layouts/Breakpoints' +import {Text} from 'view/com/util/text/Text' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout' +import {Button} from 'view/com/util/forms/Button' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {usePalette} from 'lib/hooks/usePalette' +import {useStores} from 'state/index' +import {RecommendedFollowsItem} from './RecommendedFollowsItem' + +type Props = { + next: () => void +} +export const RecommendedFollows = observer(function RecommendedFollowsImpl({ + next, +}: Props) { + const store = useStores() + const pal = usePalette('default') + const {isTabletOrMobile} = useWebMediaQueries() + + React.useEffect(() => { + // Load suggested actors if not already loaded + // prefetch should happen in the onboarding model + if ( + !store.onboarding.suggestedActors.hasLoaded || + store.onboarding.suggestedActors.isEmpty + ) { + store.onboarding.suggestedActors.loadMore(true) + } + }, [store]) + + const title = ( + <> + + Follow some + + + Recommended + + + Users + + + Follow some users to get started. We can recommend you more users based + on who you find interesting. + + + + + + ) + + return ( + <> + + + {store.onboarding.suggestedActors.isLoading ? ( + + ) : ( + ( + + )} + keyExtractor={(item, index) => item.did + index.toString()} + style={{flex: 1}} + /> + )} + + + + + + + + + Check out some recommended users. Follow them to see similar + users. + + + {store.onboarding.suggestedActors.isLoading ? ( + + ) : ( + ( + + )} + keyExtractor={(item, index) => item.did + index.toString()} + style={{flex: 1}} + /> + )} +