Skip to content

Commit d2438f8

Browse files
szymonmaslowskigreatertomioldGreg5
authored
LW-10244: Port HW onboarding revamp to multi wallet (#1130)
* feat: revamp multi wallet connect flow * fix: account selection options behind modal * fix: redirect to assets after successfull HW restoration * test: fix multi-wallet hardware flow tests * fix: handle properly the case of rejecting key export * fix: update lock file * fix: typescript error in tests * test: fix failing multi-wallet hardware flow tests * fix: translations change --------- Co-authored-by: John Oshalusi <john.oshalusi@iohk.io> Co-authored-by: januszjanus <janusz.janus@iohk.io>
1 parent c917148 commit d2438f8

File tree

32 files changed

+718
-521
lines changed

32 files changed

+718
-521
lines changed

apps/browser-extension-wallet/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"zustand": "3.5.14"
8989
},
9090
"devDependencies": {
91+
"@cardano-sdk/hardware-ledger": "0.9.3",
9192
"@emurgo/cardano-message-signing-asmjs": "1.0.1",
9293
"@types/dotenv-webpack": "7.0.3",
9394
"@types/pluralize": "^0.0.29",

apps/browser-extension-wallet/src/lib/translations/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@
188188
"browserView.onboarding.errorDialog.messageDeviceDisconnected": "Please check your hardware device connection and for Ledger, if Cardano App is open",
189189
"browserView.onboarding.errorDialog.messageGeneric": "Try connecting you device again",
190190
"browserView.onboarding.errorDialog.messagePublicKeyExportRejected": "Public key export unsuccessful. User declined action on hardware wallet device.",
191-
"browserView.onboarding.errorDialog.title": "Opps! Something went wrong",
191+
"browserView.onboarding.errorDialog.title": "Oops! Something went wrong",
192192
"browserView.onboarding.hardwareWalletSendTransition.cancel": "Cancel",
193193
"browserView.onboarding.hardwareWalletSendTransition.description": "Ledger transactions require confirmation in advanced view.",
194194
"browserView.onboarding.hardwareWalletSendTransition.ok": "Ok",

apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,26 @@ export const postHogMultiWalletActions: PostHogMultiWalletActionsType = {
5656
RECOVERY_PHRASE_INTRO_VIDEO_GOTIT_CLICK: PostHogAction.MultiWalletCreateKeepWalletSecureGotItClick,
5757
RECOVERY_PHRASE_COPY_TO_CLIPBOARD_CLICK: PostHogAction.MultiWalletCreateSaveRecoveryPhraseCopyToClipboardClick,
5858
RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK:
59-
PostHogAction.MultiWalletCreateEnterRecoveryPhrasePasteFromClipboardClick
59+
PostHogAction.MultiWalletCreateEnterRecoveryPhrasePasteFromClipboardClick,
60+
WALLET_ADDED: PostHogAction.MultiWalletCreateAdded
6061
},
6162
restore: {
6263
SETUP_OPTION_CLICK: PostHogAction.MultiWalletRestoreClick,
6364
ENTER_RECOVERY_PHRASE_NEXT_CLICK: PostHogAction.MultiWalletRestoreEnterRecoveryPhraseNextClick,
6465
ENTER_WALLET: PostHogAction.MultiWalletRestoreEnterWalletClick,
6566
RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK:
6667
PostHogAction.MultiWalletRestoreEnterRecoveryPhrasePasteFromClipboardClick,
67-
RECOVERY_PHRASE_PASTE_READ_MORE_CLICK: PostHogAction.MultiWalletCreateSaveRecoveryPhrasePasteReadMoreClick
68+
RECOVERY_PHRASE_PASTE_READ_MORE_CLICK: PostHogAction.MultiWalletCreateSaveRecoveryPhrasePasteReadMoreClick,
69+
WALLET_ADDED: PostHogAction.MultiWalletRestoreAdded,
70+
HD_WALLET: PostHogAction.MultiWalletRestoreHdWallet
6871
},
6972
hw: {
70-
SETUP_OPTION_CLICK: PostHogAction.MultiWalletHWClick
73+
SETUP_OPTION_CLICK: PostHogAction.MultiWalletHWClick,
74+
CONNECT_HW_VIEW: PostHogAction.MultiWalletHWConnectView,
75+
HW_POPUP_CONNECT_CLICK: PostHogAction.MultiWalletHWPopupConnectClick,
76+
CONNECT_HW_TRY_AGAIN_CLICK: PostHogAction.MultiWalletHWConnectTryAgainClick,
77+
SETUP_HW_ACCOUNT_NO_CLICK: PostHogAction.MultiWalletHWSetupWalletAccountNoClick,
78+
ENTER_WALLET: PostHogAction.MultiWalletHWEnterWalletClick,
79+
WALLET_ADDED: PostHogAction.MultiWalletHWAdded
7180
}
7281
};

apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,12 @@ export type PostHogActionsKeys =
6161
| 'RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK'
6262
| 'RECOVERY_PASSPHRASE_VERIFICATION_NEXT_CLICK'
6363
| 'RECOVERY_PHRASE_COPY_READ_MORE_CLICK'
64-
| 'RECOVERY_PHRASE_PASTE_READ_MORE_CLICK';
64+
| 'RECOVERY_PHRASE_PASTE_READ_MORE_CLICK'
65+
| 'WALLET_ADDED'
66+
| 'HD_WALLET';
6567
export type PostHogOnboardingActionsValueType = Partial<Record<PostHogActionsKeys, PostHogAction>>;
68+
export type PostHogOnboardingActionsType = Record<OnboardingFlows, PostHogOnboardingActionsValueType>;
6669
export type PostHogMultiWalletActionsValueType = Partial<Record<PostHogActionsKeys, PostHogAction>>;
67-
export type PostHogOnboardingActionsType = Partial<Record<OnboardingFlows, PostHogOnboardingActionsValueType>>;
6870
export type PostHogMultiWalletActionsType = Record<MultiWalletFlows, PostHogMultiWalletActionsValueType>;
6971
export type PostHogPersonProperties = {
7072
$set: {

apps/browser-extension-wallet/src/routes/wallet-paths.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ export const walletRoutePaths = {
3131
hardware: {
3232
root: '/new-wallet/hardware',
3333
connect: '/new-wallet/hardware/connect',
34-
select: '/new-wallet/hardware/select',
35-
name: '/new-wallet/hardware/name',
36-
allDone: '/new-wallet/hardware/all-done'
34+
setup: '/new-wallet/hardware/setup',
35+
create: '/new-wallet/hardware/create'
3736
},
3837
restore: {
3938
root: '/new-wallet/restore',

apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx

Lines changed: 18 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable unicorn/no-null */
22
/* eslint-disable react/no-multi-comp */
3-
import React, { useEffect, useMemo } from 'react';
3+
import React from 'react';
44
import { Route, Switch, useHistory, useRouteMatch } from 'react-router-dom';
55
import { Modal } from 'antd';
66

@@ -18,101 +18,38 @@ import { CreateWallet } from './create-wallet';
1818
import { HardwareWallet } from './hardware-wallet';
1919
import { RestoreWallet } from './restore-wallet';
2020
import { walletRoutePaths } from '@routes';
21-
import { useWalletManager } from '@hooks';
2221
import { Subject } from 'rxjs';
2322
import { Wallet } from '@lace/cardano';
24-
import { NavigationButton, PostHogAction, toast } from '@lace/common';
23+
import { NavigationButton } from '@lace/common';
2524
import { useBackgroundPage } from '@providers/BackgroundPageProvider';
26-
import { Providers } from './hardware-wallet/types';
27-
import { TOAST_DEFAULT_DURATION } from '@hooks/useActionExecution';
28-
import { useTranslation } from 'react-i18next';
29-
import { WalletConflictError } from '@cardano-sdk/web-extension';
30-
import { useAnalyticsContext } from '@providers';
31-
import { getWalletAccountsQtyString } from '@src/utils/get-wallet-count-string';
3225

3326
const { newWallet } = walletRoutePaths;
3427

35-
const createWallet = (): Promise<void> => Promise.resolve(void 0);
36-
37-
interface ConfirmationDialog {
38-
shouldShowDialog$: Subject<boolean>;
28+
interface Props {
29+
shouldShowConfirmationDialog$: Subject<boolean>;
3930
}
4031

41-
export const SetupHardwareWallet = ({ shouldShowDialog$ }: ConfirmationDialog): JSX.Element => {
42-
const { t } = useTranslation();
43-
const { connectHardwareWallet, createHardwareWallet, walletRepository } = useWalletManager();
44-
const analytics = useAnalyticsContext();
45-
const disconnectHardwareWallet$ = useMemo(() => new Subject<USBConnectionEvent>(), []);
46-
47-
const hardwareWalletProviders = useMemo(
48-
(): Providers => ({
49-
connectHardwareWallet,
50-
disconnectHardwareWallet$,
51-
shouldShowDialog$,
52-
createWallet: async ({ account, connection, model, name }) => {
53-
try {
54-
const { source } = await createHardwareWallet({
55-
connectedDevice: model,
56-
deviceConnection: connection,
57-
name,
58-
accountIndex: account
59-
});
60-
await analytics.sendEventToPostHog(PostHogAction.MultiWalletHWAdded, {
61-
// eslint-disable-next-line camelcase
62-
$set: { wallet_accounts_quantity: await getWalletAccountsQtyString(walletRepository) }
63-
});
64-
await analytics.sendMergeEvent(source.account.extendedAccountPublicKey);
65-
} catch (error) {
66-
if (error instanceof WalletConflictError) {
67-
toast.notify({ duration: TOAST_DEFAULT_DURATION, text: t('multiWallet.walletAlreadyExists') });
68-
} else {
69-
throw error;
70-
}
71-
}
72-
}
73-
}),
74-
[
75-
connectHardwareWallet,
76-
createHardwareWallet,
77-
disconnectHardwareWallet$,
78-
shouldShowDialog$,
79-
t,
80-
analytics,
81-
walletRepository
82-
]
83-
);
84-
85-
useEffect(() => {
86-
const onHardwareWalletDisconnect = (event: USBConnectionEvent) => {
87-
disconnectHardwareWallet$.next(event);
88-
};
89-
90-
navigator.usb.addEventListener('disconnect', onHardwareWalletDisconnect);
91-
92-
return () => {
93-
navigator.usb.removeEventListener('disconnect', onHardwareWalletDisconnect);
94-
disconnectHardwareWallet$.complete();
95-
};
96-
}, [disconnectHardwareWallet$]);
97-
98-
return <HardwareWallet providers={hardwareWalletProviders} />;
99-
};
32+
export const SetupHardwareWallet = ({ shouldShowConfirmationDialog$ }: Props): JSX.Element => (
33+
<HardwareWallet
34+
providers={{
35+
shouldShowConfirmationDialog$
36+
}}
37+
/>
38+
);
10039

101-
export const SetupCreateWallet = (confirmationDialog: ConfirmationDialog): JSX.Element => (
40+
export const SetupCreateWallet = ({ shouldShowConfirmationDialog$ }: Props): JSX.Element => (
10241
<CreateWallet
10342
providers={{
104-
createWallet,
10543
generateMnemonicWords: Wallet.KeyManagement.util.generateMnemonicWords,
106-
confirmationDialog
44+
shouldShowConfirmationDialog$
10745
}}
10846
/>
10947
);
11048

111-
export const SetupRestoreWallet = (confirmationDialog: ConfirmationDialog): JSX.Element => (
49+
export const SetupRestoreWallet = ({ shouldShowConfirmationDialog$ }: Props): JSX.Element => (
11250
<RestoreWallet
11351
providers={{
114-
createWallet,
115-
confirmationDialog
52+
shouldShowConfirmationDialog$
11653
}}
11754
/>
11855
);
@@ -136,15 +73,15 @@ const Component = (): JSX.Element => {
13673
<Switch>
13774
<Route
13875
path={newWallet.create.root}
139-
render={() => <SetupCreateWallet shouldShowDialog$={shouldShowDialog$} />}
76+
render={() => <SetupCreateWallet shouldShowConfirmationDialog$={shouldShowDialog$} />}
14077
/>
14178
<Route
14279
path={newWallet.hardware.root}
143-
render={() => <SetupHardwareWallet shouldShowDialog$={shouldShowDialog$} />}
80+
render={() => <SetupHardwareWallet shouldShowConfirmationDialog$={shouldShowDialog$} />}
14481
/>
14582
<Route
14683
path={newWallet.restore.root}
147-
render={() => <SetupRestoreWallet shouldShowDialog$={shouldShowDialog$} />}
84+
render={() => <SetupRestoreWallet shouldShowConfirmationDialog$={shouldShowDialog$} />}
14885
/>
14986
<Route exact path={`${path}/`} component={Home} />
15087
</Switch>

apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/CreateWallet.test.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,14 @@ describe('Multi Wallet Setup/Create Wallet', () => {
6060
let providers = {} as {
6161
createWallet: jest.Mock;
6262
generateMnemonicWords: jest.Mock;
63-
confirmationDialog: {
64-
shouldShowDialog$: BehaviorSubject<boolean>;
65-
};
63+
shouldShowConfirmationDialog$: BehaviorSubject<boolean>;
6664
};
6765

6866
beforeEach(() => {
6967
providers = {
7068
createWallet: jest.fn(),
7169
generateMnemonicWords: jest.fn(),
72-
confirmationDialog: {
73-
shouldShowDialog$: new BehaviorSubject(false)
74-
}
70+
shouldShowConfirmationDialog$: new BehaviorSubject(false)
7571
};
7672
});
7773

@@ -112,15 +108,15 @@ describe('Multi Wallet Setup/Create Wallet', () => {
112108
</AppSettingsProvider>
113109
);
114110

115-
expect(await firstValueFrom(providers.confirmationDialog.shouldShowDialog$)).toBe(false);
111+
expect(await firstValueFrom(providers.shouldShowConfirmationDialog$)).toBe(false);
116112

117113
const nextButton = getNextButton();
118114
fireEvent.click(nextButton);
119-
expect(await firstValueFrom(providers.confirmationDialog.shouldShowDialog$)).toBe(true);
115+
expect(await firstValueFrom(providers.shouldShowConfirmationDialog$)).toBe(true);
120116

121117
const backButton = getBackButton();
122118
fireEvent.click(backButton);
123119
fireEvent.click(screen.queryByTestId('delete-address-modal-confirm'));
124-
expect(await firstValueFrom(providers.confirmationDialog.shouldShowDialog$)).toBe(false);
120+
expect(await firstValueFrom(providers.shouldShowConfirmationDialog$)).toBe(false);
125121
});
126122
});

apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/context.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CreateWalletParams } from '@hooks';
2-
import { PostHogAction } from '@lace/common';
32
import { walletRoutePaths } from '@routes';
3+
import { postHogMultiWalletActions } from '@providers/AnalyticsProvider/analyticsTracker';
44
import React, { createContext, useContext, useState } from 'react';
55
import { useHistory } from 'react-router';
66
import { useHotWalletCreation } from '../useHotWalletCreation';
@@ -58,14 +58,14 @@ export const CreateWalletProvider = ({ children, providers }: Props): React.Reac
5858
};
5959

6060
const setFormDirty = (dirty: boolean) => {
61-
providers.confirmationDialog.shouldShowDialog$.next(dirty);
61+
providers.shouldShowConfirmationDialog$.next(dirty);
6262
};
6363

6464
const finalizeWalletCreation = async () => {
6565
const wallet = await createHotWallet();
6666
await sendPostWalletAddAnalytics({
6767
extendedAccountPublicKey: wallet.source.account.extendedAccountPublicKey,
68-
walletAddedPostHogAction: PostHogAction.MultiWalletCreateAdded
68+
walletAddedPostHogAction: postHogMultiWalletActions.create.WALLET_ADDED
6969
});
7070
clearSecrets();
7171
};

apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/types.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ export interface Data {
77
}
88

99
export interface Providers {
10-
createWallet: (params: Data) => Promise<void>;
1110
generateMnemonicWords: () => string[];
12-
confirmationDialog: {
13-
shouldShowDialog$: Subject<boolean>;
14-
};
11+
shouldShowConfirmationDialog$: Subject<boolean>;
1512
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { makeErrorDialog } from '@views/browser/features/wallet-setup/components/HardwareWalletFlow';
2+
import { ErrorDialogCode } from './types';
3+
4+
const commonErrorDialogTranslationKeys = {
5+
title: 'browserView.onboarding.errorDialog.title' as const,
6+
confirm: 'browserView.onboarding.errorDialog.cta' as const
7+
};
8+
export const ErrorDialog = makeErrorDialog<ErrorDialogCode>({
9+
[ErrorDialogCode.DeviceDisconnected]: {
10+
...commonErrorDialogTranslationKeys,
11+
description: 'browserView.onboarding.errorDialog.messageDeviceDisconnected'
12+
},
13+
[ErrorDialogCode.PublicKeyExportRejected]: {
14+
...commonErrorDialogTranslationKeys,
15+
description: 'browserView.onboarding.errorDialog.messagePublicKeyExportRejected'
16+
},
17+
[ErrorDialogCode.Generic]: {
18+
...commonErrorDialogTranslationKeys,
19+
description: 'browserView.onboarding.errorDialog.messageGeneric'
20+
}
21+
});

0 commit comments

Comments
 (0)