Handle instructions and integrate into 2FA setting

This commit is contained in:
Eric Bailey
2025-04-25 15:57:54 -05:00
parent 90cb1ac41f
commit f4482dd292
4 changed files with 82 additions and 41 deletions
@@ -1,4 +1,4 @@
import {View, TextInputProps} from 'react-native' import {type TextInputProps,View} from 'react-native'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@@ -14,6 +14,7 @@ export function normalizeCode(value: string) {
export function TokenField({ export function TokenField({
value, value,
onChangeText, onChangeText,
onSubmitEditing,
}: Pick<TextInputProps, 'value' | 'onChangeText' | 'onSubmitEditing'>) { }: Pick<TextInputProps, 'value' | 'onChangeText' | 'onSubmitEditing'>) {
const {_} = useLingui() const {_} = useLingui()
@@ -30,6 +31,7 @@ export function TokenField({
placeholder="XXXXX-XXXXX" placeholder="XXXXX-XXXXX"
value={value} value={value}
onChangeText={handleOnChangeText} onChangeText={handleOnChangeText}
onSubmitEditing={onSubmitEditing}
/> />
</TextField.Root> </TextField.Root>
</View> </View>
@@ -21,7 +21,7 @@ import {createStaticClick, InlineLinkText} from '#/components/Link'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Span, Text} from '#/components/Typography' import {Span, Text} from '#/components/Typography'
export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) { export function Verify({config}: {config: Exclude<Screen, {id: 'Update'}>}) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
const {currentAccount} = useSession() const {currentAccount} = useSession()
@@ -96,6 +96,22 @@ export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) {
)} )}
</Text> </Text>
{step === 'default' && (
<>
{config.instructions?.map((int, i) => (
<Text
key={i}
style={[
a.text_sm,
a.leading_snug,
t.atoms.text_contrast_medium,
]}>
{int}
</Text>
))}
</>
)}
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}> <Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
{step === 'sent' ? ( {step === 'sent' ? (
<Trans> <Trans>
@@ -136,6 +152,26 @@ export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) {
)} )}
</View> </View>
{step === 'sent' && (
<>
<Divider />
<Text
style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
<Trans>
Have a code?{' '}
<InlineLinkText
label={_(msg`Enter code`)}
{...createStaticClick(() => {
setStep('token')
})}>
Click here.
</InlineLinkText>
</Trans>
</Text>
</>
)}
{step === 'default' ? ( {step === 'default' ? (
<> <>
{error && <Admonition type="error"> {error} </Admonition>} {error && <Admonition type="error"> {error} </Admonition>}
@@ -155,21 +191,29 @@ export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) {
/> />
</Button> </Button>
<Divider /> {!config.hideInitialCodeButton && (
<>
<Divider />
<Text <Text
style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}> style={[
<Trans> a.text_sm,
Have a code?{' '} a.leading_snug,
<InlineLinkText t.atoms.text_contrast_medium,
label={_(msg`Enter code`)} ]}>
{...createStaticClick(() => { <Trans>
setStep('token') Have a code?{' '}
})}> <InlineLinkText
Click here. label={_(msg`Enter code`)}
</InlineLinkText> {...createStaticClick(() => {
</Trans> setStep('token')
</Text> })}>
Click here.
</InlineLinkText>
</Trans>
</Text>
</>
)}
</> </>
) : step === 'token' ? ( ) : step === 'token' ? (
<> <>
+1 -6
View File
@@ -14,13 +14,8 @@ export type Screen =
} }
| { | {
id: ScreenID.Verify id: ScreenID.Verify
/**
* - default flow
* - new user blockers (x5)
*/
instructions?: ReactNode[] instructions?: ReactNode[]
// onCloseWithoutVerifying, hideInitialCodeButton?: boolean
// onCloseAfterVerifying,
} }
export enum ScreenID { export enum ScreenID {
@@ -1,11 +1,14 @@
import React from 'react' import React from 'react'
import {msg} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useAgent, useSession} from '#/state/session' import {useAgent, useSession} from '#/state/session'
import {useDialogControl} from '#/components/Dialog' import {useDialogControl} from '#/components/Dialog'
import {ChangeEmailDialog} from '#/components/dialogs/ChangeEmailDialog' import {
import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' EmailDialog,
EmailDialogScreenID,
useEmailDialogControl,
} from '#/components/dialogs/EmailDialog'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {DisableEmail2FADialog} from './DisableEmail2FADialog' import {DisableEmail2FADialog} from './DisableEmail2FADialog'
import * as SettingsList from './SettingsList' import * as SettingsList from './SettingsList'
@@ -15,9 +18,8 @@ export function Email2FAToggle() {
const {currentAccount} = useSession() const {currentAccount} = useSession()
const disableDialogControl = useDialogControl() const disableDialogControl = useDialogControl()
const enableDialogControl = useDialogControl() const enableDialogControl = useDialogControl()
const verifyEmailDialogControl = useDialogControl()
const changeEmailDialogControl = useDialogControl()
const agent = useAgent() const agent = useAgent()
const emailDialogControl = useEmailDialogControl()
const enableEmailAuthFactor = React.useCallback(async () => { const enableEmailAuthFactor = React.useCallback(async () => {
if (currentAccount?.email) { if (currentAccount?.email) {
@@ -37,7 +39,16 @@ export function Email2FAToggle() {
disableDialogControl.open() disableDialogControl.open()
} else { } else {
if (!currentAccount.emailConfirmed) { if (!currentAccount.emailConfirmed) {
verifyEmailDialogControl.open() emailDialogControl.open({
id: EmailDialogScreenID.Verify,
hideInitialCodeButton: true,
instructions: [
<Trans key="2fa">
You need to verify your email address before you can enable email
2FA.
</Trans>,
],
})
return return
} }
enableDialogControl.open() enableDialogControl.open()
@@ -45,8 +56,8 @@ export function Email2FAToggle() {
}, [ }, [
currentAccount, currentAccount,
enableDialogControl, enableDialogControl,
verifyEmailDialogControl,
disableDialogControl, disableDialogControl,
emailDialogControl,
]) ])
return ( return (
@@ -59,18 +70,7 @@ export function Email2FAToggle() {
onConfirm={enableEmailAuthFactor} onConfirm={enableEmailAuthFactor}
confirmButtonCta={_(msg`Enable`)} confirmButtonCta={_(msg`Enable`)}
/> />
<VerifyEmailDialog <EmailDialog control={emailDialogControl} />
control={verifyEmailDialogControl}
changeEmailControl={changeEmailDialogControl}
onCloseAfterVerifying={enableDialogControl.open}
reasonText={_(
msg`You need to verify your email address before you can enable email 2FA.`,
)}
/>
<ChangeEmailDialog
control={changeEmailDialogControl}
verifyEmailControl={verifyEmailDialogControl}
/>
<SettingsList.BadgeButton <SettingsList.BadgeButton
label={ label={
currentAccount?.emailAuthFactor ? _(msg`Change`) : _(msg`Enable`) currentAccount?.emailAuthFactor ? _(msg`Change`) : _(msg`Enable`)