diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/StarterPack/ProfileStarterPacks.tsx | 27 | ||||
-rw-r--r-- | src/components/dialogs/VerifyEmailDialog.tsx | 62 | ||||
-rw-r--r-- | src/components/dms/MessageProfileButton.tsx | 56 | ||||
-rw-r--r-- | src/components/dms/dialogs/NewChatDialog.tsx | 20 | ||||
-rw-r--r-- | src/lib/hooks/useEmail.ts | 19 | ||||
-rw-r--r-- | src/screens/Messages/Conversation.tsx | 26 | ||||
-rw-r--r-- | src/screens/Messages/components/MessageInput.tsx | 11 | ||||
-rw-r--r-- | src/state/queries/email-verification-required.ts | 25 | ||||
-rw-r--r-- | src/view/com/composer/Composer.tsx | 21 | ||||
-rw-r--r-- | src/view/screens/Lists.tsx | 22 | ||||
-rw-r--r-- | src/view/screens/ModerationModlists.tsx | 22 |
11 files changed, 265 insertions, 46 deletions
diff --git a/src/components/StarterPack/ProfileStarterPacks.tsx b/src/components/StarterPack/ProfileStarterPacks.tsx index 00afbdcfe..5f58a19df 100644 --- a/src/components/StarterPack/ProfileStarterPacks.tsx +++ b/src/components/StarterPack/ProfileStarterPacks.tsx @@ -14,6 +14,7 @@ import {InfiniteData, UseInfiniteQueryResult} from '@tanstack/react-query' import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' +import {useEmail} from '#/lib/hooks/useEmail' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {NavigationProp} from '#/lib/routes/types' import {parseStarterPackUri} from '#/lib/strings/starter-pack' @@ -27,6 +28,7 @@ import {LinearGradientBackground} from '#/components/LinearGradientBackground' import {Loader} from '#/components/Loader' import * as Prompt from '#/components/Prompt' import {Default as StarterPackCard} from '#/components/StarterPack/StarterPackCard' +import {VerifyEmailDialog} from '../dialogs/VerifyEmailDialog' import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '../icons/Plus' interface SectionRef { @@ -186,6 +188,9 @@ function Empty() { const followersDialogControl = useDialogControl() const errorDialogControl = useDialogControl() + const {needsEmailVerification} = useEmail() + const verifyEmailControl = useDialogControl() + const [isGenerating, setIsGenerating] = React.useState(false) const {mutate: generateStarterPack} = useGenerateStarterPackMutation({ @@ -249,7 +254,13 @@ function Empty() { color="primary" size="small" disabled={isGenerating} - onPress={confirmDialogControl.open} + onPress={() => { + if (needsEmailVerification) { + verifyEmailControl.open() + } else { + confirmDialogControl.open() + } + }} style={{backgroundColor: 'transparent'}}> <ButtonText style={{color: 'white'}}> <Trans>Make one for me</Trans> @@ -262,7 +273,13 @@ function Empty() { color="primary" size="small" disabled={isGenerating} - onPress={() => navigation.navigate('StarterPackWizard')} + onPress={() => { + if (needsEmailVerification) { + verifyEmailControl.open() + } else { + navigation.navigate('StarterPackWizard') + } + }} style={{ backgroundColor: 'white', borderColor: 'white', @@ -318,6 +335,12 @@ function Empty() { onConfirm={generate} confirmButtonCta={_(msg`Retry`)} /> + <VerifyEmailDialog + reasonText={_( + msg`Before creating a starter pack, you must first verify your email.`, + )} + control={verifyEmailControl} + /> </LinearGradientBackground> ) } diff --git a/src/components/dialogs/VerifyEmailDialog.tsx b/src/components/dialogs/VerifyEmailDialog.tsx index 8dfb9bc49..d4412b6f8 100644 --- a/src/components/dialogs/VerifyEmailDialog.tsx +++ b/src/components/dialogs/VerifyEmailDialog.tsx @@ -18,8 +18,14 @@ import {Text} from '#/components/Typography' export function VerifyEmailDialog({ control, + onCloseWithoutVerifying, + onCloseAfterVerifying, + reasonText, }: { control: Dialog.DialogControlProps + onCloseWithoutVerifying?: () => void + onCloseAfterVerifying?: () => void + reasonText?: string }) { const agent = useAgent() @@ -30,18 +36,24 @@ export function VerifyEmailDialog({ control={control} onClose={async () => { if (!didVerify) { + onCloseWithoutVerifying?.() return } try { await agent.resumeSession(agent.session!) + onCloseAfterVerifying?.() } catch (e: unknown) { logger.error(String(e)) return } }}> <Dialog.Handle /> - <Inner control={control} setDidVerify={setDidVerify} /> + <Inner + control={control} + setDidVerify={setDidVerify} + reasonText={reasonText} + /> </Dialog.Outer> ) } @@ -49,9 +61,11 @@ export function VerifyEmailDialog({ export function Inner({ control, setDidVerify, + reasonText, }: { control: Dialog.DialogControlProps setDidVerify: (value: boolean) => void + reasonText?: string }) { const {_} = useLingui() const {currentAccount} = useSession() @@ -135,26 +149,32 @@ export function Inner({ <Text style={[a.text_md, a.leading_snug]}> {currentStep === 'StepOne' ? ( <> - <Trans> - You'll receive an email at{' '} - <Text style={[a.text_md, a.leading_snug, a.font_bold]}> - {currentAccount?.email} - </Text>{' '} - to verify it's you. - </Trans>{' '} - <InlineLinkText - to="#" - label={_(msg`Change email address`)} - style={[a.text_md, a.leading_snug]} - onPress={e => { - e.preventDefault() - control.close(() => { - openModal({name: 'change-email'}) - }) - return false - }}> - <Trans>Need to change it?</Trans> - </InlineLinkText> + {!reasonText ? ( + <> + <Trans> + You'll receive an email at{' '} + <Text style={[a.text_md, a.leading_snug, a.font_bold]}> + {currentAccount?.email} + </Text>{' '} + to verify it's you. + </Trans>{' '} + <InlineLinkText + to="#" + label={_(msg`Change email address`)} + style={[a.text_md, a.leading_snug]} + onPress={e => { + e.preventDefault() + control.close(() => { + openModal({name: 'change-email'}) + }) + return false + }}> + <Trans>Need to change it?</Trans> + </InlineLinkText> + </> + ) : ( + reasonText + )} </> ) : ( uiStrings[currentStep].message diff --git a/src/components/dms/MessageProfileButton.tsx b/src/components/dms/MessageProfileButton.tsx index 932982d05..22936b4c0 100644 --- a/src/components/dms/MessageProfileButton.tsx +++ b/src/components/dms/MessageProfileButton.tsx @@ -3,14 +3,18 @@ import {View} from 'react-native' import {AppBskyActorDefs} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useNavigation} from '@react-navigation/native' +import {useEmail} from '#/lib/hooks/useEmail' +import {NavigationProp} from '#/lib/routes/types' import {logEvent} from '#/lib/statsig/statsig' import {useMaybeConvoForUser} from '#/state/queries/messages/get-convo-for-members' import {atoms as a, useTheme} from '#/alf' -import {ButtonIcon} from '#/components/Button' +import {Button, ButtonIcon} from '#/components/Button' import {canBeMessaged} from '#/components/dms/util' import {Message_Stroke2_Corner0_Rounded as Message} from '#/components/icons/Message' -import {Link} from '#/components/Link' +import {useDialogControl} from '../Dialog' +import {VerifyEmailDialog} from '../dialogs/VerifyEmailDialog' export function MessageProfileButton({ profile, @@ -19,15 +23,29 @@ export function MessageProfileButton({ }) { const {_} = useLingui() const t = useTheme() + const navigation = useNavigation<NavigationProp>() + const {needsEmailVerification} = useEmail() + const verifyEmailControl = useDialogControl() const {data: convo, isPending} = useMaybeConvoForUser(profile.did) const onPress = React.useCallback(() => { + if (!convo?.id) { + return + } + + if (needsEmailVerification) { + verifyEmailControl.open() + return + } + if (convo && !convo.lastMessage) { logEvent('chat:create', {logContext: 'ProfileHeader'}) } logEvent('chat:open', {logContext: 'ProfileHeader'}) - }, [convo]) + + navigation.navigate('MessagesConversation', {conversation: convo.id}) + }, [needsEmailVerification, verifyEmailControl, convo, navigation]) if (isPending) { // show pending state based on declaration @@ -53,18 +71,26 @@ export function MessageProfileButton({ if (convo) { return ( - <Link - testID="dmBtn" - size="small" - color="secondary" - variant="solid" - shape="round" - label={_(msg`Message ${profile.handle}`)} - to={`/messages/${convo.id}`} - style={[a.justify_center]} - onPress={onPress}> - <ButtonIcon icon={Message} size="md" /> - </Link> + <> + <Button + accessibilityRole="button" + testID="dmBtn" + size="small" + color="secondary" + variant="solid" + shape="round" + label={_(msg`Message ${profile.handle}`)} + style={[a.justify_center]} + onPress={onPress}> + <ButtonIcon icon={Message} size="md" /> + </Button> + <VerifyEmailDialog + reasonText={_( + msg`Before you may message another user, you must first verify your email.`, + )} + control={verifyEmailControl} + /> + </> ) } else { return null diff --git a/src/components/dms/dialogs/NewChatDialog.tsx b/src/components/dms/dialogs/NewChatDialog.tsx index e80fef2d7..f402201a2 100644 --- a/src/components/dms/dialogs/NewChatDialog.tsx +++ b/src/components/dms/dialogs/NewChatDialog.tsx @@ -2,6 +2,7 @@ import React, {useCallback} from 'react' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useEmail} from '#/lib/hooks/useEmail' import {logEvent} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' @@ -9,6 +10,8 @@ import {FAB} from '#/view/com/util/fab/FAB' import * as Toast from '#/view/com/util/Toast' import {useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' +import {useDialogControl} from '#/components/Dialog' +import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {SearchablePeopleList} from './SearchablePeopleList' @@ -21,6 +24,8 @@ export function NewChat({ }) { const t = useTheme() const {_} = useLingui() + const {needsEmailVerification} = useEmail() + const verifyEmailControl = useDialogControl() const {mutate: createChat} = useGetConvoForMembers({ onSuccess: data => { @@ -48,7 +53,13 @@ export function NewChat({ <> <FAB testID="newChatFAB" - onPress={control.open} + onPress={() => { + if (needsEmailVerification) { + verifyEmailControl.open() + } else { + control.open() + } + }} icon={<Plus size="lg" fill={t.palette.white} />} accessibilityRole="button" accessibilityLabel={_(msg`New chat`)} @@ -62,6 +73,13 @@ export function NewChat({ onSelectChat={onCreateChat} /> </Dialog.Outer> + + <VerifyEmailDialog + reasonText={_( + msg`Before you may message another user, you must first verify your email.`, + )} + control={verifyEmailControl} + /> </> ) } diff --git a/src/lib/hooks/useEmail.ts b/src/lib/hooks/useEmail.ts new file mode 100644 index 000000000..6e52846d1 --- /dev/null +++ b/src/lib/hooks/useEmail.ts @@ -0,0 +1,19 @@ +import {useServiceConfigQuery} from '#/state/queries/email-verification-required' +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 isSelfHost = + serviceConfig?.checkEmailConfirmed && + currentAccount && + getHostnameFromUrl(currentAccount.service) !== + getHostnameFromUrl(BSKY_SERVICE) + const needsEmailVerification = !isSelfHost && !currentAccount?.emailConfirmed + + return {needsEmailVerification} +} diff --git a/src/screens/Messages/Conversation.tsx b/src/screens/Messages/Conversation.tsx index e2e646a3d..ee09adaf0 100644 --- a/src/screens/Messages/Conversation.tsx +++ b/src/screens/Messages/Conversation.tsx @@ -4,10 +4,11 @@ import {useKeyboardController} from 'react-native-keyboard-controller' import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useFocusEffect} from '@react-navigation/native' +import {useFocusEffect, useNavigation} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' -import {CommonNavigatorParams} from '#/lib/routes/types' +import {useEmail} from '#/lib/hooks/useEmail' +import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types' import {isWeb} from '#/platform/detection' import {useProfileShadow} from '#/state/cache/profile-shadow' import {ConvoProvider, isConvoActive, useConvo} from '#/state/messages/convo' @@ -19,6 +20,8 @@ import {useSetMinimalShellMode} from '#/state/shell' import {CenteredView} from '#/view/com/util/Views' import {MessagesList} from '#/screens/Messages/components/MessagesList' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' +import {useDialogControl} from '#/components/Dialog' +import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {MessagesListBlockedFooter} from '#/components/dms/MessagesListBlockedFooter' import {MessagesListHeader} from '#/components/dms/MessagesListHeader' import {Error} from '#/components/Error' @@ -161,8 +164,12 @@ function InnerReady({ hasScrolled: boolean setHasScrolled: React.Dispatch<React.SetStateAction<boolean>> }) { + const {_} = useLingui() const convoState = useConvo() + const navigation = useNavigation<NavigationProp>() const recipient = useProfileShadow(recipientUnshadowed) + const verifyEmailControl = useDialogControl() + const {needsEmailVerification} = useEmail() const moderation = React.useMemo(() => { return moderateProfile(recipient, moderationOpts) @@ -179,6 +186,12 @@ function InnerReady({ } }, [moderation]) + React.useEffect(() => { + if (needsEmailVerification) { + verifyEmailControl.open() + } + }, [needsEmailVerification, verifyEmailControl]) + return ( <> <MessagesListHeader @@ -201,6 +214,15 @@ function InnerReady({ } /> )} + <VerifyEmailDialog + reasonText={_( + msg`Before you may message another user, you must first verify your email.`, + )} + control={verifyEmailControl} + onCloseWithoutVerifying={() => { + navigation.navigate('Home') + }} + /> </> ) } diff --git a/src/screens/Messages/components/MessageInput.tsx b/src/screens/Messages/components/MessageInput.tsx index 21d6e574e..8edad6272 100644 --- a/src/screens/Messages/components/MessageInput.tsx +++ b/src/screens/Messages/components/MessageInput.tsx @@ -18,6 +18,7 @@ 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} from '#/platform/detection' import { useMessageDraft, @@ -61,10 +62,15 @@ export function MessageInput({ const [message, setMessage] = React.useState(getDraft) const inputRef = useAnimatedRef<TextInput>() + const {needsEmailVerification} = useEmail() + useSaveMessageDraft(message) useExtractEmbedFromFacets(message, setEmbed) const onSubmit = React.useCallback(() => { + if (needsEmailVerification) { + return + } if (!hasEmbed && message.trim() === '') { return } @@ -84,6 +90,7 @@ export function MessageInput({ inputRef.current?.focus() }, 100) }, [ + needsEmailVerification, hasEmbed, message, clearDraft, @@ -159,6 +166,7 @@ export function MessageInput({ ref={inputRef} hitSlop={HITSLOP_10} animatedProps={animatedProps} + editable={!needsEmailVerification} /> <Pressable accessibilityRole="button" @@ -171,7 +179,8 @@ export function MessageInput({ a.justify_center, {height: 30, width: 30, backgroundColor: t.palette.primary_500}, ]} - onPress={onSubmit}> + onPress={onSubmit} + disabled={needsEmailVerification}> <PaperPlane fill={t.palette.white} style={[a.relative, {left: 1}]} /> </Pressable> </View> diff --git a/src/state/queries/email-verification-required.ts b/src/state/queries/email-verification-required.ts new file mode 100644 index 000000000..94ff5cbc6 --- /dev/null +++ b/src/state/queries/email-verification-required.ts @@ -0,0 +1,25 @@ +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, + }) +} diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 1899966dc..a581cb79e 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -58,6 +58,7 @@ import {EmbeddingDisabledError} from '#/lib/api/resolve' import {until} from '#/lib/async/until' import {MAX_GRAPHEME_LENGTH} from '#/lib/constants' import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED' +import {useEmail} from '#/lib/hooks/useEmail' import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {usePalette} from '#/lib/hooks/usePalette' @@ -110,6 +111,8 @@ import * as Toast from '#/view/com/util/Toast' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, native, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {useDialogControl} from '#/components/Dialog' +import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' @@ -297,6 +300,15 @@ export const ComposePost = ({ } }, [onPressCancel, closeAllDialogs, closeAllModals]) + const {needsEmailVerification} = useEmail() + const emailVerificationControl = useDialogControl() + + useEffect(() => { + if (needsEmailVerification) { + emailVerificationControl.open() + } + }, [needsEmailVerification, emailVerificationControl]) + const missingAltError = useMemo(() => { if (!requireAltTextEnabled) { return @@ -570,6 +582,15 @@ export const ComposePost = ({ const isWebFooterSticky = !isNative && thread.posts.length > 1 return ( <BottomSheetPortalProvider> + <VerifyEmailDialog + control={emailVerificationControl} + onCloseWithoutVerifying={() => { + onClose() + }} + reasonText={_( + msg`Before creating a post, you must first verify your email.`, + )} + /> <KeyboardAvoidingView testID="composePostView" behavior={isIOS ? 'padding' : 'height'} diff --git a/src/view/screens/Lists.tsx b/src/view/screens/Lists.tsx index b79da6d54..f654f2bd9 100644 --- a/src/view/screens/Lists.tsx +++ b/src/view/screens/Lists.tsx @@ -2,9 +2,11 @@ import React from 'react' import {StyleSheet, View} from 'react-native' import {AtUri} from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {Trans} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' +import {useEmail} from '#/lib/hooks/useEmail' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' @@ -16,15 +18,20 @@ import {MyLists} from '#/view/com/lists/MyLists' import {Button} from '#/view/com/util/forms/Button' import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader' import {Text} from '#/view/com/util/text/Text' +import {useDialogControl} from '#/components/Dialog' +import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import * as Layout from '#/components/Layout' type Props = NativeStackScreenProps<CommonNavigatorParams, 'Lists'> export function ListsScreen({}: Props) { + const {_} = useLingui() const pal = usePalette('default') const setMinimalShellMode = useSetMinimalShellMode() const {isMobile} = useWebMediaQueries() const navigation = useNavigation<NavigationProp>() const {openModal} = useModalControls() + const {needsEmailVerification} = useEmail() + const control = useDialogControl() useFocusEffect( React.useCallback(() => { @@ -33,6 +40,11 @@ export function ListsScreen({}: Props) { ) const onPressNewList = React.useCallback(() => { + if (needsEmailVerification) { + control.open() + return + } + openModal({ name: 'create-or-edit-list', purpose: 'app.bsky.graph.defs#curatelist', @@ -46,7 +58,7 @@ export function ListsScreen({}: Props) { } catch {} }, }) - }, [openModal, navigation]) + }, [needsEmailVerification, control, openModal, navigation]) return ( <Layout.Screen testID="listsScreen"> @@ -87,6 +99,12 @@ export function ListsScreen({}: Props) { </View> </SimpleViewHeader> <MyLists filter="curate" style={s.flexGrow1} /> + <VerifyEmailDialog + reasonText={_( + msg`Before creating a list, you must first verify your email.`, + )} + control={control} + /> </Layout.Screen> ) } diff --git a/src/view/screens/ModerationModlists.tsx b/src/view/screens/ModerationModlists.tsx index b147ba502..c623c5376 100644 --- a/src/view/screens/ModerationModlists.tsx +++ b/src/view/screens/ModerationModlists.tsx @@ -2,9 +2,11 @@ import React from 'react' import {View} from 'react-native' import {AtUri} from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {Trans} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' +import {useEmail} from '#/lib/hooks/useEmail' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' @@ -16,15 +18,20 @@ import {MyLists} from '#/view/com/lists/MyLists' import {Button} from '#/view/com/util/forms/Button' import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader' import {Text} from '#/view/com/util/text/Text' +import {useDialogControl} from '#/components/Dialog' +import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import * as Layout from '#/components/Layout' type Props = NativeStackScreenProps<CommonNavigatorParams, 'ModerationModlists'> export function ModerationModlistsScreen({}: Props) { + const {_} = useLingui() const pal = usePalette('default') const setMinimalShellMode = useSetMinimalShellMode() const {isMobile} = useWebMediaQueries() const navigation = useNavigation<NavigationProp>() const {openModal} = useModalControls() + const {needsEmailVerification} = useEmail() + const control = useDialogControl() useFocusEffect( React.useCallback(() => { @@ -33,6 +40,11 @@ export function ModerationModlistsScreen({}: Props) { ) const onPressNewList = React.useCallback(() => { + if (needsEmailVerification) { + control.open() + return + } + openModal({ name: 'create-or-edit-list', purpose: 'app.bsky.graph.defs#modlist', @@ -46,7 +58,7 @@ export function ModerationModlistsScreen({}: Props) { } catch {} }, }) - }, [openModal, navigation]) + }, [needsEmailVerification, control, openModal, navigation]) return ( <Layout.Screen testID="moderationModlistsScreen"> @@ -83,6 +95,12 @@ export function ModerationModlistsScreen({}: Props) { </View> </SimpleViewHeader> <MyLists filter="mod" style={s.flexGrow1} /> + <VerifyEmailDialog + reasonText={_( + msg`Before creating a list, you must first verify your email.`, + )} + control={control} + /> </Layout.Screen> ) } |