mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Move onboarding state to new persistence + reducer context (#1835)

authored by

Paul Frazee and committed by
GitHub
4afed4be 3a211017

+199 -167
-106
src/state/models/discovery/onboarding.ts
··· 1 - import {makeAutoObservable} from 'mobx' 2 - import {RootStoreModel} from '../root-store' 3 - import {hasProp} from 'lib/type-guards' 4 - import {track} from 'lib/analytics/analytics' 5 - import {SuggestedActorsModel} from './suggested-actors' 6 - 7 - export const OnboardingScreenSteps = { 8 - Welcome: 'Welcome', 9 - RecommendedFeeds: 'RecommendedFeeds', 10 - RecommendedFollows: 'RecommendedFollows', 11 - Home: 'Home', 12 - } as const 13 - 14 - type OnboardingStep = 15 - (typeof OnboardingScreenSteps)[keyof typeof OnboardingScreenSteps] 16 - const OnboardingStepsArray = Object.values(OnboardingScreenSteps) 17 - export class OnboardingModel { 18 - // state 19 - step: OnboardingStep = 'Home' // default state to skip onboarding, only enabled for new users by calling start() 20 - 21 - // data 22 - suggestedActors: SuggestedActorsModel 23 - 24 - constructor(public rootStore: RootStoreModel) { 25 - this.suggestedActors = new SuggestedActorsModel(this.rootStore) 26 - makeAutoObservable(this, { 27 - rootStore: false, 28 - hydrate: false, 29 - serialize: false, 30 - }) 31 - } 32 - 33 - serialize(): unknown { 34 - return { 35 - step: this.step, 36 - } 37 - } 38 - 39 - hydrate(v: unknown) { 40 - if (typeof v === 'object' && v !== null) { 41 - if ( 42 - hasProp(v, 'step') && 43 - typeof v.step === 'string' && 44 - OnboardingStepsArray.includes(v.step as OnboardingStep) 45 - ) { 46 - this.step = v.step as OnboardingStep 47 - } 48 - } else { 49 - // if there is no valid state, we'll just reset 50 - this.reset() 51 - } 52 - } 53 - 54 - /** 55 - * Returns the name of the next screen in the onboarding process based on the current step or screen name provided. 56 - * @param {OnboardingStep} [currentScreenName] 57 - * @returns name of next screen in the onboarding process 58 - */ 59 - next(currentScreenName?: OnboardingStep) { 60 - currentScreenName = currentScreenName || this.step 61 - if (currentScreenName === 'Welcome') { 62 - this.step = 'RecommendedFeeds' 63 - return this.step 64 - } else if (this.step === 'RecommendedFeeds') { 65 - this.step = 'RecommendedFollows' 66 - // prefetch recommended follows 67 - this.suggestedActors.loadMore(true) 68 - return this.step 69 - } else if (this.step === 'RecommendedFollows') { 70 - this.finish() 71 - return this.step 72 - } else { 73 - // if we get here, we're in an invalid state, let's just go Home 74 - return 'Home' 75 - } 76 - } 77 - 78 - start() { 79 - this.step = 'Welcome' 80 - track('Onboarding:Begin') 81 - } 82 - 83 - finish() { 84 - this.rootStore.me.mainFeed.refresh() // load the selected content 85 - this.step = 'Home' 86 - track('Onboarding:Complete') 87 - } 88 - 89 - reset() { 90 - this.step = 'Welcome' 91 - track('Onboarding:Reset') 92 - } 93 - 94 - skip() { 95 - this.step = 'Home' 96 - track('Onboarding:Skipped') 97 - } 98 - 99 - get isComplete() { 100 - return this.step === 'Home' 101 - } 102 - 103 - get isActive() { 104 - return !this.isComplete 105 - } 106 - }
-6
src/state/models/root-store.ts
··· 27 27 // remove after backend testing finishes 28 28 // -prf 29 29 import {applyDebugHeader} from 'lib/api/debug-appview-proxy-header' 30 - import {OnboardingModel} from './discovery/onboarding' 31 30 32 31 export const appInfo = z.object({ 33 32 build: z.string(), ··· 44 43 shell = new ShellUiModel(this) 45 44 preferences = new PreferencesModel(this) 46 45 me = new MeModel(this) 47 - onboarding = new OnboardingModel(this) 48 46 invitedUsers = new InvitedUsers(this) 49 47 handleResolutions = new HandleResolutionsCache() 50 48 profiles = new ProfilesCache(this) ··· 71 69 appInfo: this.appInfo, 72 70 session: this.session.serialize(), 73 71 me: this.me.serialize(), 74 - onboarding: this.onboarding.serialize(), 75 72 preferences: this.preferences.serialize(), 76 73 invitedUsers: this.invitedUsers.serialize(), 77 74 mutedThreads: this.mutedThreads.serialize(), ··· 88 85 } 89 86 if (hasProp(v, 'me')) { 90 87 this.me.hydrate(v.me) 91 - } 92 - if (hasProp(v, 'onboarding')) { 93 - this.onboarding.hydrate(v.onboarding) 94 88 } 95 89 if (hasProp(v, 'session')) { 96 90 this.session.hydrate(v.session)
+4 -3
src/state/models/ui/create-account.ts
··· 9 9 import {getAge} from 'lib/strings/time' 10 10 import {track} from 'lib/analytics/analytics' 11 11 import {logger} from '#/logger' 12 + import {DispatchContext as OnboardingDispatchContext} from '#/state/shell/onboarding' 12 13 13 14 const DEFAULT_DATE = new Date(Date.now() - 60e3 * 60 * 24 * 365 * 20) // default to 20 years ago 14 15 ··· 90 91 } 91 92 } 92 93 93 - async submit() { 94 + async submit(onboardingDispatch: OnboardingDispatchContext) { 94 95 if (!this.email) { 95 96 this.setStep(2) 96 97 return this.setError('Please enter your email.') ··· 111 112 this.setIsProcessing(true) 112 113 113 114 try { 114 - this.rootStore.onboarding.start() // start now to avoid flashing the wrong view 115 + onboardingDispatch({type: 'start'}) // start now to avoid flashing the wrong view 115 116 await this.rootStore.session.createAccount({ 116 117 service: this.serviceUrl, 117 118 email: this.email, ··· 122 123 /* dont await */ this.rootStore.preferences.setBirthDate(this.birthDate) 123 124 track('Create Account') 124 125 } catch (e: any) { 125 - this.rootStore.onboarding.skip() // undo starting the onboard 126 + onboardingDispatch({type: 'skip'}) // undo starting the onboard 126 127 let errMsg = e.toString() 127 128 if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) { 128 129 errMsg =
+3 -3
src/state/persisted/schema.ts
··· 7 7 did: z.string(), 8 8 refreshJwt: z.string().optional(), 9 9 accessJwt: z.string().optional(), 10 - handle: z.string(), 11 - displayName: z.string(), 12 - aviUrl: z.string(), 10 + handle: z.string().optional(), 11 + displayName: z.string().optional(), 12 + aviUrl: z.string().optional(), 13 13 }) 14 14 15 15 export const schema = z.object({
+1 -1
src/state/shell/color-mode.tsx
··· 27 27 setState(persisted.get('colorMode')) 28 28 updateDocument(persisted.get('colorMode')) 29 29 }) 30 - }, [setStateWrapped]) 30 + }, [setState]) 31 31 32 32 return ( 33 33 <stateContext.Provider value={state}>
+5 -1
src/state/shell/index.tsx
··· 4 4 import {Provider as MinimalModeProvider} from './minimal-mode' 5 5 import {Provider as ColorModeProvider} from './color-mode' 6 6 import {Provider as AltTextRequiredProvider} from './alt-text-required' 7 + import {Provider as OnboardingProvider} from './onboarding' 7 8 8 9 export {useIsDrawerOpen, useSetDrawerOpen} from './drawer-open' 9 10 export { ··· 16 17 useRequireAltTextEnabled, 17 18 useSetRequireAltTextEnabled, 18 19 } from './alt-text-required' 20 + export {useOnboardingState, useOnboardingDispatch} from './onboarding' 19 21 20 22 export function Provider({children}: React.PropsWithChildren<{}>) { 21 23 return ( ··· 23 25 <DrawerSwipableProvider> 24 26 <MinimalModeProvider> 25 27 <ColorModeProvider> 26 - <AltTextRequiredProvider>{children}</AltTextRequiredProvider> 28 + <OnboardingProvider> 29 + <AltTextRequiredProvider>{children}</AltTextRequiredProvider> 30 + </OnboardingProvider> 27 31 </ColorModeProvider> 28 32 </MinimalModeProvider> 29 33 </DrawerSwipableProvider>
+119
src/state/shell/onboarding.tsx
··· 1 + import React from 'react' 2 + import * as persisted from '#/state/persisted' 3 + import {track} from '#/lib/analytics/analytics' 4 + 5 + export const OnboardingScreenSteps = { 6 + Welcome: 'Welcome', 7 + RecommendedFeeds: 'RecommendedFeeds', 8 + RecommendedFollows: 'RecommendedFollows', 9 + Home: 'Home', 10 + } as const 11 + 12 + type OnboardingStep = 13 + (typeof OnboardingScreenSteps)[keyof typeof OnboardingScreenSteps] 14 + const OnboardingStepsArray = Object.values(OnboardingScreenSteps) 15 + 16 + type Action = 17 + | {type: 'set'; step: OnboardingStep} 18 + | {type: 'next'; currentStep?: OnboardingStep} 19 + | {type: 'start'} 20 + | {type: 'finish'} 21 + | {type: 'skip'} 22 + 23 + export type StateContext = persisted.Schema['onboarding'] & { 24 + isComplete: boolean 25 + isActive: boolean 26 + } 27 + export type DispatchContext = (action: Action) => void 28 + 29 + const stateContext = React.createContext<StateContext>( 30 + compute(persisted.defaults.onboarding), 31 + ) 32 + const dispatchContext = React.createContext<DispatchContext>((_: Action) => {}) 33 + 34 + function reducer(state: StateContext, action: Action): StateContext { 35 + switch (action.type) { 36 + case 'set': { 37 + if (OnboardingStepsArray.includes(action.step)) { 38 + persisted.write('onboarding', {step: action.step}) 39 + return compute({...state, step: action.step}) 40 + } 41 + return state 42 + } 43 + case 'next': { 44 + const currentStep = action.currentStep || state.step 45 + let nextStep = 'Home' 46 + if (currentStep === 'Welcome') { 47 + nextStep = 'RecommendedFeeds' 48 + } else if (currentStep === 'RecommendedFeeds') { 49 + nextStep = 'RecommendedFollows' 50 + } else if (currentStep === 'RecommendedFollows') { 51 + nextStep = 'Home' 52 + } 53 + persisted.write('onboarding', {step: nextStep}) 54 + return compute({...state, step: nextStep}) 55 + } 56 + case 'start': { 57 + track('Onboarding:Begin') 58 + persisted.write('onboarding', {step: 'Welcome'}) 59 + return compute({...state, step: 'Welcome'}) 60 + } 61 + case 'finish': { 62 + track('Onboarding:Complete') 63 + persisted.write('onboarding', {step: 'Home'}) 64 + return compute({...state, step: 'Home'}) 65 + } 66 + case 'skip': { 67 + track('Onboarding:Skipped') 68 + persisted.write('onboarding', {step: 'Home'}) 69 + return compute({...state, step: 'Home'}) 70 + } 71 + default: { 72 + throw new Error('Invalid action') 73 + } 74 + } 75 + } 76 + 77 + export function Provider({children}: React.PropsWithChildren<{}>) { 78 + const [state, dispatch] = React.useReducer( 79 + reducer, 80 + compute(persisted.get('onboarding')), 81 + ) 82 + 83 + React.useEffect(() => { 84 + return persisted.onUpdate(() => { 85 + dispatch({ 86 + type: 'set', 87 + step: persisted.get('onboarding').step as OnboardingStep, 88 + }) 89 + }) 90 + }, [dispatch]) 91 + 92 + return ( 93 + <stateContext.Provider value={state}> 94 + <dispatchContext.Provider value={dispatch}> 95 + {children} 96 + </dispatchContext.Provider> 97 + </stateContext.Provider> 98 + ) 99 + } 100 + 101 + export function useOnboardingState() { 102 + return React.useContext(stateContext) 103 + } 104 + 105 + export function useOnboardingDispatch() { 106 + return React.useContext(dispatchContext) 107 + } 108 + 109 + export function isOnboardingActive() { 110 + return compute(persisted.get('onboarding')).isActive 111 + } 112 + 113 + function compute(state: persisted.Schema['onboarding']): StateContext { 114 + return { 115 + ...state, 116 + isActive: state.step !== 'Home', 117 + isComplete: state.step === 'Home', 118 + } 119 + }
+8 -7
src/view/com/auth/Onboarding.tsx
··· 4 4 import {ErrorBoundary} from 'view/com/util/ErrorBoundary' 5 5 import {s} from 'lib/styles' 6 6 import {usePalette} from 'lib/hooks/usePalette' 7 - import {useStores} from 'state/index' 8 7 import {Welcome} from './onboarding/Welcome' 9 8 import {RecommendedFeeds} from './onboarding/RecommendedFeeds' 10 9 import {RecommendedFollows} from './onboarding/RecommendedFollows' 11 10 import {useSetMinimalShellMode} from '#/state/shell/minimal-mode' 11 + import {useOnboardingState, useOnboardingDispatch} from '#/state/shell' 12 12 13 13 export const Onboarding = observer(function OnboardingImpl() { 14 14 const pal = usePalette('default') 15 - const store = useStores() 16 15 const setMinimalShellMode = useSetMinimalShellMode() 16 + const onboardingState = useOnboardingState() 17 + const onboardingDispatch = useOnboardingDispatch() 17 18 18 19 React.useEffect(() => { 19 20 setMinimalShellMode(true) 20 21 }, [setMinimalShellMode]) 21 22 22 - const next = () => store.onboarding.next() 23 - const skip = () => store.onboarding.skip() 23 + const next = () => onboardingDispatch({type: 'next'}) 24 + const skip = () => onboardingDispatch({type: 'skip'}) 24 25 25 26 return ( 26 27 <SafeAreaView testID="onboardingView" style={[s.hContentRegion, pal.view]}> 27 28 <ErrorBoundary> 28 - {store.onboarding.step === 'Welcome' && ( 29 + {onboardingState.step === 'Welcome' && ( 29 30 <Welcome skip={skip} next={next} /> 30 31 )} 31 - {store.onboarding.step === 'RecommendedFeeds' && ( 32 + {onboardingState.step === 'RecommendedFeeds' && ( 32 33 <RecommendedFeeds next={next} /> 33 34 )} 34 - {store.onboarding.step === 'RecommendedFollows' && ( 35 + {onboardingState.step === 'RecommendedFollows' && ( 35 36 <RecommendedFollows next={next} /> 36 37 )} 37 38 </ErrorBoundary>
+4 -2
src/view/com/auth/create/CreateAccount.tsx
··· 15 15 import {useStores} from 'state/index' 16 16 import {CreateAccountModel} from 'state/models/ui/create-account' 17 17 import {usePalette} from 'lib/hooks/usePalette' 18 + import {useOnboardingDispatch} from '#/state/shell' 18 19 19 20 import {Step1} from './Step1' 20 21 import {Step2} from './Step2' ··· 29 30 const pal = usePalette('default') 30 31 const store = useStores() 31 32 const model = React.useMemo(() => new CreateAccountModel(store), [store]) 33 + const onboardingDispatch = useOnboardingDispatch() 32 34 33 35 React.useEffect(() => { 34 36 screen('CreateAccount') ··· 59 61 model.next() 60 62 } else { 61 63 try { 62 - await model.submit() 64 + await model.submit(onboardingDispatch) 63 65 } catch { 64 66 // dont need to handle here 65 67 } finally { 66 68 track('Try Create Account') 67 69 } 68 70 } 69 - }, [model, track]) 71 + }, [model, track, onboardingDispatch]) 70 72 71 73 return ( 72 74 <LoggedOutLayout
+23 -16
src/view/com/auth/onboarding/RecommendedFollows.tsx
··· 11 11 import {usePalette} from 'lib/hooks/usePalette' 12 12 import {useStores} from 'state/index' 13 13 import {RecommendedFollowsItem} from './RecommendedFollowsItem' 14 + import {SuggestedActorsModel} from '#/state/models/discovery/suggested-actors' 14 15 15 16 type Props = { 16 17 next: () => void ··· 21 22 const store = useStores() 22 23 const pal = usePalette('default') 23 24 const {isTabletOrMobile} = useWebMediaQueries() 24 - 25 - React.useEffect(() => { 26 - // Load suggested actors if not already loaded 27 - // prefetch should happen in the onboarding model 28 - if ( 29 - !store.onboarding.suggestedActors.hasLoaded || 30 - store.onboarding.suggestedActors.isEmpty 31 - ) { 32 - store.onboarding.suggestedActors.loadMore(true) 33 - } 25 + const suggestedActors = React.useMemo(() => { 26 + const model = new SuggestedActorsModel(store) 27 + model.refresh() 28 + return model 34 29 }, [store]) 35 30 36 31 const title = ( ··· 98 93 horizontal 99 94 titleStyle={isTabletOrMobile ? undefined : {minWidth: 470}} 100 95 contentStyle={{paddingHorizontal: 0}}> 101 - {store.onboarding.suggestedActors.isLoading ? ( 96 + {suggestedActors.isLoading ? ( 102 97 <ActivityIndicator size="large" /> 103 98 ) : ( 104 99 <FlatList 105 - data={store.onboarding.suggestedActors.suggestions} 100 + data={suggestedActors.suggestions} 106 101 renderItem={({item, index}) => ( 107 - <RecommendedFollowsItem item={item} index={index} /> 102 + <RecommendedFollowsItem 103 + item={item} 104 + index={index} 105 + insertSuggestionsByActor={suggestedActors.insertSuggestionsByActor.bind( 106 + suggestedActors, 107 + )} 108 + /> 108 109 )} 109 110 keyExtractor={(item, index) => item.did + index.toString()} 110 111 style={{flex: 1}} ··· 126 127 users. 127 128 </Text> 128 129 </View> 129 - {store.onboarding.suggestedActors.isLoading ? ( 130 + {suggestedActors.isLoading ? ( 130 131 <ActivityIndicator size="large" /> 131 132 ) : ( 132 133 <FlatList 133 - data={store.onboarding.suggestedActors.suggestions} 134 + data={suggestedActors.suggestions} 134 135 renderItem={({item, index}) => ( 135 - <RecommendedFollowsItem item={item} index={index} /> 136 + <RecommendedFollowsItem 137 + item={item} 138 + index={index} 139 + insertSuggestionsByActor={suggestedActors.insertSuggestionsByActor.bind( 140 + suggestedActors, 141 + )} 142 + /> 136 143 )} 137 144 keyExtractor={(item, index) => item.did + index.toString()} 138 145 style={{flex: 1}}
+17 -16
src/view/com/auth/onboarding/RecommendedFollowsItem.tsx
··· 1 - import React, {useMemo} from 'react' 1 + import React from 'react' 2 2 import {View, StyleSheet, ActivityIndicator} from 'react-native' 3 3 import {AppBskyActorDefs, moderateProfile} from '@atproto/api' 4 4 import {observer} from 'mobx-react-lite' ··· 18 18 type Props = { 19 19 item: SuggestedActor 20 20 index: number 21 + insertSuggestionsByActor: (did: string, index: number) => Promise<void> 21 22 } 22 - export const RecommendedFollowsItem: React.FC<Props> = ({item, index}) => { 23 + export const RecommendedFollowsItem: React.FC<Props> = ({ 24 + item, 25 + index, 26 + insertSuggestionsByActor, 27 + }) => { 23 28 const pal = usePalette('default') 24 - const store = useStores() 25 29 const {isMobile} = useWebMediaQueries() 26 - const delay = useMemo(() => { 27 - return ( 28 - 50 * 29 - (Math.abs(store.onboarding.suggestedActors.lastInsertedAtIndex - index) % 30 - 5) 31 - ) 32 - }, [index, store.onboarding.suggestedActors.lastInsertedAtIndex]) 33 30 34 31 return ( 35 32 <Animated.View 36 - entering={FadeInRight.delay(delay).springify()} 33 + entering={FadeInRight} 37 34 style={[ 38 35 styles.cardContainer, 39 36 pal.view, ··· 43 40 borderRightWidth: isMobile ? undefined : 1, 44 41 }, 45 42 ]}> 46 - <ProfileCard key={item.did} profile={item} index={index} /> 43 + <ProfileCard 44 + key={item.did} 45 + profile={item} 46 + index={index} 47 + insertSuggestionsByActor={insertSuggestionsByActor} 48 + /> 47 49 </Animated.View> 48 50 ) 49 51 } ··· 51 53 export const ProfileCard = observer(function ProfileCardImpl({ 52 54 profile, 53 55 index, 56 + insertSuggestionsByActor, 54 57 }: { 55 58 profile: AppBskyActorDefs.ProfileViewBasic 56 59 index: number 60 + insertSuggestionsByActor: (did: string, index: number) => Promise<void> 57 61 }) { 58 62 const {track} = useAnalytics() 59 63 const store = useStores() ··· 94 98 onToggleFollow={async isFollow => { 95 99 if (isFollow) { 96 100 setAddingMoreSuggestions(true) 97 - await store.onboarding.suggestedActors.insertSuggestionsByActor( 98 - profile.did, 99 - index, 100 - ) 101 + await insertSuggestionsByActor(profile.did, index) 101 102 setAddingMoreSuggestions(false) 102 103 track('Onboarding:SuggestedFollowFollowed') 103 104 }
+3 -1
src/view/com/auth/withAuthRequired.tsx
··· 13 13 import {Text} from '../util/text/Text' 14 14 import {usePalette} from 'lib/hooks/usePalette' 15 15 import {STATUS_PAGE_URL} from 'lib/constants' 16 + import {useOnboardingState} from '#/state/shell' 16 17 17 18 export const withAuthRequired = <P extends object>( 18 19 Component: React.ComponentType<P>, 19 20 ): React.FC<P> => 20 21 observer(function AuthRequired(props: P) { 21 22 const store = useStores() 23 + const onboardingState = useOnboardingState() 22 24 if (store.session.isResumingSession) { 23 25 return <Loading /> 24 26 } 25 27 if (!store.session.hasSession) { 26 28 return <LoggedOut /> 27 29 } 28 - if (store.onboarding.isActive) { 30 + if (onboardingState.isActive) { 29 31 return <Onboarding /> 30 32 } 31 33 return <Component {...props} />
+4 -2
src/view/screens/Settings.tsx
··· 52 52 useSetColorMode, 53 53 useRequireAltTextEnabled, 54 54 useSetRequireAltTextEnabled, 55 + useOnboardingDispatch, 55 56 } from '#/state/shell' 56 57 57 58 // TEMPORARY (APP-700) ··· 70 71 const setMinimalShellMode = useSetMinimalShellMode() 71 72 const requireAltTextEnabled = useRequireAltTextEnabled() 72 73 const setRequireAltTextEnabled = useSetRequireAltTextEnabled() 74 + const onboardingDispatch = useOnboardingDispatch() 73 75 const navigation = useNavigation<NavigationProp>() 74 76 const {isMobile} = useWebMediaQueries() 75 77 const {screen, track} = useAnalytics() ··· 157 159 }, [store]) 158 160 159 161 const onPressResetOnboarding = React.useCallback(async () => { 160 - store.onboarding.reset() 162 + onboardingDispatch({type: 'start'}) 161 163 Toast.show('Onboarding reset') 162 - }, [store]) 164 + }, [onboardingDispatch]) 163 165 164 166 const onPressBuildInfo = React.useCallback(() => { 165 167 Clipboard.setString(
+8 -3
src/view/shell/index.web.tsx
··· 17 17 import {useNavigation} from '@react-navigation/native' 18 18 import {NavigationProp} from 'lib/routes/types' 19 19 import {useAuxClick} from 'lib/hooks/useAuxClick' 20 - import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell' 20 + import { 21 + useIsDrawerOpen, 22 + useSetDrawerOpen, 23 + useOnboardingState, 24 + } from '#/state/shell' 21 25 22 26 const ShellInner = observer(function ShellInnerImpl() { 23 27 const store = useStores() 24 28 const isDrawerOpen = useIsDrawerOpen() 25 29 const setDrawerOpen = useSetDrawerOpen() 30 + const onboardingState = useOnboardingState() 26 31 const {isDesktop, isMobile} = useWebMediaQueries() 27 32 const navigator = useNavigation<NavigationProp>() 28 33 useAuxClick() ··· 34 39 }) 35 40 }, [navigator, store.shell, setDrawerOpen]) 36 41 37 - const showBottomBar = isMobile && !store.onboarding.isActive 42 + const showBottomBar = isMobile && !onboardingState.isActive 38 43 const showSideNavs = 39 - !isMobile && store.session.hasSession && !store.onboarding.isActive 44 + !isMobile && store.session.hasSession && !onboardingState.isActive 40 45 return ( 41 46 <View style={[s.hContentRegion, {overflow: 'hidden'}]}> 42 47 <View style={s.hContentRegion}>