Skip to content

Commit f7730b9

Browse files
authored
Merge pull request #426 from Concordium/abort-recovery
Allow aborting recovery when there are no identities
2 parents 03f2580 + 71e7a42 commit f7730b9

File tree

8 files changed

+64
-10
lines changed

8 files changed

+64
-10
lines changed

packages/browser-wallet-message-hub/src/message.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export enum InternalMessageType {
4747
AddWeb3IdCredential = 'I_AddWeb3IdCredential',
4848
LoadWeb3IdBackup = 'I_LoadWeb3IdBackup',
4949
ImportWeb3IdBackup = 'I_ImportWeb3IdBackup',
50+
AbortRecovery = 'I_AbortRecovery',
5051
}
5152

5253
// eslint-disable-next-line @typescript-eslint/no-explicit-any

packages/browser-wallet/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 1.4.0
4+
5+
### Added
6+
7+
- Recovery can now be aborted when an identity has not yet been found.
8+
39
## 1.3.2
410

511
### Added

packages/browser-wallet/src/background/recovery.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { decrypt } from '@shared/utils/crypto';
3333
import { Buffer } from 'buffer/';
3434
import { GRPCTIMEOUT } from '@shared/constants/networkConfiguration';
3535
import { addCredential, addIdentity, updateCredentials, updateIdentities } from './update';
36-
import bgMessageHandler from './message-handler';
36+
import bgMessageHandler, { onMessage } from './message-handler';
3737
import { openWindow } from './window-management';
3838

3939
// How many empty identityIndices are allowed before stopping
@@ -107,7 +107,7 @@ async function getSeed() {
107107
return Buffer.from(mnemonicToSeedSync(await decrypt(encryptedSeed, passcode))).toString('hex');
108108
}
109109

110-
async function performRecovery() {
110+
async function performRecovery(respond: (i: RecoveryBackgroundResponse) => void) {
111111
try {
112112
let status = await sessionRecoveryStatus.get();
113113
if (!status) {
@@ -144,8 +144,12 @@ async function performRecovery() {
144144
let initialGap: number | undefined = status.identityGap || 0;
145145
let initialIndex: number | undefined = status.identityIndex || 0;
146146

147+
const aborted = new AbortController();
148+
onMessage(InternalMessageType.AbortRecovery).then(() => aborted.abort());
149+
147150
for (const provider of providers) {
148151
const providerIndex = provider.ipInfo.ipIdentity;
152+
149153
if (completedProviders.includes(providerIndex)) {
150154
// eslint-disable-next-line no-continue
151155
continue;
@@ -160,6 +164,9 @@ async function performRecovery() {
160164
let identityIndex = initialIndex || 0;
161165
initialIndex = undefined;
162166
while (emptyIndices < maxEmpty) {
167+
if (aborted.signal.aborted) {
168+
return;
169+
}
163170
// Check if there is already an identity on the current index
164171
let identity = identities?.find(identityMatch({ index: identityIndex, providerIndex }));
165172
if (!identity || identity.status === CreationStatus.Rejected) {
@@ -256,7 +263,7 @@ async function performRecovery() {
256263
if (newCreds.length) {
257264
await addCredential(newCreds, network.genesisHash);
258265
}
259-
return {
266+
const added = {
260267
identities: [...identitiesToAdd, ...identitiesToUpdate].map((id) => ({
261268
index: id.index,
262269
providerIndex: id.providerIndex,
@@ -265,6 +272,9 @@ async function performRecovery() {
265272
return { address: pair.cred.address, balance: pair.balance };
266273
}),
267274
};
275+
respond({ status: BackgroundResponseStatus.Success, added });
276+
} catch (e) {
277+
respond({ status: BackgroundResponseStatus.Error, reason: (e as Error).toString() });
268278
} finally {
269279
await sessionIsRecovering.set(false);
270280
}
@@ -284,10 +294,7 @@ export async function startRecovery() {
284294
await openWindow();
285295
bgMessageHandler.sendInternalMessage(InternalMessageType.RecoveryFinished, result);
286296
};
287-
return performRecovery()
288-
.then((added) => respond({ status: BackgroundResponseStatus.Success, added }))
289-
.catch((e) => respond({ status: BackgroundResponseStatus.Error, reason: e.toString() }))
290-
.finally(() => chrome.alarms.clear(RECOVERY_ALARM_NAME));
297+
return performRecovery(respond).finally(() => chrome.alarms.clear(RECOVERY_ALARM_NAME));
291298
});
292299
}
293300
}

packages/browser-wallet/src/popup/pages/Recovery/Recovery.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@
6060
margin: $description-margin-top rem(20px) 0;
6161
margin-bottom: rem(10px);
6262
}
63+
64+
&__abort-button {
65+
color: $color-cta;
66+
text-decoration: underline;
67+
}
6368
}
6469

6570
&__exit-button {

packages/browser-wallet/src/popup/pages/Recovery/RecoveryMain.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
import React, { useEffect, useState, useCallback } from 'react';
1+
import React, { useEffect, useState, useCallback, useMemo } from 'react';
22
import { useNavigate } from 'react-router-dom';
33
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
44
import { useTranslation } from 'react-i18next';
55
import { grpcClientAtom, networkConfigurationAtom } from '@popup/store/settings';
6-
import { identityProvidersAtom, isRecoveringAtom, setRecoveryPayloadAtom } from '@popup/store/identity';
6+
import {
7+
identitiesAtom,
8+
identityProvidersAtom,
9+
isRecoveringAtom,
10+
recoveryNewIdentitiesAtom,
11+
setRecoveryPayloadAtom,
12+
} from '@popup/store/identity';
713
import PendingArrows from '@assets/svg/pending-arrows.svg';
814
import { BackgroundResponseStatus } from '@shared/utils/types';
915
import { getIdentityProviders } from '@popup/shared/utils/wallet-proxy';
1016
import { getGlobal, getNet } from '@shared/utils/network-helpers';
1117
import PageHeader from '@popup/shared/PageHeader';
1218
import { absoluteRoutes } from '@popup/constants/routes';
19+
import Button from '@popup/shared/Button';
20+
import { popupMessageHandler } from '@popup/shared/message-handler';
21+
import { InternalMessageType } from '@concordium/browser-wallet-message-hub';
1322

1423
export default function RecoveryMain() {
1524
const { t } = useTranslation('recovery');
@@ -18,6 +27,8 @@ export default function RecoveryMain() {
1827
const [providers, setProviders] = useAtom(identityProvidersAtom);
1928
const [isRecovering, setIsRecovering] = useAtom(isRecoveringAtom);
2029
const setRecoveryStatus = useSetAtom(setRecoveryPayloadAtom);
30+
const newIdentities = useAtomValue(recoveryNewIdentitiesAtom);
31+
const identities = useAtomValue(identitiesAtom);
2132
const [runRecovery, setRunRecovery] = useState<boolean>(true);
2233
const navigate = useNavigate();
2334

@@ -60,12 +71,31 @@ export default function RecoveryMain() {
6071
.catch((error) => onError(error.message));
6172
}, [runRecovery, isRecovering.loading, isRecovering.value, providers.length]);
6273

74+
const abort = useCallback(async () => {
75+
popupMessageHandler.sendInternalMessage(InternalMessageType.AbortRecovery);
76+
await setIsRecovering(false);
77+
navigate(absoluteRoutes.home.account.path);
78+
}, []);
79+
80+
const noIdentities = useMemo(
81+
() => identities.length === 0 && (!newIdentities || newIdentities.length === 0),
82+
[identities.length, newIdentities?.length]
83+
);
84+
6385
return (
6486
<>
6587
<PageHeader>{t('main.title')}</PageHeader>
6688
<div className="recovery__main onboarding-setup__page-with-header">
6789
<div className="onboarding-setup__page-with-header__description">{t('main.description')}</div>
6890
<PendingArrows className="loading" />
91+
{noIdentities && (
92+
<p className="m-t-auto m-b-10">
93+
{t('main.abort')}{' '}
94+
<Button className="recovery__main__abort-button" clear onClick={abort}>
95+
{t('main.abortClickable')}
96+
</Button>
97+
</p>
98+
)}
6999
</div>
70100
</>
71101
);

packages/browser-wallet/src/popup/pages/Recovery/i18n/da.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const t: typeof en = {
88
main: {
99
title: 'Genskaber din wallet',
1010
description: 'Vent venligst mens der søges efter dine identiteter og konti.',
11+
abort: 'Jeg har ikke lavet en konto,',
12+
abortClickable: 'så spring over',
1113
},
1214
finish: {
1315
success: 'Følgende identiteter og konti blev fundet.',

packages/browser-wallet/src/popup/pages/Recovery/i18n/en.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const t = {
66
main: {
77
title: 'Restoring your wallet',
88
description: 'Searching for your IDs and accounts. Please wait.',
9+
abort: 'I have never created an account,',
10+
abortClickable: 'so skip this check',
911
},
1012
finish: {
1113
success: 'The following identities and accounts were recovered.',

packages/browser-wallet/src/popup/store/identity.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
SessionPendingIdentity,
1010
} from '@shared/storage/types';
1111
import { Atom, atom, WritableAtom } from 'jotai';
12-
import { atomFamily } from 'jotai/utils';
12+
import { atomFamily, selectAtom } from 'jotai/utils';
1313
import { credentialsAtomWithLoading } from './account';
1414
import { AsyncWrapper, atomWithChromeStorage } from './utils';
1515

@@ -53,6 +53,7 @@ const recoveryStatusAtom = atomWithChromeStorage<RecoveryStatus | undefined>(
5353
export const setRecoveryPayloadAtom = atom<null, RecoveryPayload, Promise<void>>(null, (_, set, payload) =>
5454
set(recoveryStatusAtom, { payload })
5555
);
56+
export const recoveryNewIdentitiesAtom = selectAtom(recoveryStatusAtom, (v) => v.value?.identitiesToAdd);
5657

5758
export const identityByAddressAtomFamily = atomFamily<
5859
string | undefined,

0 commit comments

Comments
 (0)