Skip to content

Commit

Permalink
feat(ConnectWalletForm): ask consent for automatic key addition
Browse files Browse the repository at this point in the history
  • Loading branch information
sidvishnoi committed Oct 2, 2024
1 parent e41f23e commit fe6d3b4
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 11 deletions.
12 changes: 12 additions & 0 deletions src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,21 @@
"connectWallet_error_grantRejected": {
"message": "Connect wallet cancelled. You rejected the request."
},
"connectWalletKeyService_text_consent": {
"message": "By agreeing, you provide us consent to automatically access your wallet to securely add a key. Please note, this process does not involve accessing or handling your funds."
},
"connectWalletKeyService_label_consentAccept": {
"message": "Accept"
},
"connectWalletKeyService_label_consentDecline": {
"message": "Decline"
},
"connectWalletKeyService_error_notImplemented": {
"message": "Automatic key addition is not implemented for given wallet provider yet."
},
"connectWalletKeyService_error_noConsent": {
"message": "You declined consent for automatic key addition."
},
"connectWalletKeyService_error_failed": {
"message": "Automatic key addition failed at step “$STEP_ID$” with message “$MESSAGE$”.",
"placeholders": {
Expand Down
9 changes: 9 additions & 0 deletions src/background/services/keyAutoAdd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@ export class KeyAutoAddService {
}));
}
}

static supports(walletAddress: WalletAddress): boolean {
try {
void walletAddressToProvider(walletAddress);
return true;
} catch {
return false;
}
}
}

export function walletAddressToProvider(walletAddress: WalletAddress): {
Expand Down
21 changes: 19 additions & 2 deletions src/background/services/openPayments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,13 @@ export class OpenPaymentsService {
this.setConnectState(null);
return;
}
const { walletAddressUrl, amount, recurring, skipAutoKeyShare } = params;
const {
walletAddressUrl,
amount,
recurring,
autoKeyAdd,
autoKeyAddConsent,
} = params;

const walletAddress = await getWalletInformation(walletAddressUrl);
const exchangeRates = await getExchangeRates();
Expand Down Expand Up @@ -385,8 +391,19 @@ export class OpenPaymentsService {
if (
isErrorWithKey(error) &&
error.key === 'connectWallet_error_invalidClient' &&
!skipAutoKeyShare
autoKeyAdd
) {
if (!KeyAutoAddService.supports(walletAddress)) {
this.updateConnectStateError(error);
throw new ErrorWithKey(
'connectWalletKeyService_error_notImplemented',
);
}
if (!autoKeyAddConsent) {
this.updateConnectStateError(error);
throw new ErrorWithKey('connectWalletKeyService_error_noConsent');
}

// add key to wallet and try again
try {
const tabId = await this.addPublicKeyToWallet(walletAddress);
Expand Down
62 changes: 55 additions & 7 deletions src/popup/components/ConnectWalletForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface Inputs {
walletAddressUrl: string;
amount: string;
recurring: boolean;
autoKeyAddConsent: boolean;
}

interface ConnectWalletFormProps {
Expand Down Expand Up @@ -62,9 +63,14 @@ export const ConnectWalletForm = ({
const [recurring, setRecurring] = React.useState<Inputs['recurring']>(
defaultValues.recurring || false,
);

const [autoKeyShareFailed, setAutoKeyShareFailed] = React.useState(
isAutoKeyAddFailed(state),
);
const [showConsent, setShowConsent] = React.useState(false);
const autoKeyAddConsent = React.useRef<boolean>(
defaultValues.autoKeyAddConsent || false,
);

const resetState = React.useCallback(async () => {
await clearConnectState();
Expand Down Expand Up @@ -156,12 +162,8 @@ export const ConnectWalletForm = ({
[saveValue, currencySymbol, t],
);

const handleSubmit = async (ev: React.FormEvent<HTMLFormElement>) => {
ev.preventDefault();
if (!walletAddressInfo) {
setErrors((_) => ({ ..._, walletAddressUrl: 'Not fetched yet?!' }));
return;
}
const handleSubmit = async (ev?: React.FormEvent<HTMLFormElement>) => {
ev?.preventDefault();

const errWalletAddressUrl = validateWalletAddressUrl(walletAddressUrl);
const errAmount = validateAmount(amount, currencySymbol.symbol);
Expand All @@ -186,14 +188,19 @@ export const ConnectWalletForm = ({
walletAddressUrl: toWalletAddressUrl(walletAddressUrl),
amount,
recurring,
skipAutoKeyShare,
autoKeyAdd: !skipAutoKeyShare,
autoKeyAddConsent: autoKeyAddConsent.current,
});
if (res.success) {
onConnect();
} else {
if (isErrorWithKey(res.error)) {
const error = res.error;
if (error.key.startsWith('connectWalletKeyService_error_')) {
if (error.key === 'connectWalletKeyService_error_noConsent') {
setShowConsent(true);
return;
}
setErrors((_) => ({ ..._, keyPair: t(error) }));
} else {
setErrors((_) => ({ ..._, connect: t(error) }));
Expand Down Expand Up @@ -223,6 +230,26 @@ export const ConnectWalletForm = ({
}
}, [defaultValues.walletAddressUrl, handleWalletAddressUrlChange]);

if (showConsent) {
return (
<AutoKeyAddConsent
onAccept={() => {
autoKeyAddConsent.current = true;
// saveValue('autoKeyAddConsent', true);
setShowConsent(false);
handleSubmit();
}}
onDecline={() => {
setErrors((_) => ({
..._,
keyPair: t('connectWalletKeyService_error_noConsent'),
}));
setShowConsent(false);
}}
/>
);
}

return (
<form
data-testid="connect-wallet-form"
Expand Down Expand Up @@ -389,6 +416,27 @@ export const ConnectWalletForm = ({
);
};

const AutoKeyAddConsent: React.FC<{
onAccept: () => void;
onDecline: () => void;
}> = ({ onAccept, onDecline }) => {
const t = useTranslation();
return (
<form className="space-y-4" data-testid="connect-wallet-auto-key-consent">
<p className="text-medium">{t('connectWalletKeyService_text_consent')}</p>

<div className="mx-auto flex w-3/4 justify-around gap-4">
<Button onClick={onAccept}>
{t('connectWalletKeyService_label_consentAccept')}
</Button>
<Button onClick={onDecline} variant="destructive">
{t('connectWalletKeyService_label_consentDecline')}
</Button>
</div>
</form>
);
};

const ManualKeyPairNeeded: React.FC<{
error: { message: string; details: string; whyText: string };
hideError?: boolean;
Expand Down
3 changes: 3 additions & 0 deletions src/popup/pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export const Component = () => {
amount: localStorage?.getItem('connect.amount') || undefined,
walletAddressUrl:
localStorage?.getItem('connect.walletAddressUrl') || undefined,
autoKeyAddConsent:
localStorage?.getItem('connect.autoKeyAddConsent') === 'true' ||
false,
}}
saveValue={(key, val) => {
localStorage?.setItem(`connect.${key}`, val.toString());
Expand Down
3 changes: 2 additions & 1 deletion src/shared/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export interface ConnectWalletPayload {
walletAddressUrl: string;
amount: string;
recurring: boolean;
skipAutoKeyShare: boolean;
autoKeyAdd: boolean;
autoKeyAddConsent: boolean | null;
}

export interface AddFundsPayload {
Expand Down
10 changes: 9 additions & 1 deletion tests/e2e/connectAutoKeyTestWallet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,17 @@ test('Connect to test wallet with automatic key addition when not logged-in to w
await page.close();
});

page = await test.step('shows login page', async () => {
await test.step('asks for key-add consent', async () => {
await connectButton.click();
await popup.waitForSelector(
`[data-testid="connect-wallet-auto-key-consent"]`,
);

expect(popup.getByTestId('connect-wallet-auto-key-consent')).toBeVisible();
await popup.getByRole('button', { name: 'Accept' }).click();
});

page = await test.step('shows login page', async () => {
const openedPage = await context.waitForEvent('page', {
predicate: (page) => page.url().startsWith(loginPageUrl),
timeout: 3 * 1000,
Expand Down

0 comments on commit fe6d3b4

Please sign in to comment.