From 70dbc94766b8f3c9d2c1b815fad66232523d28ab Mon Sep 17 00:00:00 2001
From: Samuel Newman <mozzius@protonmail.com>
Date: Wed, 23 Apr 2025 19:22:08 +0300
Subject: Modernise change email flow (#8106)

* use new verify email dialog in 2fa flow

* alf change email flow

* Fallback change email dialog

* Update ChangeEmailDialog.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Update ChangeEmailDialog.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Update ChangeEmailDialog.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Update ChangeEmailDialog.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Update ChangeEmailDialog.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Update ChangeEmailDialog.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Update ChangeEmailDialog.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Update ChangeEmailDialog.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Update Email2FAToggle.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* don't use existing email as default value

* increase max width of email dialogs

* Use ALF verify email dialog for reminder (#5924)

* use new verify email dialog for reminder

* style tweaks, improve web

* add a lil toast

* Apply suggestions from code review

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Ditch close and push up image

---------

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
Co-authored-by: Eric Bailey <git@esb.lol>

* delete old change/verify email modals (#8122)

(cherry picked from commit fceb655b3bacad1bce210810234137b7233d263d)

* Translate email placeholder

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Align copy

* Clean up error handling

---------

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
Co-authored-by: Eric Bailey <git@esb.lol>
---
 src/components/dialogs/VerifyEmailDialog.tsx | 159 +++++++++++++++++++--------
 1 file changed, 111 insertions(+), 48 deletions(-)

(limited to 'src/components/dialogs/VerifyEmailDialog.tsx')

diff --git a/src/components/dialogs/VerifyEmailDialog.tsx b/src/components/dialogs/VerifyEmailDialog.tsx
index ced9171ce..b8d1cd192 100644
--- a/src/components/dialogs/VerifyEmailDialog.tsx
+++ b/src/components/dialogs/VerifyEmailDialog.tsx
@@ -1,86 +1,115 @@
-import React from 'react'
+import {useState} from 'react'
 import {View} from 'react-native'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
 import {cleanError} from '#/lib/strings/errors'
 import {logger} from '#/logger'
-import {useModalControls} from '#/state/modals'
 import {useAgent, useSession} from '#/state/session'
 import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
-import {atoms as a, useBreakpoints} from '#/alf'
+import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
 import {Button, ButtonText} from '#/components/Button'
 import * as Dialog from '#/components/Dialog'
 import * as TextField from '#/components/forms/TextField'
+import {Envelope_Filled_Stroke2_Corner0_Rounded as EnvelopeIcon} from '#/components/icons/Envelope'
 import {InlineLinkText} from '#/components/Link'
 import {Loader} from '#/components/Loader'
 import {Text} from '#/components/Typography'
+import {ChangeEmailDialog} from './ChangeEmailDialog'
 
 export function VerifyEmailDialog({
   control,
   onCloseWithoutVerifying,
   onCloseAfterVerifying,
   reasonText,
+  changeEmailControl,
+  reminder,
 }: {
   control: Dialog.DialogControlProps
   onCloseWithoutVerifying?: () => void
   onCloseAfterVerifying?: () => void
   reasonText?: string
+  /**
+   * if a changeEmailControl for a ChangeEmailDialog is not provided,
+   * this component will create one for you. Using this prop
+   * helps reduce duplication, since these dialogs are often used together.
+   */
+  changeEmailControl?: Dialog.DialogControlProps
+  reminder?: boolean
 }) {
   const agent = useAgent()
+  const fallbackChangeEmailControl = Dialog.useDialogControl()
 
-  const [didVerify, setDidVerify] = React.useState(false)
+  const [didVerify, setDidVerify] = useState(false)
 
   return (
-    <Dialog.Outer
-      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
+    <>
+      <Dialog.Outer
         control={control}
-        setDidVerify={setDidVerify}
-        reasonText={reasonText}
-      />
-    </Dialog.Outer>
+        onClose={async () => {
+          if (!didVerify) {
+            onCloseWithoutVerifying?.()
+            return
+          }
+
+          try {
+            await agent.resumeSession(agent.session!)
+            onCloseAfterVerifying?.()
+          } catch (e: unknown) {
+            logger.error(String(e))
+            return
+          }
+        }}>
+        <Dialog.Handle />
+        <Inner
+          setDidVerify={setDidVerify}
+          reasonText={reasonText}
+          changeEmailControl={changeEmailControl ?? fallbackChangeEmailControl}
+          reminder={reminder}
+        />
+      </Dialog.Outer>
+      {!changeEmailControl && (
+        <ChangeEmailDialog
+          control={fallbackChangeEmailControl}
+          verifyEmailControl={control}
+        />
+      )}
+    </>
   )
 }
 
 export function Inner({
-  control,
   setDidVerify,
   reasonText,
+  changeEmailControl,
+  reminder,
 }: {
-  control: Dialog.DialogControlProps
   setDidVerify: (value: boolean) => void
   reasonText?: string
+  changeEmailControl: Dialog.DialogControlProps
+  reminder?: boolean
 }) {
+  const control = Dialog.useDialogContext()
   const {_} = useLingui()
   const {currentAccount} = useSession()
   const agent = useAgent()
-  const {openModal} = useModalControls()
   const {gtMobile} = useBreakpoints()
+  const t = useTheme()
 
-  const [currentStep, setCurrentStep] = React.useState<
-    'StepOne' | 'StepTwo' | 'StepThree'
-  >('StepOne')
-  const [confirmationCode, setConfirmationCode] = React.useState('')
-  const [isProcessing, setIsProcessing] = React.useState(false)
-  const [error, setError] = React.useState('')
+  const [currentStep, setCurrentStep] = useState<
+    'Reminder' | 'StepOne' | 'StepTwo' | 'StepThree'
+  >(reminder ? 'Reminder' : 'StepOne')
+  const [confirmationCode, setConfirmationCode] = useState('')
+  const [isProcessing, setIsProcessing] = useState(false)
+  const [error, setError] = useState('')
 
   const uiStrings = {
+    Reminder: {
+      title: _(msg`Please Verify Your Email`),
+      message: _(
+        msg`Your email has not yet been verified. This is an important security step which we recommend.`,
+      ),
+    },
     StepOne: {
       title: _(msg`Verify Your Email`),
       message: '',
@@ -132,11 +161,20 @@ export function Inner({
   return (
     <Dialog.ScrollableInner
       label={_(msg`Verify email dialog`)}
-      style={[
-        gtMobile ? {width: 'auto', maxWidth: 400, minWidth: 200} : a.w_full,
-      ]}>
-      <Dialog.Close />
+      style={web({maxWidth: 450})}>
       <View style={[a.gap_xl]}>
+        {currentStep === 'Reminder' && (
+          <View
+            style={[
+              a.rounded_sm,
+              a.align_center,
+              a.justify_center,
+              {height: 150},
+              t.atoms.bg_contrast_100,
+            ]}>
+            <EnvelopeIcon width={64} fill="white" />
+          </View>
+        )}
         <View style={[a.gap_sm]}>
           <Text style={[a.font_heavy, a.text_2xl]}>
             {uiStrings[currentStep].title}
@@ -164,7 +202,7 @@ export function Inner({
                       onPress={e => {
                         e.preventDefault()
                         control.close(() => {
-                          openModal({name: 'change-email'})
+                          changeEmailControl.open()
                         })
                         return false
                       }}>
@@ -189,7 +227,7 @@ export function Inner({
                     onPress={e => {
                       e.preventDefault()
                       control.close(() => {
-                        openModal({name: 'change-email'})
+                        changeEmailControl.open()
                       })
                       return false
                     }}>
@@ -219,7 +257,32 @@ export function Inner({
           </View>
         ) : null}
         <View style={[a.gap_sm, gtMobile && [a.flex_row_reverse, a.ml_auto]]}>
-          {currentStep === 'StepOne' ? (
+          {currentStep === 'Reminder' ? (
+            <>
+              <Button
+                label={_(msg`Get started`)}
+                variant="solid"
+                color="primary"
+                size="large"
+                onPress={() => setCurrentStep('StepOne')}>
+                <ButtonText>
+                  <Trans>Get started</Trans>
+                </ButtonText>
+              </Button>
+              <Button
+                label={_(msg`Maybe later`)}
+                accessibilityHint={_(msg`Snoozes the reminder`)}
+                variant="ghost"
+                color="secondary"
+                size="large"
+                disabled={isProcessing}
+                onPress={() => control.close()}>
+                <ButtonText>
+                  <Trans>Maybe later</Trans>
+                </ButtonText>
+              </Button>
+            </>
+          ) : currentStep === 'StepOne' ? (
             <>
               <Button
                 label={_(msg`Send confirmation email`)}
@@ -229,21 +292,21 @@ export function Inner({
                 disabled={isProcessing}
                 onPress={onSendEmail}>
                 <ButtonText>
-                  <Trans>Send Confirmation</Trans>
+                  <Trans>Send confirmation</Trans>
                 </ButtonText>
                 {isProcessing ? (
                   <Loader size="sm" style={[{color: 'white'}]} />
                 ) : null}
               </Button>
               <Button
-                label={_(msg`I Have a Code`)}
+                label={_(msg`I have a code`)}
                 variant="solid"
                 color="secondary"
                 size="large"
                 disabled={isProcessing}
                 onPress={() => setCurrentStep('StepTwo')}>
                 <ButtonText>
-                  <Trans>I Have a Code</Trans>
+                  <Trans>I have a code</Trans>
                 </ButtonText>
               </Button>
             </>
@@ -264,7 +327,7 @@ export function Inner({
                 ) : null}
               </Button>
               <Button
-                label={_(msg`Resend Email`)}
+                label={_(msg`Resend email`)}
                 variant="solid"
                 color="secondary"
                 size="large"
@@ -274,13 +337,13 @@ export function Inner({
                   setCurrentStep('StepOne')
                 }}>
                 <ButtonText>
-                  <Trans>Resend Email</Trans>
+                  <Trans>Resend email</Trans>
                 </ButtonText>
               </Button>
             </>
           ) : currentStep === 'StepThree' ? (
             <Button
-              label={_(msg`Confirm`)}
+              label={_(msg`Close`)}
               variant="solid"
               color="primary"
               size="large"
-- 
cgit 1.4.1

