Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 102 additions & 41 deletions src/app/pages/CreateAccount.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';

import classNames from 'clsx';
import { AnimatePresence, motion } from 'framer-motion';
import { SubmitHandler, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

Expand All @@ -9,13 +10,16 @@ import FormSubmitButton from 'app/atoms/FormSubmitButton';
import { ACCOUNT_NAME_PATTERN } from 'app/defaults';
import { ReactComponent as ArrowRightIcon } from 'app/icons/arrow-right.svg';
import PageLayout from 'app/layouts/PageLayout';
import { Button } from 'components/Button';
import { useMidenContext, useAllAccounts } from 'lib/miden/front';
import { navigate } from 'lib/woozie';
import { WalletType } from 'screens/onboarding/types';
import SelectAuthScheme from 'screens/onboarding/create-wallet-flow/SelectAuthScheme';
import { AuthScheme, WalletType } from 'screens/onboarding/types';

type FormData = {
name: string;
walletType: WalletType;
authScheme: AuthScheme;
};

const WalletTypeOptions = [
Expand All @@ -31,11 +35,19 @@ const WalletTypeOptions = [
}
];

enum Step {
SelectAccountType = 1,
SelectAuthScheme = 2
}

const SUBMIT_ERROR_TYPE = 'submit-error';

const CreateAccount: FC = () => {
const { t } = useTranslation();
const [step, setStep] = useState<Step>(Step.SelectAccountType);
const [navigationDirection, setNavigationDirection] = useState<'forward' | 'backward'>('forward');
const [selectedWalletType, setSelectedWalletType] = useState<WalletType>(WalletType.OnChain);
const [selectedAuthScheme, setSelectedAuthScheme] = useState<AuthScheme>(AuthScheme.Falcon);
const { createAccount, updateCurrentAccount } = useMidenContext();
const allAccounts = useAllAccounts();

Expand Down Expand Up @@ -79,14 +91,19 @@ const CreateAccount: FC = () => {
setSelectedWalletType(type);
};

const handleContinueToAuthScheme = () => {
setNavigationDirection('forward');
setStep(Step.SelectAuthScheme);
};

const onSubmit = useCallback<SubmitHandler<FormData>>(
async ({ name, walletType }) => {
if (isSubmitting) return;

clearErrors('name');

console.log(selectedAuthScheme);
try {
await createAccount(selectedWalletType, name);
await createAccount(selectedWalletType, selectedAuthScheme, name);
} catch (err: any) {
console.error(err);

Expand All @@ -95,12 +112,12 @@ const CreateAccount: FC = () => {
setError('name', { type: SUBMIT_ERROR_TYPE, message: err.message });
}
},
[isSubmitting, clearErrors, setError, createAccount, selectedWalletType]
[isSubmitting, clearErrors, setError, createAccount, selectedWalletType, selectedAuthScheme]
);

return (
<PageLayout pageTitle={<>{t('createAccount')}</>}>
<div className="w-full max-w-sm mx-auto px-4">
<div className="w-full max-w-sm mx-auto px-4 overflow-auto">
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
{...register('name', {
Expand All @@ -124,42 +141,86 @@ const CreateAccount: FC = () => {
{t('accountNameInputDescription')}
</div>

{/* Wallet Type Selection */}
<div className="mb-8">
<div className="font-medium mb-4" style={{ fontSize: '14px', lineHeight: '20px' }}>
{t('chooseYourAccountType')}
</div>
{WalletTypeOptions.map((option, idx) => (
<div
key={option.id}
className={classNames('flex flex-col border p-4 rounded-lg cursor-pointer', 'w-full', 'mb-4', {
'bg-blue-100': selectedWalletType === option.id // Highlight if selected
})}
onClick={() => handleWalletTypeSelect(option.id)}
>
<div className="flex flex-row justify-between items-center">
<h3 className="font-medium text-base">{option.title}</h3>
<ArrowRightIcon fill="black" height="20px" width="20px" />
</div>
<p className="text-grey-600">{option.description}</p>
</div>
))}
</div>

<FormSubmitButton
className="capitalize w-full justify-center"
loading={isSubmitting}
style={{
fontSize: '18px',
lineHeight: '24px',
paddingLeft: '0.5rem',
paddingRight: '0.5rem',
paddingTop: '12px',
paddingBottom: '12px'
}}
>
{t('createAccount')}
</FormSubmitButton>
<AnimatePresence mode={'wait'} initial={false}>
<motion.div
key={step}
initial="initialState"
animate="animateState"
exit="exitState"
transition={{
type: 'tween',
duration: 0.2
}}
variants={{
initialState: {
x: navigationDirection === 'forward' ? '1vw' : '-1vw',
opacity: 0
},
animateState: {
x: 0,
opacity: 1
},
exitState: {
x: navigationDirection === 'forward' ? '-1vw' : '1vw',
opacity: 0
}
}}
>
{step === Step.SelectAccountType ? (
<>
{/* Wallet Type Selection */}
<div className="mb-8">
<div className="font-medium mb-4" style={{ fontSize: '14px', lineHeight: '20px' }}>
{t('chooseYourAccountType')}
</div>
{WalletTypeOptions.map(option => (
<div
key={option.id}
className={classNames('flex flex-col border p-4 rounded-lg cursor-pointer', 'w-full', 'mb-4', {
'bg-blue-100': selectedWalletType === option.id
})}
onClick={() => handleWalletTypeSelect(option.id)}
>
<div className="flex flex-row justify-between items-center">
<h3 className="font-medium text-base">{option.title}</h3>
<ArrowRightIcon fill="black" height="20px" width="20px" />
</div>
<p className="text-grey-600">{option.description}</p>
</div>
))}
</div>

<Button title={t('continue')} onClick={handleContinueToAuthScheme} className="w-full" />
</>
) : (
<>
<SelectAuthScheme
authScheme={selectedAuthScheme}
setAuthScheme={setSelectedAuthScheme}
onSubmit={() => {}}
onCreateAccountScreen={true}
/>

<div className="flex gap-2 mt-4">
<FormSubmitButton
className="capitalize flex-grow justify-center"
loading={isSubmitting}
style={{
fontSize: '18px',
lineHeight: '24px',
paddingLeft: '0.5rem',
paddingRight: '0.5rem',
paddingTop: '12px',
paddingBottom: '12px'
}}
>
{t('createAccount')}
</FormSubmitButton>
</div>
</>
)}
</motion.div>
</AnimatePresence>
</form>
</div>
</PageLayout>
Expand Down
1 change: 1 addition & 0 deletions src/app/pages/Onboarding.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export enum OnboardingSelectors {
ImportWalletButton = 'Onboarding/ImportWalletButton',
NewSeedPhraseButton = 'Onboarding/NewSeedPhraseButton',
VerifySeedPhraseButton = 'Onboarding/VerifySeedPhraseButton',
SelectAuthSchemePage = 'Onboarding/SelectAuthSchemePage',
ConfirmExistingSeedPhraseButton = 'Onboarding/ConfirmExistingSeedPhraseButton'
}
18 changes: 16 additions & 2 deletions src/app/pages/Welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useWalletStore } from 'lib/store';
import { fetchStateFromBackend } from 'lib/store/hooks/useIntercomSync';
import { navigate, useLocation } from 'lib/woozie';
import { OnboardingFlow } from 'screens/onboarding/navigator';
import { ImportType, OnboardingAction, OnboardingStep, OnboardingType } from 'screens/onboarding/types';
import { AuthScheme, ImportType, OnboardingAction, OnboardingStep, OnboardingType } from 'screens/onboarding/types';

/**
* Wait for the wallet state to become Ready after registration.
Expand All @@ -39,6 +39,7 @@ const Welcome: FC = () => {
const { hash } = useLocation();
const [step, setStep] = useState(OnboardingStep.Welcome);
const [seedPhrase, setSeedPhrase] = useState<string[] | null>(null);
const [authScheme, setAuthScheme] = useState<AuthScheme>(AuthScheme.Falcon);
const [onboardingType, setOnboardingType] = useState<OnboardingType | null>(null);
const [importType, setImportType] = useState<ImportType | null>(null);
const [password, setPassword] = useState<string | null>(null);
Expand All @@ -58,6 +59,7 @@ const Welcome: FC = () => {
try {
await registerWallet(
password,
authScheme,
seedPhraseFormatted,
onboardingType === OnboardingType.Import // might be able to leverage ownMnemonic to determine whther to attempt imports in general
);
Expand All @@ -75,6 +77,7 @@ const Welcome: FC = () => {
}
}, [
password,
authScheme,
seedPhrase,
importedWithFile,
registerWallet,
Expand Down Expand Up @@ -125,6 +128,10 @@ const Welcome: FC = () => {
case 'verify-seed-phrase':
navigate('/#verify-seed-phrase');
break;
case 'select-auth-scheme':
setAuthScheme(action.payload);
navigate('/#select-auth-scheme');
break;
case 'create-password':
navigate('/#create-password');
break;
Expand Down Expand Up @@ -196,7 +203,7 @@ const Welcome: FC = () => {
navigate('/#backup-seed-phrase');
} else if (step === OnboardingStep.CreatePassword) {
if (onboardingType === OnboardingType.Create) {
navigate('/#verify-seed-phrase');
navigate('/#select-auth-scheme');
} else {
if (importType === ImportType.WalletFile) {
navigate('/#import-from-file');
Expand All @@ -206,6 +213,8 @@ const Welcome: FC = () => {
}
} else if (step === OnboardingStep.ImportFromFile || step === OnboardingStep.ImportFromSeed) {
navigate('/#select-import-type');
} else if (step === OnboardingStep.SelectAuthScheme) {
navigate('/#verify-seed-phrase');
}
break;
default:
Expand Down Expand Up @@ -244,6 +253,9 @@ const Welcome: FC = () => {
case '#create-password':
setStep(OnboardingStep.CreatePassword);
break;
case '#select-auth-scheme':
setStep(OnboardingStep.SelectAuthScheme);
break;
case '#confirmation':
if (!password) {
navigate('/');
Expand Down Expand Up @@ -280,6 +292,8 @@ const Welcome: FC = () => {
password={password}
isLoading={isLoading}
onAction={onAction}
authScheme={authScheme}
setAuthScheme={setAuthScheme}
/>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/ProgressIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface ProgressIndicatorProps extends React.HTMLAttributes<HTMLDivElem
}
export const ProgressIndicator: React.FC<ProgressIndicatorProps> = ({ className, steps, currentStep, ...props }) => {
return (
<div {...props} className={classNames('flex justify-center items-center gap-2 w-10', className)}>
<div {...props} className={classNames('flex justify-center items-center gap-2 w-15', className)}>
{Array.from({ length: steps }).map((_, index) => (
<div
key={index}
Expand Down
13 changes: 8 additions & 5 deletions src/lib/intercom/mobile-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Actions from 'lib/miden/back/actions';
import { store } from 'lib/miden/back/store';
import { MidenMessageType } from 'lib/miden/types';
import { WalletMessageType } from 'lib/shared/types';
import { AuthScheme, WalletType } from 'screens/onboarding/types';

import { MobileIntercomAdapter, getMobileIntercomAdapter } from './mobile-adapter';

Expand Down Expand Up @@ -81,12 +82,13 @@ describe('MobileIntercomAdapter', () => {
it('handles NewWalletRequest', async () => {
const response = await adapter.request({
type: WalletMessageType.NewWalletRequest,
authScheme: AuthScheme.Falcon,
password: 'test123',
mnemonic: 'word1 word2 word3',
ownMnemonic: false
} as any);
});

expect(Actions.registerNewWallet).toHaveBeenCalledWith('test123', 'word1 word2 word3', false);
expect(Actions.registerNewWallet).toHaveBeenCalledWith('test123', AuthScheme.Falcon, 'word1 word2 word3', false);
expect(response).toEqual({ type: WalletMessageType.NewWalletResponse });
});

Expand Down Expand Up @@ -123,11 +125,12 @@ describe('MobileIntercomAdapter', () => {
it('handles CreateAccountRequest', async () => {
const response = await adapter.request({
type: WalletMessageType.CreateAccountRequest,
walletType: 'public',
authScheme: AuthScheme.Falcon,
walletType: WalletType.OnChain,
name: 'Test Account'
} as any);
});

expect(Actions.createHDAccount).toHaveBeenCalledWith('public', 'Test Account');
expect(Actions.createHDAccount).toHaveBeenCalledWith(WalletType.OnChain, AuthScheme.Falcon, 'Test Account');
expect(response).toEqual({ type: WalletMessageType.CreateAccountResponse });
});

Expand Down
4 changes: 2 additions & 2 deletions src/lib/intercom/mobile-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class MobileIntercomAdapter {
};

case WalletMessageType.NewWalletRequest:
await Actions.registerNewWallet(req.password, req.mnemonic, req.ownMnemonic);
await Actions.registerNewWallet(req.password, req.authScheme, req.mnemonic, req.ownMnemonic);
return { type: WalletMessageType.NewWalletResponse };

case WalletMessageType.ImportFromClientRequest:
Expand All @@ -83,7 +83,7 @@ export class MobileIntercomAdapter {
return { type: WalletMessageType.LockResponse };

case WalletMessageType.CreateAccountRequest:
await Actions.createHDAccount((req as any).walletType, (req as any).name);
await Actions.createHDAccount(req.walletType, req.authScheme, req.name);
return { type: WalletMessageType.CreateAccountResponse };

case WalletMessageType.UpdateCurrentAccountRequest:
Expand Down
Loading