Skip to content

Commit

Permalink
[Seedless Onboarding]: Adjust password form (#2692)
Browse files Browse the repository at this point in the history
* fix: Show error when submitting password form, update wording

* fix: Add event when canceling safe creation form

* fix: Close accordion on form submit
  • Loading branch information
usame-algan authored Oct 26, 2023
1 parent 0f2ba8b commit 748b145
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 61 deletions.
4 changes: 2 additions & 2 deletions public/images/common/bar-chart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/common/PageHeader/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
flex-direction: column;
justify-content: space-between;
background-color: var(--color-background-main);
z-index: 1;
z-index: 2;
width: 100%;
position: sticky !important;
top: calc(var(--header-height) - 76px);
Expand Down
1 change: 1 addition & 0 deletions src/components/new-safe/create/steps/SetNameStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function SetNameStep({
}

const onCancel = () => {
trackEvent(CREATE_SAFE_EVENTS.CANCEL_CREATE_SAFE_FORM)
router.push(AppRoutes.welcome)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,14 @@ const ExportMPCAccountModal = ({ onClose, open }: { onClose: () => void; open: b
)}
{error && <ErrorMessage className={css.modalError}>{error}</ErrorMessage>}

<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center" width="100%">
<Box
display="flex"
flexDirection="row"
justifyContent="space-between"
alignItems="center"
width="100%"
mt={2}
>
<Button variant="outlined" onClick={handleClose}>
Close
</Button>
Expand Down
27 changes: 4 additions & 23 deletions src/components/settings/SecurityLogin/SocialSignerMFA/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { logError } from '@/services/exceptions'
import ErrorCodes from '@/services/exceptions/ErrorCodes'
import { asError } from '@/services/exceptions/utils'
import { type Web3AuthMPCCoreKit } from '@web3auth/mpc-core-kit'
import { showNotification } from '@/store/notificationsSlice'
import { type AppDispatch } from '@/store'

export const isMFAEnabled = (mpcCoreKit: Web3AuthMPCCoreKit) => {
if (!mpcCoreKit) {
Expand All @@ -17,14 +15,13 @@ export const isMFAEnabled = (mpcCoreKit: Web3AuthMPCCoreKit) => {
}

export const enableMFA = async (
dispatch: AppDispatch,
mpcCoreKit: Web3AuthMPCCoreKit,
{
newPassword,
oldPassword,
currentPassword,
}: {
newPassword: string
oldPassword: string | undefined
currentPassword: string | undefined
},
) => {
if (!mpcCoreKit) {
Expand All @@ -33,7 +30,7 @@ export const enableMFA = async (
const securityQuestions = new SecurityQuestionRecovery(mpcCoreKit)
try {
// 1. setup device factor with password recovery
await securityQuestions.upsertPassword(newPassword, oldPassword)
await securityQuestions.upsertPassword(newPassword, currentPassword)
const securityQuestionFactor = await securityQuestions.recoverWithPassword(newPassword)
if (!securityQuestionFactor) {
throw Error('Could not recover using the new password recovery')
Expand All @@ -46,25 +43,9 @@ export const enableMFA = async (
}

await mpcCoreKit.commitChanges()

dispatch(
showNotification({
variant: 'success',
groupKey: 'global-upsert-password',
message: 'Successfully created or updated password',
}),
)
} catch (e) {
const error = asError(e)
logError(ErrorCodes._304, error.message)

// TODO: Check if we should use a notification or show an error inside the form
dispatch(
showNotification({
variant: 'error',
groupKey: 'global-upsert-password',
message: 'Failed to create or update password',
}),
)
throw error
}
}
98 changes: 64 additions & 34 deletions src/components/settings/SecurityLogin/SocialSignerMFA/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
FormControl,
SvgIcon,
Divider,
Alert,
} from '@mui/material'
import { MPC_WALLET_EVENTS } from '@/services/analytics/events/mpcWallet'
import { useState, useMemo, type ChangeEvent } from 'react'
Expand All @@ -24,17 +25,16 @@ import BarChartIcon from '@/public/images/common/bar-chart.svg'
import ShieldIcon from '@/public/images/common/shield.svg'
import ShieldOffIcon from '@/public/images/common/shield-off.svg'
import useMPC from '@/hooks/wallets/mpc/useMPC'
import { useAppDispatch } from '@/store'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'

enum PasswordFieldNames {
oldPassword = 'oldPassword',
currentPassword = 'currentPassword',
newPassword = 'newPassword',
confirmPassword = 'confirmPassword',
}

type PasswordFormData = {
[PasswordFieldNames.oldPassword]: string | undefined
[PasswordFieldNames.currentPassword]: string | undefined
[PasswordFieldNames.newPassword]: string
[PasswordFieldNames.confirmPassword]: string
}
Expand All @@ -50,7 +50,9 @@ const strongPassword = new RegExp('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-
// At least 9 characters, one lowercase, one uppercase, one number, one symbol
const mediumPassword = new RegExp('((?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{9,}))')

export const _getPasswordStrength = (value: string): PasswordStrength => {
export const _getPasswordStrength = (value: string): PasswordStrength | undefined => {
if (value === '') return undefined

if (strongPassword.test(value)) {
return PasswordStrength.strong
}
Expand Down Expand Up @@ -78,15 +80,16 @@ const passwordStrengthMap = {
} as const

const SocialSignerMFA = () => {
const dispatch = useAppDispatch()
const mpcCoreKit = useMPC()
const [passwordStrength, setPasswordStrength] = useState<PasswordStrength>(PasswordStrength.weak)
const [passwordStrength, setPasswordStrength] = useState<PasswordStrength>()
const [submitError, setSubmitError] = useState<string>()
const [open, setOpen] = useState<boolean>(false)

const formMethods = useForm<PasswordFormData>({
mode: 'all',
defaultValues: {
[PasswordFieldNames.confirmPassword]: '',
[PasswordFieldNames.oldPassword]: undefined,
[PasswordFieldNames.currentPassword]: undefined,
[PasswordFieldNames.newPassword]: '',
},
})
Expand All @@ -103,16 +106,29 @@ const SocialSignerMFA = () => {
const onSubmit = async (data: PasswordFormData) => {
if (!mpcCoreKit) return

await enableMFA(dispatch, mpcCoreKit, data)
try {
await enableMFA(mpcCoreKit, data)
onReset()
setOpen(false)
} catch (e) {
setSubmitError('The password you entered is incorrect. Please try again.')
}
}

const onReset = () => {
reset()
setPasswordStrength(PasswordStrength.weak)
setPasswordStrength(undefined)
setSubmitError(undefined)
}

const toggleAccordion = () => {
setOpen((prev) => !prev)
}

const confirmPassword = watch(PasswordFieldNames.confirmPassword)
const passwordsMatch = watch(PasswordFieldNames.newPassword) === confirmPassword && confirmPassword !== ''
const newPassword = watch(PasswordFieldNames.newPassword)
const passwordsEmpty = confirmPassword === '' && newPassword === ''
const passwordsMatch = newPassword === confirmPassword

const isSubmitDisabled =
!passwordsMatch ||
Expand All @@ -128,7 +144,7 @@ const SocialSignerMFA = () => {
Protect your social login signer with a password. It will be used to restore access in another browser or on
another device.
</Typography>
<Accordion>
<Accordion expanded={open} defaultExpanded={false} onChange={toggleAccordion}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Box display="flex" alignItems="center" gap={1}>
<SvgIcon component={CheckIcon} sx={{ color: isPasswordSet ? 'success.main' : 'border.light' }} />
Expand All @@ -140,13 +156,12 @@ const SocialSignerMFA = () => {
<Grid item container xs={12} md={7} gap={3} p={3}>
{isPasswordSet && (
<>
<Typography>You already have a password setup.</Typography>
<FormControl fullWidth>
<PasswordInput
name={PasswordFieldNames.oldPassword}
placeholder="Old password"
label="Old password"
helperText={formState.errors[PasswordFieldNames.oldPassword]?.message}
name={PasswordFieldNames.currentPassword}
placeholder="Current password"
label="Current password"
helperText={formState.errors[PasswordFieldNames.currentPassword]?.message}
required
/>
</FormControl>
Expand Down Expand Up @@ -174,10 +189,16 @@ const SocialSignerMFA = () => {
alignItems="center"
gap={1}
mt={1}
className={css[passwordStrengthMap[passwordStrength].className]}
className={
passwordStrength !== undefined
? css[passwordStrengthMap[passwordStrength].className]
: css.defaultPassword
}
>
<BarChartIcon />
{passwordStrengthMap[passwordStrength].label} password
{passwordStrength !== undefined
? `${passwordStrengthMap[passwordStrength].label} password`
: 'Password strength'}
</Typography>
<Typography variant="body2" color="text.secondary" mt={1}>
Include at least 9 or more characters, a number, an uppercase letter and a symbol
Expand All @@ -187,8 +208,8 @@ const SocialSignerMFA = () => {
<FormControl fullWidth>
<PasswordInput
name={PasswordFieldNames.confirmPassword}
placeholder="Confirm new password"
label="Confirm new password"
placeholder="Confirm password"
label="Confirm password"
helperText={formState.errors[PasswordFieldNames.confirmPassword]?.message}
required
/>
Expand All @@ -198,9 +219,19 @@ const SocialSignerMFA = () => {
alignItems="center"
gap={1}
mt={1}
className={passwordsMatch ? css.passwordsMatch : css.passwordsNoMatch}
className={
passwordsEmpty
? css.passwordsShouldMatch
: passwordsMatch
? css.passwordsMatch
: css.passwordsNoMatch
}
>
{passwordsMatch ? (
{passwordsEmpty ? (
<>
<ShieldOffIcon /> Passwords should match
</>
) : passwordsMatch ? (
<>
<ShieldIcon /> Passwords match
</>
Expand All @@ -212,19 +243,18 @@ const SocialSignerMFA = () => {
</Typography>
</FormControl>

<Button variant="text" onClick={onReset} disabled={!formState.isDirty}>
Cancel
</Button>
<Track {...MPC_WALLET_EVENTS.UPSERT_PASSWORD}>
<Button
sx={{ justifySelf: 'flex-end', marginLeft: 'auto', fontSize: '14px' }}
disabled={isSubmitDisabled}
type="submit"
variant="contained"
>
{isPasswordSet ? 'Change' : 'Create'} Password
{submitError && <Alert severity="error">{submitError}</Alert>}

<Box display="flex" justifyContent="space-between" width={1}>
<Button sx={{ fontSize: '14px' }} variant="text" onClick={onReset} disabled={!formState.isDirty}>
Cancel
</Button>
</Track>
<Track {...MPC_WALLET_EVENTS.UPSERT_PASSWORD}>
<Button sx={{ fontSize: '14px' }} disabled={isSubmitDisabled} type="submit" variant="contained">
{isPasswordSet ? 'Change' : 'Create'} Password
</Button>
</Track>
</Box>
</Grid>
<Grid item xs={12} md={5} p={3} sx={{ borderLeft: '1px solid #DCDEE0' }}>
<Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@
margin-bottom: 0;
}

.defaultPassword,
.passwordsShouldMatch {
color: var(--color-border-main);
}

.passwordsShouldMatch svg path {
stroke: var(--color-border-main);
}

.defaultPassword svg path {
stroke: var(--color-border-main);
}

.weakPassword {
color: var(--color-error-main);
}
Expand Down
4 changes: 4 additions & 0 deletions src/services/analytics/events/createLoadSafe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export const CREATE_SAFE_EVENTS = {
action: 'Retry Safe creation',
category: CREATE_SAFE_CATEGORY,
},
CANCEL_CREATE_SAFE_FORM: {
action: 'Cancel safe creation form',
category: CREATE_SAFE_CATEGORY,
},
CANCEL_CREATE_SAFE: {
event: EventType.META,
action: 'Cancel Safe creation',
Expand Down
4 changes: 4 additions & 0 deletions src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ input[type='number'] {
stroke: var(--color-border-main);
}

.illustration-very-light-stroke {
stroke: var(--color-border-light);
}

.illustration-background-stroke {
stroke: var(--color-logo-background);
}
Expand Down

0 comments on commit 748b145

Please sign in to comment.