[Perf - part 1] Hoist service config query (#8812)

This commit is contained in:
Samuel Newman
2025-08-14 01:13:51 +03:00
committed by GitHub
parent b2c56cbd6d
commit 7a334362ae
12 changed files with 119 additions and 88 deletions
+64
View File
@@ -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,
})
}
+20 -2
View File
@@ -21,6 +21,8 @@ TrendingContext.displayName = 'TrendingContext'
const LiveNowContext = createContext<LiveNowContext | null>(null)
LiveNowContext.displayName = 'LiveNowContext'
const CheckEmailConfirmedContext = createContext<boolean | null>(null)
export function Provider({children}: {children: React.ReactNode}) {
const langPrefs = useLanguagePrefs()
const {data: config, isLoading: isInitialLoad} = useServiceConfigQuery()
@@ -61,10 +63,16 @@ export function Provider({children}: {children: React.ReactNode}) {
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 (
<TrendingContext.Provider value={trending}>
<LiveNowContext.Provider value={liveNow}>
{children}
<CheckEmailConfirmedContext.Provider value={checkEmailConfirmed}>
{children}
</CheckEmailConfirmedContext.Provider>
</LiveNowContext.Provider>
</TrendingContext.Provider>
)
@@ -78,7 +86,7 @@ export function useLiveNowConfig() {
const ctx = useContext(LiveNowContext)
if (!ctx) {
throw new Error(
'useLiveNowConfig must be used within a LiveNowConfigProvider',
'useLiveNowConfig must be used within a ServiceConfigManager',
)
}
return ctx
@@ -88,3 +96,13 @@ export function useCanGoLive(did?: string) {
const config = useLiveNowConfig()
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
}