import React from 'react' import {View} from 'react-native' import {TID} from '@atproto/common-web' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' import {useAnalytics} from '#/lib/analytics/analytics' import {BSKY_APP_ACCOUNT_DID, IS_PROD_SERVICE} from '#/lib/constants' import {DISCOVER_SAVED_FEED, TIMELINE_SAVED_FEED} from '#/lib/constants' import {logEvent, useGate} from '#/lib/statsig/statsig' import {logger} from '#/logger' import { preferencesQueryKey, useOverwriteSavedFeedsMutation, } from '#/state/queries/preferences' import {RQKEY as profileRQKey} from '#/state/queries/profile' import {useAgent} from '#/state/session' import {useOnboardingDispatch} from '#/state/shell' import {uploadBlob} from 'lib/api' import { DescriptionText, OnboardingControls, TitleText, } from '#/screens/Onboarding/Layout' import {Context} from '#/screens/Onboarding/state' import { bulkWriteFollows, sortPrimaryAlgorithmFeeds, } from '#/screens/Onboarding/util' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {IconCircle} from '#/components/IconCircle' import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth' import {News2_Stroke2_Corner0_Rounded as News} from '#/components/icons/News2' import {Trending2_Stroke2_Corner2_Rounded as Trending} from '#/components/icons/Trending2' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' export function StepFinished() { const {_} = useLingui() const t = useTheme() const {track} = useAnalytics() const {state, dispatch} = React.useContext(Context) const onboardDispatch = useOnboardingDispatch() const [saving, setSaving] = React.useState(false) const {mutateAsync: overwriteSavedFeeds} = useOverwriteSavedFeedsMutation() const queryClient = useQueryClient() const {getAgent} = useAgent() const gate = useGate() const finishOnboarding = React.useCallback(async () => { setSaving(true) // TODO uncomment const { interestsStepResults, suggestedAccountsStepResults, algoFeedsStepResults, topicalFeedsStepResults, profileStepResults, } = state const {selectedInterests} = interestsStepResults const selectedFeeds = [ ...sortPrimaryAlgorithmFeeds(algoFeedsStepResults.feedUris), ...topicalFeedsStepResults.feedUris, ] try { await Promise.all([ bulkWriteFollows( getAgent, suggestedAccountsStepResults.accountDids.concat(BSKY_APP_ACCOUNT_DID), ), // these must be serial (async () => { await getAgent().setInterestsPref({tags: selectedInterests}) /* * In the reduced onboading experiment, we'll rely on the default * feeds set in `createAgentAndCreateAccount`. No feeds will be * selected in onboarding and therefore we don't need to run this * code (which would overwrite the other feeds already set). */ if (!gate('reduced_onboarding_and_home_algo')) { const otherFeeds = selectedFeeds.length ? selectedFeeds.map(f => ({ type: 'feed', value: f, pinned: true, id: TID.nextStr(), })) : [] /* * If no selected feeds and we're in prod, add the discover feed * (mimics old behavior) */ if ( IS_PROD_SERVICE(getAgent().service.toString()) && !otherFeeds.length ) { otherFeeds.push({ ...DISCOVER_SAVED_FEED, pinned: true, id: TID.nextStr(), }) } await overwriteSavedFeeds([ { ...TIMELINE_SAVED_FEED, pinned: true, id: TID.nextStr(), }, ...otherFeeds, ]) } })(), (async () => { if (!gate('reduced_onboarding_and_home_algo')) return const {imageUri, imageMime} = profileStepResults if (imageUri && imageMime) { const blobPromise = uploadBlob(getAgent(), imageUri, imageMime) await getAgent().upsertProfile(async existing => { existing = existing ?? {} const res = await blobPromise if (res.data.blob) { existing.avatar = res.data.blob } return existing }) } logEvent('onboarding:finished:avatarResult', { avatarResult: profileStepResults.isCreatedAvatar ? 'created' : profileStepResults.image ? 'uploaded' : 'default', }) })(), ]) } catch (e: any) { logger.info(`onboarding: bulk save failed`) logger.error(e) // don't alert the user, just let them into their account } // Try to ensure that prefs and profile are up-to-date by the time we render Home. await Promise.all([ queryClient.invalidateQueries({ queryKey: preferencesQueryKey, }), queryClient.invalidateQueries({ queryKey: profileRQKey(getAgent().session?.did ?? ''), }), ]).catch(e => { logger.error(e) // Keep going. }) setSaving(false) dispatch({type: 'finish'}) onboardDispatch({type: 'finish'}) track('OnboardingV2:StepFinished:End') track('OnboardingV2:Complete') logEvent('onboarding:finished:nextPressed', {}) }, [ state, dispatch, onboardDispatch, setSaving, overwriteSavedFeeds, track, getAgent, gate, queryClient, ]) React.useEffect(() => { track('OnboardingV2:StepFinished:Start') }, [track]) return ( You're ready to go! We hope you have a wonderful time. Remember, Bluesky is: Public Your posts, likes, and blocks are public. Mutes are private. Open Never lose access to your followers or data. Flexible Choose the algorithms that power your custom feeds. ) }