[Perf - part 1] Hoist service config query (#8812)
This commit is contained in:
+15
-12
@@ -29,6 +29,7 @@ import {Provider as A11yProvider} from '#/state/a11y'
|
|||||||
import {Provider as AgeAssuranceProvider} from '#/state/ageAssurance'
|
import {Provider as AgeAssuranceProvider} from '#/state/ageAssurance'
|
||||||
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
||||||
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
||||||
|
import {Provider as EmailVerificationProvider} from '#/state/email-verification'
|
||||||
import {listenSessionDropped} from '#/state/events'
|
import {listenSessionDropped} from '#/state/events'
|
||||||
import {
|
import {
|
||||||
beginResolveGeolocation,
|
beginResolveGeolocation,
|
||||||
@@ -155,18 +156,20 @@ function InnerApp() {
|
|||||||
<MutedThreadsProvider>
|
<MutedThreadsProvider>
|
||||||
<ProgressGuideProvider>
|
<ProgressGuideProvider>
|
||||||
<ServiceAccountManager>
|
<ServiceAccountManager>
|
||||||
<HideBottomBarBorderProvider>
|
<EmailVerificationProvider>
|
||||||
<GestureHandlerRootView
|
<HideBottomBarBorderProvider>
|
||||||
style={s.h100pct}>
|
<GestureHandlerRootView
|
||||||
<GlobalGestureEventsProvider>
|
style={s.h100pct}>
|
||||||
<IntentDialogProvider>
|
<GlobalGestureEventsProvider>
|
||||||
<TestCtrls />
|
<IntentDialogProvider>
|
||||||
<Shell />
|
<TestCtrls />
|
||||||
<NuxDialogs />
|
<Shell />
|
||||||
</IntentDialogProvider>
|
<NuxDialogs />
|
||||||
</GlobalGestureEventsProvider>
|
</IntentDialogProvider>
|
||||||
</GestureHandlerRootView>
|
</GlobalGestureEventsProvider>
|
||||||
</HideBottomBarBorderProvider>
|
</GestureHandlerRootView>
|
||||||
|
</HideBottomBarBorderProvider>
|
||||||
|
</EmailVerificationProvider>
|
||||||
</ServiceAccountManager>
|
</ServiceAccountManager>
|
||||||
</ProgressGuideProvider>
|
</ProgressGuideProvider>
|
||||||
</MutedThreadsProvider>
|
</MutedThreadsProvider>
|
||||||
|
|||||||
+9
-6
@@ -18,6 +18,7 @@ import {Provider as A11yProvider} from '#/state/a11y'
|
|||||||
import {Provider as AgeAssuranceProvider} from '#/state/ageAssurance'
|
import {Provider as AgeAssuranceProvider} from '#/state/ageAssurance'
|
||||||
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
||||||
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
||||||
|
import {Provider as EmailVerificationProvider} from '#/state/email-verification'
|
||||||
import {listenSessionDropped} from '#/state/events'
|
import {listenSessionDropped} from '#/state/events'
|
||||||
import {
|
import {
|
||||||
beginResolveGeolocation,
|
beginResolveGeolocation,
|
||||||
@@ -136,12 +137,14 @@ function InnerApp() {
|
|||||||
<SafeAreaProvider>
|
<SafeAreaProvider>
|
||||||
<ProgressGuideProvider>
|
<ProgressGuideProvider>
|
||||||
<ServiceConfigProvider>
|
<ServiceConfigProvider>
|
||||||
<HideBottomBarBorderProvider>
|
<EmailVerificationProvider>
|
||||||
<IntentDialogProvider>
|
<HideBottomBarBorderProvider>
|
||||||
<Shell />
|
<IntentDialogProvider>
|
||||||
<NuxDialogs />
|
<Shell />
|
||||||
</IntentDialogProvider>
|
<NuxDialogs />
|
||||||
</HideBottomBarBorderProvider>
|
</IntentDialogProvider>
|
||||||
|
</HideBottomBarBorderProvider>
|
||||||
|
</EmailVerificationProvider>
|
||||||
</ServiceConfigProvider>
|
</ServiceConfigProvider>
|
||||||
</ProgressGuideProvider>
|
</ProgressGuideProvider>
|
||||||
</SafeAreaProvider>
|
</SafeAreaProvider>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {type AppBskyActorDefs} from '@atproto/api'
|
|||||||
|
|
||||||
import {useGate} from '#/lib/statsig/statsig'
|
import {useGate} from '#/lib/statsig/statsig'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {STALE} from '#/state/queries'
|
||||||
import {Nux, useNuxs, useResetNuxs, useSaveNux} from '#/state/queries/nuxs'
|
import {Nux, useNuxs, useResetNuxs, useSaveNux} from '#/state/queries/nuxs'
|
||||||
import {
|
import {
|
||||||
usePreferencesQuery,
|
usePreferencesQuery,
|
||||||
@@ -56,7 +57,10 @@ export function useNuxDialogContext() {
|
|||||||
export function NuxDialogs() {
|
export function NuxDialogs() {
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {data: preferences} = usePreferencesQuery()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
const {data: profile} = useProfileQuery({did: currentAccount?.did})
|
const {data: profile} = useProfileQuery({
|
||||||
|
did: currentAccount?.did,
|
||||||
|
staleTime: STALE.INFINITY, // createdAt isn't gonna change
|
||||||
|
})
|
||||||
const onboardingActive = useOnboardingState().isActive
|
const onboardingActive = useOnboardingState().isActive
|
||||||
|
|
||||||
const isLoading =
|
const isLoading =
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import {STALE} from '#/state/queries'
|
|
||||||
import {useServiceConfigQuery} from '#/state/queries/email-verification-required'
|
|
||||||
import {useProfileQuery} from '#/state/queries/profile'
|
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {BSKY_SERVICE} from '../constants'
|
|
||||||
import {getHostnameFromUrl} from '../strings/url-helpers'
|
|
||||||
|
|
||||||
export function useEmail() {
|
|
||||||
const {currentAccount} = useSession()
|
|
||||||
|
|
||||||
const {data: serviceConfig} = useServiceConfigQuery()
|
|
||||||
const {data: profile} = useProfileQuery({
|
|
||||||
did: currentAccount?.did,
|
|
||||||
staleTime: STALE.INFINITY,
|
|
||||||
})
|
|
||||||
|
|
||||||
const checkEmailConfirmed = !!serviceConfig?.checkEmailConfirmed
|
|
||||||
|
|
||||||
// Date set for 11 AM PST on the 18th of November
|
|
||||||
const isNewEnough =
|
|
||||||
!!profile?.createdAt &&
|
|
||||||
Date.parse(profile.createdAt) >= Date.parse('2024-11-18T19:00:00.000Z')
|
|
||||||
|
|
||||||
const isSelfHost =
|
|
||||||
currentAccount &&
|
|
||||||
getHostnameFromUrl(currentAccount.service) !==
|
|
||||||
getHostnameFromUrl(BSKY_SERVICE)
|
|
||||||
|
|
||||||
const needsEmailVerification =
|
|
||||||
!isSelfHost &&
|
|
||||||
checkEmailConfirmed &&
|
|
||||||
!currentAccount?.emailConfirmed &&
|
|
||||||
isNewEnough
|
|
||||||
|
|
||||||
return {needsEmailVerification}
|
|
||||||
}
|
|
||||||
@@ -2,10 +2,10 @@ import {useMemo} from 'react'
|
|||||||
import {Trans} from '@lingui/macro'
|
import {Trans} from '@lingui/macro'
|
||||||
|
|
||||||
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
|
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
|
||||||
import {useOpenComposer as rootUseOpenComposer} from '#/state/shell/composer'
|
import {useOpenComposer as useRootOpenComposer} from '#/state/shell/composer'
|
||||||
|
|
||||||
export function useOpenComposer() {
|
export function useOpenComposer() {
|
||||||
const {openComposer} = rootUseOpenComposer()
|
const {openComposer} = useRootOpenComposer()
|
||||||
const requireEmailVerification = useRequireEmailVerification()
|
const requireEmailVerification = useRequireEmailVerification()
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {Keyboard} from 'react-native'
|
import {Keyboard} from 'react-native'
|
||||||
|
|
||||||
import {useEmail} from '#/lib/hooks/useEmail'
|
import {useEmail} from '#/state/email-verification'
|
||||||
import {useRequireAuth, useSession} from '#/state/session'
|
import {useRequireAuth, useSession} from '#/state/session'
|
||||||
import {useCloseAllActiveElements} from '#/state/util'
|
import {useCloseAllActiveElements} from '#/state/util'
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
} from '@react-navigation/native'
|
} from '@react-navigation/native'
|
||||||
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||||
|
|
||||||
import {useEmail} from '#/lib/hooks/useEmail'
|
|
||||||
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
|
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
import {
|
import {
|
||||||
@@ -24,6 +23,7 @@ import {
|
|||||||
} from '#/lib/routes/types'
|
} from '#/lib/routes/types'
|
||||||
import {isWeb} from '#/platform/detection'
|
import {isWeb} from '#/platform/detection'
|
||||||
import {type Shadow, useMaybeProfileShadow} from '#/state/cache/profile-shadow'
|
import {type Shadow, useMaybeProfileShadow} from '#/state/cache/profile-shadow'
|
||||||
|
import {useEmail} from '#/state/email-verification'
|
||||||
import {ConvoProvider, isConvoActive, useConvo} from '#/state/messages/convo'
|
import {ConvoProvider, isConvoActive, useConvo} from '#/state/messages/convo'
|
||||||
import {ConvoStatus} from '#/state/messages/convo/types'
|
import {ConvoStatus} from '#/state/messages/convo/types'
|
||||||
import {useCurrentConvoId} from '#/state/messages/current-convo-id'
|
import {useCurrentConvoId} from '#/state/messages/current-convo-id'
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import Graphemer from 'graphemer'
|
|||||||
|
|
||||||
import {HITSLOP_10, MAX_DM_GRAPHEME_LENGTH} from '#/lib/constants'
|
import {HITSLOP_10, MAX_DM_GRAPHEME_LENGTH} from '#/lib/constants'
|
||||||
import {useHaptics} from '#/lib/haptics'
|
import {useHaptics} from '#/lib/haptics'
|
||||||
import {useEmail} from '#/lib/hooks/useEmail'
|
|
||||||
import {isIOS, isWeb} from '#/platform/detection'
|
import {isIOS, isWeb} from '#/platform/detection'
|
||||||
|
import {useEmail} from '#/state/email-verification'
|
||||||
import {
|
import {
|
||||||
useMessageDraft,
|
useMessageDraft,
|
||||||
useSaveMessageDraft,
|
useSaveMessageDraft,
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import {useLingui} from '@lingui/react'
|
|||||||
import {StackActions, useNavigation} from '@react-navigation/native'
|
import {StackActions, useNavigation} from '@react-navigation/native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useEmail} from '#/lib/hooks/useEmail'
|
|
||||||
import {type NavigationProp} from '#/lib/routes/types'
|
import {type NavigationProp} from '#/lib/routes/types'
|
||||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||||
|
import {useEmail} from '#/state/email-verification'
|
||||||
import {useAcceptConversation} from '#/state/queries/messages/accept-conversation'
|
import {useAcceptConversation} from '#/state/queries/messages/accept-conversation'
|
||||||
import {precacheConvoQuery} from '#/state/queries/messages/conversation'
|
import {precacheConvoQuery} from '#/state/queries/messages/conversation'
|
||||||
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
|
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import {createContext, useContext, useMemo} from 'react'
|
||||||
|
|
||||||
|
import {BSKY_SERVICE} from '#/lib/constants'
|
||||||
|
import {getHostnameFromUrl} from '#/lib/strings/url-helpers'
|
||||||
|
import {STALE} from '#/state/queries'
|
||||||
|
import {useProfileQuery} from '#/state/queries/profile'
|
||||||
|
import {useCheckEmailConfirmed} from '#/state/service-config'
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
|
|
||||||
|
type EmailVerificationContext = {
|
||||||
|
needsEmailVerification: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmailVerificationContext = createContext<EmailVerificationContext | null>(
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
EmailVerificationContext.displayName = 'EmailVerificationContext'
|
||||||
|
|
||||||
|
export function Provider({children}: {children: React.ReactNode}) {
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
|
const {data: profile} = useProfileQuery({
|
||||||
|
did: currentAccount?.did,
|
||||||
|
staleTime: STALE.INFINITY,
|
||||||
|
})
|
||||||
|
|
||||||
|
const checkEmailConfirmed = useCheckEmailConfirmed()
|
||||||
|
|
||||||
|
// Date set for 11 AM PST on the 18th of November
|
||||||
|
const isNewEnough =
|
||||||
|
!!profile?.createdAt &&
|
||||||
|
Date.parse(profile.createdAt) >= Date.parse('2024-11-18T19:00:00.000Z')
|
||||||
|
|
||||||
|
const isSelfHost =
|
||||||
|
currentAccount &&
|
||||||
|
getHostnameFromUrl(currentAccount.service) !==
|
||||||
|
getHostnameFromUrl(BSKY_SERVICE)
|
||||||
|
|
||||||
|
const needsEmailVerification =
|
||||||
|
!isSelfHost &&
|
||||||
|
checkEmailConfirmed &&
|
||||||
|
!currentAccount?.emailConfirmed &&
|
||||||
|
isNewEnough
|
||||||
|
|
||||||
|
const value = useMemo(
|
||||||
|
() => ({needsEmailVerification}),
|
||||||
|
[needsEmailVerification],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EmailVerificationContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</EmailVerificationContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Provider.displayName = 'EmailVerificationProvider'
|
||||||
|
|
||||||
|
export function useEmail() {
|
||||||
|
const ctx = useContext(EmailVerificationContext)
|
||||||
|
if (!ctx) {
|
||||||
|
throw new Error('useEmail must be used within a EmailVerificationProvider')
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import {useQuery} from '@tanstack/react-query'
|
|
||||||
|
|
||||||
interface ServiceConfig {
|
|
||||||
checkEmailConfirmed: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useServiceConfigQuery() {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: ['service-config'],
|
|
||||||
queryFn: async () => {
|
|
||||||
const res = await fetch(
|
|
||||||
'https://api.bsky.app/xrpc/app.bsky.unspecced.getConfig',
|
|
||||||
)
|
|
||||||
if (!res.ok) {
|
|
||||||
return {
|
|
||||||
checkEmailConfirmed: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = await res.json()
|
|
||||||
return json as ServiceConfig
|
|
||||||
},
|
|
||||||
staleTime: 5 * 60 * 1000,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -21,6 +21,8 @@ TrendingContext.displayName = 'TrendingContext'
|
|||||||
const LiveNowContext = createContext<LiveNowContext | null>(null)
|
const LiveNowContext = createContext<LiveNowContext | null>(null)
|
||||||
LiveNowContext.displayName = 'LiveNowContext'
|
LiveNowContext.displayName = 'LiveNowContext'
|
||||||
|
|
||||||
|
const CheckEmailConfirmedContext = createContext<boolean | null>(null)
|
||||||
|
|
||||||
export function Provider({children}: {children: React.ReactNode}) {
|
export function Provider({children}: {children: React.ReactNode}) {
|
||||||
const langPrefs = useLanguagePrefs()
|
const langPrefs = useLanguagePrefs()
|
||||||
const {data: config, isLoading: isInitialLoad} = useServiceConfigQuery()
|
const {data: config, isLoading: isInitialLoad} = useServiceConfigQuery()
|
||||||
@@ -61,10 +63,16 @@ export function Provider({children}: {children: React.ReactNode}) {
|
|||||||
|
|
||||||
const liveNow = useMemo<LiveNowContext>(() => config?.liveNow ?? [], [config])
|
const liveNow = useMemo<LiveNowContext>(() => config?.liveNow ?? [], [config])
|
||||||
|
|
||||||
|
// probably true, so default to true when loading
|
||||||
|
// if the call fails, the query will set it to false for us
|
||||||
|
const checkEmailConfirmed = config?.checkEmailConfirmed ?? true
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TrendingContext.Provider value={trending}>
|
<TrendingContext.Provider value={trending}>
|
||||||
<LiveNowContext.Provider value={liveNow}>
|
<LiveNowContext.Provider value={liveNow}>
|
||||||
{children}
|
<CheckEmailConfirmedContext.Provider value={checkEmailConfirmed}>
|
||||||
|
{children}
|
||||||
|
</CheckEmailConfirmedContext.Provider>
|
||||||
</LiveNowContext.Provider>
|
</LiveNowContext.Provider>
|
||||||
</TrendingContext.Provider>
|
</TrendingContext.Provider>
|
||||||
)
|
)
|
||||||
@@ -78,7 +86,7 @@ export function useLiveNowConfig() {
|
|||||||
const ctx = useContext(LiveNowContext)
|
const ctx = useContext(LiveNowContext)
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'useLiveNowConfig must be used within a LiveNowConfigProvider',
|
'useLiveNowConfig must be used within a ServiceConfigManager',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return ctx
|
return ctx
|
||||||
@@ -88,3 +96,13 @@ export function useCanGoLive(did?: string) {
|
|||||||
const config = useLiveNowConfig()
|
const config = useLiveNowConfig()
|
||||||
return !!config.find(cfg => cfg.did === did)
|
return !!config.find(cfg => cfg.did === did)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useCheckEmailConfirmed() {
|
||||||
|
const ctx = useContext(CheckEmailConfirmedContext)
|
||||||
|
if (ctx === null) {
|
||||||
|
throw new Error(
|
||||||
|
'useCheckEmailConfirmed must be used within a ServiceConfigManager',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user