Skip to content

Commit

Permalink
feat: unify codebase of the onboarding and multi-wallet flows
Browse files Browse the repository at this point in the history
  • Loading branch information
szymonmaslowski committed May 8, 2024
1 parent 1dd354c commit 665d666
Show file tree
Hide file tree
Showing 33 changed files with 446 additions and 485 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const postHogMultiWalletActions: PostHogMultiWalletActionsType = {
WALLET_ADDED: PostHogAction.MultiWalletRestoreAdded,
HD_WALLET: PostHogAction.MultiWalletRestoreHdWallet
},
hw: {
hardware: {
SETUP_OPTION_CLICK: PostHogAction.MultiWalletHWClick,
CONNECT_HW_VIEW: PostHogAction.MultiWalletHWConnectView,
HW_POPUP_CONNECT_CLICK: PostHogAction.MultiWalletHWPopupConnectClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export enum TxCreationType {
}

export type OnboardingFlows = 'create' | 'restore' | 'hw' | 'forgot_password' | 'onboarding';
export type MultiWalletFlows = 'create' | 'restore' | 'hw';
export type MultiWalletFlows = 'create' | 'restore' | 'hardware';
export type PostHogActionsKeys =
| 'SETUP_OPTION_CLICK'
| 'ANALYTICS_AGREE_CLICK'
Expand All @@ -64,10 +64,9 @@ export type PostHogActionsKeys =
| 'RECOVERY_PHRASE_PASTE_READ_MORE_CLICK'
| 'WALLET_ADDED'
| 'HD_WALLET';
export type PostHogOnboardingActionsValueType = Partial<Record<PostHogActionsKeys, PostHogAction>>;
export type PostHogOnboardingActionsType = Record<OnboardingFlows, PostHogOnboardingActionsValueType>;
export type PostHogMultiWalletActionsValueType = Partial<Record<PostHogActionsKeys, PostHogAction>>;
export type PostHogMultiWalletActionsType = Record<MultiWalletFlows, PostHogMultiWalletActionsValueType>;
export type PostHogActions = Partial<Record<PostHogActionsKeys, PostHogAction>>;
export type PostHogOnboardingActionsType = Record<OnboardingFlows, PostHogActions>;
export type PostHogMultiWalletActionsType = Record<MultiWalletFlows, PostHogActions>;
export type PostHogPersonProperties = {
$set: {
user_tracking_type: UserTrackingType;
Expand Down
19 changes: 3 additions & 16 deletions apps/browser-extension-wallet/src/routes/wallet-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,9 @@ export const walletRoutePaths = {
},
newWallet: {
root: '/new-wallet',
create: {
root: '/new-wallet/create',
setup: '/new-wallet/create/setup',
recoveryPhrase: '/new-wallet/create/recovery-phrase'
},
hardware: {
root: '/new-wallet/hardware',
connect: '/new-wallet/hardware/connect',
setup: '/new-wallet/hardware/setup',
create: '/new-wallet/hardware/create'
},
restore: {
root: '/new-wallet/restore',
setup: '/new-wallet/restore/setup',
enterRecoveryPhrase: '/new-wallet/restore/enter-recovery-phrase'
}
create: '/new-wallet/create',
hardware: '/new-wallet/hardware',
restore: '/new-wallet/restore'
},
sharedWallet: {
root: '/add-shared-wallet'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ export const Home = (): JSX.Element => {
<WalletSetupOptionsStep
onNewWalletRequest={() => {
void analytics.sendEventToPostHog(postHogMultiWalletActions.create.SETUP_OPTION_CLICK);
history.push(walletRoutePaths.newWallet.create.root);
history.push(walletRoutePaths.newWallet.create);
}}
onHardwareWalletRequest={() => {
void analytics.sendEventToPostHog(postHogMultiWalletActions.hw.SETUP_OPTION_CLICK);
history.push(walletRoutePaths.newWallet.hardware.root);
void analytics.sendEventToPostHog(postHogMultiWalletActions.hardware.SETUP_OPTION_CLICK);
history.push(walletRoutePaths.newWallet.hardware);
}}
onRestoreWalletRequest={() => {
void analytics.sendEventToPostHog(postHogMultiWalletActions.restore.SETUP_OPTION_CLICK);
history.push(walletRoutePaths.newWallet.restore.root);
history.push(walletRoutePaths.newWallet.restore);
}}
translations={walletSetupOptionsStepTranslations}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,97 +1,43 @@
/* eslint-disable unicorn/no-null */
/* eslint-disable react/no-multi-comp */
import React from 'react';
import { Route, Switch, useHistory, useRouteMatch } from 'react-router-dom';
import { Modal } from 'antd';

import styles from './MultiWallet.module.scss';

import { Home } from './components/Home';

import {
WalletSetupConfirmationDialogProvider,
WalletSetupFlow,
WalletSetupFlowProvider,
useWalletSetupConfirmationDialog
} from '@lace/core';
import { CreateWallet } from './create-wallet';
import { HardwareWallet } from './hardware-wallet';
import { RestoreWallet } from './restore-wallet';
import { walletRoutePaths } from '@routes';
import { Subject } from 'rxjs';
import { Wallet } from '@lace/cardano';
import { NavigationButton } from '@lace/common';
import { WalletSetupConfirmationDialogProvider, WalletSetupFlow, WalletSetupFlowProvider } from '@lace/core';
import { useBackgroundPage } from '@providers/BackgroundPageProvider';
import { walletRoutePaths } from '@routes';
import { Modal } from 'antd';
import React from 'react';
import { useHistory } from 'react-router-dom';
import styles from './MultiWallet.module.scss';
import { WalletOnboardingFlows } from './WalletOnboardingFlows';
import { postHogMultiWalletActions } from '@providers/AnalyticsProvider/analyticsTracker';
import { Home } from './Home';

const { newWallet } = walletRoutePaths;

interface Props {
shouldShowConfirmationDialog$: Subject<boolean>;
}

export const SetupHardwareWallet = ({ shouldShowConfirmationDialog$ }: Props): JSX.Element => (
<HardwareWallet
providers={{
shouldShowConfirmationDialog$
}}
/>
);

export const SetupCreateWallet = ({ shouldShowConfirmationDialog$ }: Props): JSX.Element => (
<CreateWallet
providers={{
generateMnemonicWords: Wallet.KeyManagement.util.generateMnemonicWords,
shouldShowConfirmationDialog$
}}
/>
);

export const SetupRestoreWallet = ({ shouldShowConfirmationDialog$ }: Props): JSX.Element => (
<RestoreWallet
providers={{
shouldShowConfirmationDialog$
}}
/>
);

const Component = (): JSX.Element => {
const { path } = useRouteMatch();
export const MultiWallet = (): JSX.Element => {
const history = useHistory();
const { page, setBackgroundPage } = useBackgroundPage();
const { isDialogOpen, withConfirmationDialog, shouldShowDialog$ } = useWalletSetupConfirmationDialog();
const closeWalletCreation = withConfirmationDialog(() => {
setBackgroundPage();
history.push(page);
});

return (
<WalletSetupFlowProvider flow={WalletSetupFlow.ADD_WALLET}>
<Modal centered closable={false} footer={null} open={!isDialogOpen} width="100%" className={styles.modal}>
<div className={styles.closeButton}>
<NavigationButton icon="cross" onClick={closeWalletCreation} />
</div>
<Switch>
<Route
path={newWallet.create.root}
render={() => <SetupCreateWallet shouldShowConfirmationDialog$={shouldShowDialog$} />}
/>
<Route
path={newWallet.hardware.root}
render={() => <SetupHardwareWallet shouldShowConfirmationDialog$={shouldShowDialog$} />}
/>
<Route
path={newWallet.restore.root}
render={() => <SetupRestoreWallet shouldShowConfirmationDialog$={shouldShowDialog$} />}
/>
<Route exact path={`${path}/`} component={Home} />
</Switch>
</Modal>
<WalletSetupConfirmationDialogProvider>
{({ isDialogOpen, withConfirmationDialog, shouldShowDialog$ }) => (
<Modal centered closable={false} footer={null} open={!isDialogOpen} width="100%" className={styles.modal}>
<div className={styles.closeButton}>
<NavigationButton
icon="cross"
onClick={withConfirmationDialog(() => {
setBackgroundPage();
history.push(page);
})}
/>
</div>
<WalletOnboardingFlows
postHogActions={postHogMultiWalletActions}
renderHome={() => <Home />}
setFormDirty={(dirty) => shouldShowDialog$.next(dirty)}
urlPath={walletRoutePaths.newWallet}
/>
</Modal>
)}
</WalletSetupConfirmationDialogProvider>
</WalletSetupFlowProvider>
);
};

export const MultiWallet = (): JSX.Element => (
<WalletSetupConfirmationDialogProvider>
<Component />
</WalletSetupConfirmationDialogProvider>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useKeyboardShortcut } from '@lace/common';
import { Redirect, Route, Switch, useRouteMatch } from 'react-router-dom';
import { CreateWallet } from './create-wallet';
import { HardwareWallet } from './hardware-wallet';
import { RestoreWallet } from './restore-wallet';
import React, { ReactNode, VFC } from 'react';
import { Flows, WalletOnboardingPostHogActions, SetFormDirty } from './types';
import { WalletOnboardingProvider } from './walletOnboardingContext';

type WalletOnboardingProps = {
aliasEventRequired?: boolean;
flowsEnabled?: boolean;
forgotPasswordFlowActive?: boolean;
postHogActions: WalletOnboardingPostHogActions;
renderHome: () => ReactNode;
setFormDirty?: SetFormDirty;
urlPath: Record<Flows, string>;
};

export const WalletOnboardingFlows: VFC<WalletOnboardingProps> = ({
aliasEventRequired = false,
flowsEnabled = true,
forgotPasswordFlowActive = false,
postHogActions,
renderHome,
setFormDirty = () => void 0,
urlPath
}) => {
useKeyboardShortcut(['Enter'], () => {
const nextBnt: HTMLButtonElement = document.querySelector('[data-testid="wallet-setup-step-btn-next"]');
const confirmGoBack: HTMLButtonElement = document.querySelector('[data-testid="delete-address-modal-confirm"]');

if (confirmGoBack) {
confirmGoBack.click();
} else if (nextBnt && !nextBnt.getAttribute('disabled')) {
nextBnt.click();
}
});

const { path } = useRouteMatch();
return (
<WalletOnboardingProvider value={{ aliasEventRequired, forgotPasswordFlowActive, postHogActions, setFormDirty }}>
<Switch>
<Route exact path={`${path}/`} render={renderHome} />
{flowsEnabled && (
<>
<Route path={urlPath.create} component={CreateWallet} />
<Route path={urlPath.hardware} component={HardwareWallet} />
<Route path={urlPath.restore} component={RestoreWallet} />
</>
)}
{!flowsEnabled && <Redirect to={`${path}/`} />}
</Switch>
</WalletOnboardingProvider>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,11 @@ jest.doMock('@hooks/useWalletManager', () => ({
import React from 'react';
import '@testing-library/jest-dom';
import { fireEvent, render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Providers } from './types';
import { walletRoutePaths } from '@routes';
import {
DEFAULT_MNEMONIC_LENGTH,
createAssetsRoute,
fillMnemonic,
getNextButton,
mnemonicWords,
setupStep,
getBackButton
} from '../tests/utils';
Expand Down Expand Up @@ -72,17 +68,12 @@ describe('Multi Wallet Setup/Create Wallet', () => {
});

test('setting up a new hot wallet', async () => {
providers.generateMnemonicWords.mockReturnValue(mnemonicWords);
providers.createWallet.mockResolvedValue(void 0);

render(
<AppSettingsProvider>
<DatabaseProvider>
<StoreProvider appMode={APP_MODE_BROWSER}>
<MemoryRouter initialEntries={[walletRoutePaths.newWallet.create.root]}>
<CreateWallet providers={providers as Providers} />
{createAssetsRoute()}
</MemoryRouter>
<CreateWallet />
{createAssetsRoute()}
</StoreProvider>
</DatabaseProvider>
</AppSettingsProvider>
Expand All @@ -93,16 +84,12 @@ describe('Multi Wallet Setup/Create Wallet', () => {
});

test('should emit correct value for shouldShowDialog', async () => {
providers.generateMnemonicWords.mockReturnValue(mnemonicWords);

render(
<AppSettingsProvider>
<DatabaseProvider>
<StoreProvider appMode={APP_MODE_BROWSER}>
<MemoryRouter initialEntries={[walletRoutePaths.newWallet.create.root]}>
<CreateWallet providers={providers as Providers} />
{createAssetsRoute()}
</MemoryRouter>
<CreateWallet />
{createAssetsRoute()}
</StoreProvider>
</DatabaseProvider>
</AppSettingsProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
import React from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import { Setup } from './steps/Setup';
import { NewRecoveryPhrase } from './steps/NewRecoveryPhrase';
import { CreateWalletProvider } from './context';
import { walletRoutePaths } from '@routes';
import { Providers } from './types';
import { WalletCreateStep } from './types';

const {
newWallet: { create }
} = walletRoutePaths;

interface Props {
providers: Providers;
}

export const CreateWallet = ({ providers }: Props): JSX.Element => (
<CreateWalletProvider providers={providers}>
<Switch>
<Route path={create.recoveryPhrase} component={NewRecoveryPhrase} />
<Route path={create.setup} component={Setup} />
<Redirect from={create.root} to={create.recoveryPhrase} />
</Switch>
export const CreateWallet = (): JSX.Element => (
<CreateWalletProvider>
{({ step }) => (
<>
{(step === WalletCreateStep.RecoveryPhraseWriteDown || step === WalletCreateStep.RecoveryPhraseInput) && (
<NewRecoveryPhrase />
)}
{step === WalletCreateStep.Setup && <Setup />}
</>
)}
</CreateWalletProvider>
);

0 comments on commit 665d666

Please sign in to comment.