Skip to content

Commit

Permalink
Created change passcode page
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivan-Mahda committed Oct 17, 2024
1 parent 33e831e commit d46ad0f
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.change-passcode-x {
.divider {
display: block;
margin: rem(16px) 0 rem(24px) 0;
border-bottom: 1px solid rgba($color-white, 0.1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, { useRef } from 'react';
import Page from '@popup/popupX/shared/Page';
import { useTranslation } from 'react-i18next';
import Button from '@popup/popupX/shared/Button';
import FormPassword from '@popup/popupX/shared/Form/Password';
import Form, { useForm } from '@popup/popupX/shared/Form/Form';
import { useAtom, useSetAtom } from 'jotai';
import { encryptedSeedPhraseAtom, sessionPasscodeAtom } from '@popup/store/settings';
import { addToastAtom } from '@popup/state';
import { SubmitHandler, UseFormGetValues, Validate } from 'react-hook-form';
import { decrypt, encrypt } from '@shared/utils/crypto';

type FormValues = {
currentPasscode: string;
newPasscode: string;
newPasscodeRepeated: string;
};

export default function ChangePasscode() {
const { t } = useTranslation('x', { keyPrefix: 'passcode' });

const formRef = useRef<HTMLFormElement>(null);
const form = useForm<FormValues>();
const [encryptedSeedPhrase, setEncryptedSeedPhrase] = useAtom(encryptedSeedPhraseAtom);
const [passcode, setPasscode] = useAtom(sessionPasscodeAtom);
const addToast = useSetAtom(addToastAtom);

if (passcode.loading || !passcode.value || encryptedSeedPhrase.loading) {
return null;
}

const handleSubmit: SubmitHandler<FormValues> = async (vs) => {
if (encryptedSeedPhrase.value && passcode.value) {
const decryptedSeedPhrase = await decrypt(encryptedSeedPhrase.value, passcode.value);
const encryptedSeedPhraseWithNewPasscode = await encrypt(decryptedSeedPhrase, vs.newPasscode);

setEncryptedSeedPhrase(encryptedSeedPhraseWithNewPasscode);
setPasscode(vs.newPasscode);
addToast(t('passcodeUpdated'));
}
};

function validateCurrentPasscode(): Validate<string> {
return (currentPasscode) => (currentPasscode !== passcode.value ? t('incorrectPasscode') : undefined);
}

function validateNewPasscode(getValues: UseFormGetValues<FormValues>): Validate<string> {
return (newPasscodeRepeated) =>
getValues().newPasscode !== newPasscodeRepeated ? t('form.passcodeMismatch') : undefined;
}

return (
<Page className="change-passcode-x">
<Page.Top heading={t('changePasscode')} />
<Page.Main>
<Form
id="test-form"
onSubmit={handleSubmit}
className="change-passcode-page__form"
formMethods={form}
ref={formRef}
>
{(f) => {
return (
<>
<FormPassword
control={f.control}
name="currentPasscode"
label={t('labels.currentPasscode')}
rules={{
required: t('form.passcodeRequired'),
validate: validateCurrentPasscode(),
}}
/>
<span className="divider" />
<FormPassword
control={f.control}
name="newPasscode"
label={t('labels.newPasscode')}
rules={{
required: t('form.passcodeRequired'),
minLength: {
value: 6,
message: t('form.passcodeMinLength'),
},
}}
/>
<FormPassword
control={f.control}
name="newPasscodeRepeated"
label={t('labels.newPasscodeRepeated')}
rules={{ validate: validateNewPasscode(f.getValues) }}
/>
</>
);
}}
</Form>
</Page.Main>
<Page.Footer>
<Button.Main
form="test-form"
type="submit"
label={t('changePasscode')}
disabled={form.formState.isSubmitting}
/>
</Page.Footer>
</Page>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const t = {
changePasscode: 'Change passcode',
incorrectPasscode: 'Incorrect passcode',
passcodeUpdated: 'Successfully updated passcode',
labels: {
currentPasscode: 'Enter current passcode',
newPasscode: 'Enter new passcode',
newPasscodeRepeated: 'Confirm new passcode',
},
form: {
passcodeRequired: 'A passcode must be entered',
passcodeMismatch: 'Passcode does not match',
passcodeMinLength: 'Passcode must be at least 6 characters',
},
};

export default t;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ChangePasscode';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Text from '@popup/popupX/shared/Text';

export type ButtonProps = Pick<
ButtonHTMLAttributes<HTMLButtonElement>,
'type' | 'children' | 'disabled' | 'className' | 'onClick' | 'onMouseUp' | 'tabIndex'
'type' | 'children' | 'disabled' | 'className' | 'onClick' | 'onMouseUp' | 'tabIndex' | 'form'
>;

type PolymorphicProps<A extends ElementType = 'button'> = PolymorphicComponentProps<A, ButtonProps>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@

@include when-valid {
.form-input__field:where(:focus) {
border-color: green; /* ToDo get color */
border-color: $color-green-success;
}
}

@include when-invalid {
.form-input__field {
border-color: red; /* ToDo get color */
border-color: $color-red-attention;
}
}

Expand Down
43 changes: 22 additions & 21 deletions packages/browser-wallet/src/popup/popupX/shared/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
useForm as useFormLib,
FormProvider,
} from 'react-hook-form';
import { ClassName } from 'wallet-common-helpers';
import { ClassName, Id } from 'wallet-common-helpers';

const useFormDefaults: Pick<UseFormProps, 'mode'> = {
mode: 'onTouched',
Expand All @@ -23,24 +23,25 @@ export const useForm = <TFormValues extends FieldValues = FieldValues>(
props?: UseFormProps<TFormValues>
): UseFormReturn<TFormValues> => useFormLib<TFormValues>({ ...useFormDefaults, ...props });

type FormProps<TFormValues extends FieldValues = FieldValues> = ClassName & {
/**
* Submit handler, receiving the values of the form as arg.
*/
onSubmit: SubmitHandler<TFormValues>;
/**
* Optional form methods from 'react-hook-form' useForm, if form methods need to be accessed outside of the form.
*/
formMethods?: UseFormReturn<TFormValues>;
/**
* Default values of the entire form.
*/
defaultValues?: DefaultValues<TFormValues>;
/**
* Function passing form methods to function body. Should be used to register form components (using register/control) to the form context.
*/
children: (methods: UseFormReturn<TFormValues>) => React.ReactNode;
};
type FormProps<TFormValues extends FieldValues = FieldValues> = Id &
ClassName & {
/**
* Submit handler, receiving the values of the form as arg.
*/
onSubmit: SubmitHandler<TFormValues>;
/**
* Optional form methods from 'react-hook-form' useForm, if form methods need to be accessed outside of the form.
*/
formMethods?: UseFormReturn<TFormValues>;
/**
* Default values of the entire form.
*/
defaultValues?: DefaultValues<TFormValues>;
/**
* Function passing form methods to function body. Should be used to register form components (using register/control) to the form context.
*/
children: (methods: UseFormReturn<TFormValues>) => React.ReactNode;
};

/**
* @description
Expand Down Expand Up @@ -68,15 +69,15 @@ type FormProps<TFormValues extends FieldValues = FieldValues> = ClassName & {
const Form = forwardRef(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
<V extends Record<string, any>>(
{ onSubmit, formMethods: external, defaultValues, children, className }: FormProps<V>,
{ onSubmit, formMethods: external, defaultValues, children, className, id }: FormProps<V>,
ref: Ref<HTMLFormElement>
) => {
const internal = useForm<V>({ defaultValues });
const methods = external ?? internal;

return (
<FormProvider {...methods}>
<form className={className} onSubmit={methods.handleSubmit(onSubmit)} ref={ref}>
<form id={id} className={className} onSubmit={methods.handleSubmit(onSubmit)} ref={ref}>
{children(methods)}
</form>
</FormProvider>
Expand Down
2 changes: 2 additions & 0 deletions packages/browser-wallet/src/popup/popupX/shell/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { TokenDetails, TokenDetailsCcd } from '@popup/popupX/pages/TokenDetails'
import IdCards from '@popup/popupX/pages/IdCards';
import Accounts from '@popup/popupX/pages/Accounts';
import SeedPhrase from 'src/popup/popupX/pages/SeedPhrase';
import ChangePasscode from 'src/popup/popupX/pages/ChangePasscode';
import { Web3IdCredentials, Web3IdImport } from '@popup/popupX/pages/Web3Id';
import NetworkSettings from '@popup/popupX/pages/NetworkSettings';
import ConnectNetwork from '@popup/popupX/pages/ConnectNetwork';
Expand Down Expand Up @@ -74,6 +75,7 @@ export default function Routes() {
<Route element={<PrivateKey />} path={relativeRoutes.settings.accounts.privateKey.path} />
</Route>
<Route element={<SeedPhrase />} path={relativeRoutes.settings.seedPhrase.path} />
<Route element={<ChangePasscode />} path={relativeRoutes.settings.passcode.path} />
<Route path={relativeRoutes.settings.web3Id.path}>
<Route index element={<Web3IdCredentials />} />
<Route element={<Web3IdImport />} path={relativeRoutes.settings.web3Id.import.path} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
@import '../pages/ConnectNetwork/ConnectNetwork';
@import '../pages/About/About';
@import '../pages/SeedPhrase/SeedPhrase';
@import '../pages/ChangePasscode/ChangePasscode';
@import '../pages/TransactionLog/TransactionLog';
@import '../pages/Web3Id/Web3Id';
@import '../pages/MainPage/MainPage';
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-wallet/src/popup/shell/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default function Root() {

return (
<Provider>
<MemoryRouter initialEntries={['/walletX/home']}>
<MemoryRouter initialEntries={['/walletX/settings/passcode']}>
<Network>
<Theme>
<AccountInfoListenerContext>
Expand Down
2 changes: 2 additions & 0 deletions packages/browser-wallet/src/popup/shell/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import restore from '@popup/popupX/pages/Restore/i18n/en';
import connectedSites from '@popup/popupX/pages/ConnectedSites/i18n/en';
import privateKey from '@popup/popupX/pages/PrivateKey/i18n/en';
import seedPhrase from '@popup/popupX/pages/SeedPhrase/i18n/en';
import passcode from '@popup/popupX/pages/ChangePasscode/i18n/en';
import network from '@popup/popupX/pages/NetworkSettings/i18n/en';
import connect from '@popup/popupX/pages/ConnectNetwork/i18n/en';
import sharedX from '@popup/popupX/shared/i18n/en';
Expand Down Expand Up @@ -88,6 +89,7 @@ const t = {
connectedSites,
privateKey,
seedPhrase,
passcode,
network,
connect,
sharedX,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type MakeRequired<T, K extends keyof T> = NotOptional<Pick<T, K>> & Omit<
*/
export type EqualRecord<T> = { [P in keyof T]: P };

export type Id = Pick<HTMLAttributes<HTMLElement>, 'id'>;
export type ClassName = Pick<HTMLAttributes<HTMLElement>, 'className'>;
export type Style = Pick<HTMLAttributes<HTMLElement>, 'style'>;

Expand Down

0 comments on commit d46ad0f

Please sign in to comment.