Skip to content

Commit 915675b

Browse files
authored
fix: support force disconnect wallet in case of error (#1166)
1 parent ce7c293 commit 915675b

8 files changed

Lines changed: 83 additions & 18 deletions

File tree

src/_locales/en/messages.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@
5454
"keyRevoked_action_reconnectBtn": {
5555
"message": "Reconnect"
5656
},
57+
"disconnectWallet_error_generic": {
58+
"message": "We were unable to disconnect your wallet ($ERROR$).",
59+
"placeholders": {
60+
"ERROR": { "content": "$1", "example": "Internal server error" }
61+
}
62+
},
5763
"pay_action_pay": {
5864
"message": "Send now"
5965
},

src/background/services/background.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,12 +266,12 @@ export class Background {
266266
return;
267267

268268
case 'DISCONNECT_WALLET':
269-
await this.walletService.disconnectWallet();
269+
await this.walletService.disconnectWallet(message.payload.force);
270270
this.tabState.clearAllState('disconnect');
271271
await this.browser.alarms.clear(ALARM_RESET_OUT_OF_FUNDS);
272272
await this.updateVisualIndicatorsForCurrentTab();
273273
this.sendToPopup.send('SET_STATE', { state: {}, prevState: {} });
274-
return;
274+
return success(undefined);
275275

276276
case 'TOGGLE_CONTINUOUS_PAYMENTS': {
277277
await this.monetizationService.toggleContinuousPayments();

src/background/services/openPayments.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ export class OpenPaymentsService {
212212
}
213213
}
214214

215-
const isOpenPaymentsClientError = (error: unknown) =>
215+
export const isOpenPaymentsClientError = (error: unknown) =>
216216
error instanceof OpenPaymentsClientError;
217217

218218
export const isKeyRevokedError = (error: unknown) => {

src/background/services/wallet.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ import {
2323
} from '@/background/utils';
2424
import { KeyAutoAddService } from '@/background/services/keyAutoAdd';
2525
import { generateEd25519KeyPair, exportJWK } from '@/shared/crypto';
26-
import { isInvalidClientError } from '@/background/services/openPayments';
26+
import {
27+
isInvalidClientError,
28+
isOpenPaymentsClientError,
29+
} from '@/background/services/openPayments';
2730
import { APP_URL } from '@/background/constants';
2831
import { bytesToHex } from '@noble/hashes/utils';
2932
import type { Cradle } from '@/background/container';
@@ -37,6 +40,7 @@ export class WalletService {
3740
private storage: Cradle['storage'];
3841
private events: Cradle['events'];
3942
private browser: Cradle['browser'];
43+
private logger: Cradle['logger'];
4044
private appName: Cradle['appName'];
4145
private browserName: Cradle['browserName'];
4246
private t: Cradle['t'];
@@ -47,6 +51,7 @@ export class WalletService {
4751
storage,
4852
events,
4953
browser,
54+
logger,
5055
appName,
5156
browserName,
5257
t,
@@ -57,6 +62,7 @@ export class WalletService {
5762
storage,
5863
events,
5964
browser,
65+
logger,
6066
appName,
6167
browserName,
6268
t,
@@ -245,22 +251,41 @@ export class WalletService {
245251
this.resetConnectState();
246252
}
247253

248-
async disconnectWallet() {
254+
async disconnectWallet(force = false) {
249255
const { recurringGrant, oneTimeGrant } = await this.storage.get([
250256
'recurringGrant',
251257
'oneTimeGrant',
252258
]);
253259
if (!recurringGrant && !oneTimeGrant) {
254260
return;
255261
}
262+
263+
const handleError = (
264+
err: unknown,
265+
grantType: 'recurring' | 'oneTime',
266+
force: boolean,
267+
) => {
268+
this.logger.error(`Could not cancel ${grantType} grant`, { err });
269+
if (force) return;
270+
271+
if (isOpenPaymentsClientError(err)) {
272+
throw new ErrorWithKey('disconnectWallet_error_generic', [
273+
err.status ? `HTTP ${err.status} - ${err.message}` : err.message,
274+
]);
275+
}
276+
throw err;
277+
};
278+
256279
if (recurringGrant) {
257-
await this.outgoingPaymentGrantService.cancelGrant(
258-
recurringGrant.continue,
259-
);
280+
await this.outgoingPaymentGrantService
281+
.cancelGrant(recurringGrant.continue)
282+
.catch((err) => handleError(err, 'recurring', force));
260283
this.outgoingPaymentGrantService.disableRecurringGrant();
261284
}
262285
if (oneTimeGrant) {
263-
await this.outgoingPaymentGrantService.cancelGrant(oneTimeGrant.continue);
286+
await this.outgoingPaymentGrantService
287+
.cancelGrant(oneTimeGrant.continue)
288+
.catch((err) => handleError(err, 'recurring', force));
264289
this.outgoingPaymentGrantService.disableOneTimeGrant();
265290
}
266291
await this.storage.clear();

src/pages/popup/components/ErrorKeyRevoked.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { ReconnectWalletPayload, Response } from '@/shared/messages';
1111

1212
interface Props {
1313
info: Pick<PopupStore, 'publicKey' | 'walletAddress'>;
14-
disconnectWallet: () => Promise<Response>;
14+
disconnectWallet: (force: boolean) => Promise<Response>;
1515
reconnectWallet: (data: ReconnectWalletPayload) => Promise<Response>;
1616
onReconnect?: () => void;
1717
onDisconnect?: () => void;
@@ -90,7 +90,7 @@ const MainScreen = ({
9090
setErrorMsg('');
9191
try {
9292
setIsLoading(true);
93-
await disconnectWallet();
93+
await disconnectWallet(true);
9494
onDisconnect?.();
9595
} catch (error) {
9696
setErrorMsg(error.message);

src/pages/popup/components/Settings/WalletInformation.tsx

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { Input } from '@/pages/shared/components/ui/Input';
44
import { Label } from '@/pages/shared/components/ui/Label';
55
import { Code } from '@/pages/shared/components/ui/Code';
66
import { Button } from '@/pages/shared/components/ui/Button';
7-
import { useMessage } from '@/popup/lib/context';
7+
import { toErrorInfoFactory } from '@/pages/shared/lib/utils';
8+
import { useMessage, useTranslation } from '@/popup/lib/context';
89
import { ROUTES_PATH } from '@/popup/Popup';
910
import { useLocation } from 'wouter';
1011
import type { PopupStore } from '@/shared/types';
@@ -18,9 +19,32 @@ export const WalletInformation = ({
1819
publicKey,
1920
walletAddress,
2021
}: WalletInformationProps) => {
22+
const t = useTranslation();
2123
const message = useMessage();
2224
const [_location, navigate] = useLocation();
25+
const [disconnectError, setDisconnectError] = React.useState<string>('');
2326
const [isSubmitting, setIsSubmitting] = React.useState(false);
27+
const toErrorInfo = toErrorInfoFactory(t);
28+
29+
const disconnectWallet = async (force = false) => {
30+
setIsSubmitting(true);
31+
setDisconnectError('');
32+
try {
33+
const res = await message.send('DISCONNECT_WALLET', { force });
34+
if (!res.success) {
35+
if (res.error) {
36+
throw new Error(toErrorInfo(res.error)!.message);
37+
}
38+
throw new Error(res.message);
39+
}
40+
navigate(ROUTES_PATH.HOME);
41+
window.location.reload();
42+
} catch (error) {
43+
setDisconnectError(error.message);
44+
} finally {
45+
setIsSubmitting(false);
46+
}
47+
};
2448

2549
return (
2650
<div className="flex h-full flex-col gap-8">
@@ -29,10 +53,7 @@ export const WalletInformation = ({
2953
className="space-y-4"
3054
onSubmit={async (ev) => {
3155
ev.preventDefault();
32-
setIsSubmitting(true);
33-
await message.send('DISCONNECT_WALLET');
34-
navigate(ROUTES_PATH.HOME);
35-
window.location.reload();
56+
await disconnectWallet();
3657
}}
3758
>
3859
<Input
@@ -58,6 +79,19 @@ export const WalletInformation = ({
5879
>
5980
Disconnect
6081
</Button>
82+
83+
{disconnectError && (
84+
<p className="text-sm text-error !mt-1">
85+
{disconnectError}
86+
<button
87+
type="button"
88+
onClick={() => disconnectWallet(true)}
89+
className="ml-1 inline-block underline"
90+
>
91+
Force disconnect?
92+
</button>
93+
</p>
94+
)}
6195
</form>
6296

6397
<details className="border-t">

src/pages/popup/pages/ErrorKeyRevoked.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export default () => {
3232
info={{ publicKey, walletAddress }}
3333
reconnectWallet={(data) => message.send('RECONNECT_WALLET', data)}
3434
onReconnect={onReconnect}
35-
disconnectWallet={() => message.send('DISCONNECT_WALLET')}
35+
disconnectWallet={(force) => message.send('DISCONNECT_WALLET', { force })}
3636
onDisconnect={onDisconnect}
3737
/>
3838
);

src/shared/messages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export type PopupToBackgroundMessage = {
177177
output: never;
178178
};
179179
DISCONNECT_WALLET: {
180-
input: never;
180+
input: { force: boolean };
181181
output: never;
182182
};
183183
TOGGLE_CONTINUOUS_PAYMENTS: {

0 commit comments

Comments
 (0)