diff --git a/src/App.native.tsx b/src/App.native.tsx index 4037eedd27..0b46da9ddf 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -29,6 +29,7 @@ import {Provider as A11yProvider} from '#/state/a11y' import {Provider as AgeAssuranceProvider} from '#/state/ageAssurance' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as DialogStateProvider} from '#/state/dialogs' +import {Provider as EmailVerificationProvider} from '#/state/email-verification' import {listenSessionDropped} from '#/state/events' import { beginResolveGeolocation, @@ -155,18 +156,20 @@ function InnerApp() { - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/App.web.tsx b/src/App.web.tsx index 2897aa3f2f..dfa1e7480f 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -18,6 +18,7 @@ import {Provider as A11yProvider} from '#/state/a11y' import {Provider as AgeAssuranceProvider} from '#/state/ageAssurance' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as DialogStateProvider} from '#/state/dialogs' +import {Provider as EmailVerificationProvider} from '#/state/email-verification' import {listenSessionDropped} from '#/state/events' import { beginResolveGeolocation, @@ -136,12 +137,14 @@ function InnerApp() { - - - - - - + + + + + + + + diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx index 5529893c01..67278eb106 100644 --- a/src/components/dialogs/nuxs/index.tsx +++ b/src/components/dialogs/nuxs/index.tsx @@ -3,6 +3,7 @@ import {type AppBskyActorDefs} from '@atproto/api' import {useGate} from '#/lib/statsig/statsig' import {logger} from '#/logger' +import {STALE} from '#/state/queries' import {Nux, useNuxs, useResetNuxs, useSaveNux} from '#/state/queries/nuxs' import { usePreferencesQuery, @@ -55,7 +56,10 @@ export function useNuxDialogContext() { export function NuxDialogs() { const {currentAccount} = useSession() 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 isLoading = diff --git a/src/lib/hooks/useEmail.ts b/src/lib/hooks/useEmail.ts deleted file mode 100644 index 37573c386d..0000000000 --- a/src/lib/hooks/useEmail.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {STALE} from '#/state/queries' -import {useProfileQuery} from '#/state/queries/profile' -import {useCheckEmailConfirmed} from '#/state/service-config' -import {useSession} from '#/state/session' -import {BSKY_SERVICE} from '../constants' -import {getHostnameFromUrl} from '../strings/url-helpers' - -export function useEmail() { - 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 - - return {needsEmailVerification} -} diff --git a/src/lib/hooks/useRequireEmailVerification.tsx b/src/lib/hooks/useRequireEmailVerification.tsx index 26045847e0..2e5b339783 100644 --- a/src/lib/hooks/useRequireEmailVerification.tsx +++ b/src/lib/hooks/useRequireEmailVerification.tsx @@ -1,7 +1,7 @@ import {useCallback} from 'react' import {Keyboard} from 'react-native' -import {useEmail} from '#/lib/hooks/useEmail' +import {useEmail} from '#/state/email-verification' import {useRequireAuth, useSession} from '#/state/session' import {useCloseAllActiveElements} from '#/state/util' import { diff --git a/src/screens/Messages/Conversation.tsx b/src/screens/Messages/Conversation.tsx index 7f3b53b94d..2e820cc124 100644 --- a/src/screens/Messages/Conversation.tsx +++ b/src/screens/Messages/Conversation.tsx @@ -15,7 +15,6 @@ import { } from '@react-navigation/native' import {type NativeStackScreenProps} from '@react-navigation/native-stack' -import {useEmail} from '#/lib/hooks/useEmail' import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import { @@ -24,6 +23,7 @@ import { } from '#/lib/routes/types' import {isWeb} from '#/platform/detection' import {type Shadow, useMaybeProfileShadow} from '#/state/cache/profile-shadow' +import {useEmail} from '#/state/email-verification' import {ConvoProvider, isConvoActive, useConvo} from '#/state/messages/convo' import {ConvoStatus} from '#/state/messages/convo/types' import {useCurrentConvoId} from '#/state/messages/current-convo-id' diff --git a/src/screens/Messages/components/MessageInput.tsx b/src/screens/Messages/components/MessageInput.tsx index 6cde1d4fec..87f22b2329 100644 --- a/src/screens/Messages/components/MessageInput.tsx +++ b/src/screens/Messages/components/MessageInput.tsx @@ -18,8 +18,8 @@ import Graphemer from 'graphemer' import {HITSLOP_10, MAX_DM_GRAPHEME_LENGTH} from '#/lib/constants' import {useHaptics} from '#/lib/haptics' -import {useEmail} from '#/lib/hooks/useEmail' import {isIOS, isWeb} from '#/platform/detection' +import {useEmail} from '#/state/email-verification' import { useMessageDraft, useSaveMessageDraft, diff --git a/src/screens/Messages/components/RequestButtons.tsx b/src/screens/Messages/components/RequestButtons.tsx index 3490bec0da..560888552b 100644 --- a/src/screens/Messages/components/RequestButtons.tsx +++ b/src/screens/Messages/components/RequestButtons.tsx @@ -5,9 +5,9 @@ import {useLingui} from '@lingui/react' import {StackActions, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' -import {useEmail} from '#/lib/hooks/useEmail' import {type NavigationProp} from '#/lib/routes/types' import {useProfileShadow} from '#/state/cache/profile-shadow' +import {useEmail} from '#/state/email-verification' import {useAcceptConversation} from '#/state/queries/messages/accept-conversation' import {precacheConvoQuery} from '#/state/queries/messages/conversation' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' diff --git a/src/state/email-verification.tsx b/src/state/email-verification.tsx new file mode 100644 index 0000000000..0528d59ea1 --- /dev/null +++ b/src/state/email-verification.tsx @@ -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( + 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 ( + + {children} + + ) +} +Provider.displayName = 'EmailVerificationProvider' + +export function useEmail() { + const ctx = useContext(EmailVerificationContext) + if (!ctx) { + throw new Error('useEmail must be used within a EmailVerificationProvider') + } + return ctx +}