Skip to content

Commit

Permalink
Merge branch 'main' into Bibi/update-messages
Browse files Browse the repository at this point in the history
  • Loading branch information
sidvishnoi authored Oct 7, 2024
2 parents 34fdd8f + 795f604 commit 1d014cd
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 45 deletions.
29 changes: 22 additions & 7 deletions src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,6 @@
"connectWallet_action_connect": {
"message": "Connect"
},
"connectWallet_text_footerNotice": {
"message": "We will automatically connect with your wallet provider."
},
"connectWallet_text_footerNoticeLearnMore": {
"message": "Learn more",
"description": "Learn more about how this works"
},
"connectWallet_error_urlRequired": {
"message": "The wallet address is required."
},
Expand Down Expand Up @@ -172,9 +165,31 @@
"connectWallet_error_grantRejected": {
"message": "Wallet connection cancelled. The request was not authorized."
},
"connectWalletKeyService_text_consentP1": {
"message": "We'll automatically connect with your wallet provider."
},
"connectWalletKeyService_text_consentLearnMore": {
"message": "Learn more",
"description": "Learn more about how this works"
},
"connectWalletKeyService_text_consentP2": {
"message": "By agreeing, you provide us consent to automatically access your wallet to securely add a key."
},
"connectWalletKeyService_text_consentP3": {
"message": "Please note, this process does not involve accessing or handling your funds."
},
"connectWalletKeyService_label_consentAccept": {
"message": "Agree"
},
"connectWalletKeyService_label_consentDecline": {
"message": "Decline"
},
"connectWalletKeyService_error_notImplemented": {
"message": "Automatic key addition is not yet implemented for the selected wallet provider."
},
"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
3 changes: 2 additions & 1 deletion src/background/services/background.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Browser } from 'webextension-polyfill';
import type { ToBackgroundMessage } from '@/shared/messages';
import {
errorWithKeyToJSON,
failure,
getNextOccurrence,
getWalletInformation,
Expand Down Expand Up @@ -278,7 +279,7 @@ export class Background {
} catch (e) {
if (isErrorWithKey(e)) {
this.logger.error(message.action, e);
return failure({ key: e.key, substitutions: e.substitutions });
return failure(errorWithKeyToJSON(e));
}
if (e instanceof OpenPaymentsClientError) {
this.logger.error(message.action, e.message, e.description);
Expand Down
21 changes: 17 additions & 4 deletions src/background/services/keyAutoAdd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,14 @@ export class KeyAutoAddService {
this.browser.tabs.onRemoved.removeListener(onTabCloseListener);
const { stepName, details: err } = message.payload;
reject(
new ErrorWithKey('connectWalletKeyService_error_failed', [
stepName,
isErrorWithKey(err.error) ? this.t(err.error) : err.message,
]),
new ErrorWithKey(
'connectWalletKeyService_error_failed',
[
stepName,
isErrorWithKey(err.error) ? this.t(err.error) : err.message,
],
isErrorWithKey(err.error) ? err.error : undefined,
),
);
} else if (message.action === 'PROGRESS') {
// can also save progress to show in popup
Expand Down Expand Up @@ -168,6 +172,15 @@ export class KeyAutoAddService {
}));
}
}

static supports(walletAddress: WalletAddress): boolean {
try {
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
114 changes: 88 additions & 26 deletions src/popup/components/ConnectWalletForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface Inputs {
walletAddressUrl: string;
amount: string;
recurring: boolean;
autoKeyAddConsent: boolean;
}

type ErrorInfo = { message: string; info?: ErrorWithKeyLike };
Expand Down Expand Up @@ -69,6 +70,10 @@ export const ConnectWalletForm = ({
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 @@ -162,8 +167,8 @@ export const ConnectWalletForm = ({
[saveValue, currencySymbol, toErrorInfo],
);

const handleSubmit = async (ev: React.FormEvent<HTMLFormElement>) => {
ev.preventDefault();
const handleSubmit = async (ev?: React.FormEvent<HTMLFormElement>) => {
ev?.preventDefault();

const errWalletAddressUrl = validateWalletAddressUrl(walletAddressUrl);
const errAmount = validateAmount(amount, currencySymbol.symbol);
Expand All @@ -188,14 +193,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((prev) => ({ ...prev, keyPair: toErrorInfo(error) }));
} else {
setErrors((prev) => ({ ...prev, connect: toErrorInfo(error) }));
Expand Down Expand Up @@ -225,6 +235,24 @@ export const ConnectWalletForm = ({
}
}, [defaultValues.walletAddressUrl, handleWalletAddressUrlChange]);

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

return (
<form
data-testid="connect-wallet-form"
Expand Down Expand Up @@ -350,6 +378,7 @@ export const ConnectWalletForm = ({
details: errors.keyPair,
whyText: t('connectWallet_error_failedAutoKeyAddWhy'),
}}
retry={resetState}
hideError={!errors.keyPair}
text={t('connectWallet_label_publicKey')}
learnMoreText={t('connectWallet_text_publicKeyLearnMore')}
Expand Down Expand Up @@ -379,13 +408,46 @@ export const ConnectWalletForm = ({
>
{t('connectWallet_action_connect')}
</Button>
</div>
</form>
);
};

{!errors.keyPair && !autoKeyShareFailed && (
<Footer
text={t('connectWallet_text_footerNotice')}
learnMoreText={t('connectWallet_text_footerNoticeLearnMore')}
/>
)}
const AutoKeyAddConsent: React.FC<{
onAccept: () => void;
onDecline: () => void;
}> = ({ onAccept, onDecline }) => {
const t = useTranslation();
return (
<form
className="space-y-4 text-center"
data-testid="connect-wallet-auto-key-consent"
>
<p className="text-lg leading-snug text-weak">
{t('connectWalletKeyService_text_consentP1')}{' '}
<a
hidden
href="https://webmonetization.org"
className="text-primary hover:underline"
target="_blank"
rel="noreferrer"
>
{t('connectWalletKeyService_text_consentLearnMore')}
</a>
</p>

<div className="space-y-2 pt-12 text-medium">
<p>{t('connectWalletKeyService_text_consentP2')}</p>
<p>{t('connectWalletKeyService_text_consentP3')}</p>
</div>

<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>
);
Expand All @@ -394,10 +456,11 @@ export const ConnectWalletForm = ({
const ManualKeyPairNeeded: React.FC<{
error: { message: string; details: null | ErrorInfo; whyText: string };
hideError?: boolean;
retry: () => Promise<void>;
text: string;
learnMoreText: string;
publicKey: string;
}> = ({ error, hideError, text, learnMoreText, publicKey }) => {
}> = ({ error, hideError, text, learnMoreText, publicKey, retry }) => {
const ErrorDetails = () => {
if (!error || !error.details) return null;
return (
Expand All @@ -406,6 +469,15 @@ const ManualKeyPairNeeded: React.FC<{
{error.whyText}
</summary>
<span>{error.details.message}</span>
{canRetryAutoKeyAdd(error.details.info) && (
<button
type="button"
onClick={retry}
className="ml-1 inline-block text-primary underline"
>
Try again?
</button>
)}
</details>
);
};
Expand Down Expand Up @@ -448,24 +520,14 @@ function isAutoKeyAddFailed(state: PopupTransientState['connect']) {
return false;
}

const Footer: React.FC<{
text: string;
learnMoreText: string;
}> = ({ text, learnMoreText }) => {
function canRetryAutoKeyAdd(err?: ErrorInfo['info']) {
if (!err) return false;
return (
<p className="text-center text-xs text-weak">
{text}{' '}
<a
href="https://webmonetization.org"
className="text-primary hover:underline"
target="_blank"
rel="noreferrer"
>
{learnMoreText}
</a>
</p>
err.key === 'connectWalletKeyService_error_noConsent' ||
err.cause?.key === 'connectWalletKeyService_error_timeoutLogin' ||
err.cause?.key === 'connectWalletKeyService_error_accountNotFound'
);
};
}

function validateWalletAddressUrl(value: string): null | ErrorWithKeyLike {
if (!value) {
Expand Down
2 changes: 2 additions & 0 deletions src/popup/pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const Component = () => {
amount: localStorage?.getItem('connect.amount') || undefined,
walletAddressUrl:
localStorage?.getItem('connect.walletAddressUrl') || undefined,
autoKeyAddConsent:
localStorage?.getItem('connect.autoKeyAddConsent') === 'true',
}}
saveValue={(key, val) => {
localStorage?.setItem(`connect.${key}`, val.toString());
Expand Down
10 changes: 7 additions & 3 deletions src/shared/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface ErrorWithKeyLike<T extends ErrorKeys = ErrorKeys> {
key: Extract<ErrorKeys, T>;
// Could be empty, but required for checking if an object follows this interface
substitutions: string[];
cause?: ErrorWithKeyLike;
}

export class ErrorWithKey<T extends ErrorKeys = ErrorKeys>
Expand All @@ -83,8 +84,9 @@ export class ErrorWithKey<T extends ErrorKeys = ErrorKeys>
constructor(
public readonly key: ErrorWithKeyLike<T>['key'],
public readonly substitutions: ErrorWithKeyLike<T>['substitutions'] = [],
public readonly cause?: ErrorWithKeyLike,
) {
super(key);
super(key, { cause });
}
}

Expand All @@ -96,7 +98,8 @@ export class ErrorWithKey<T extends ErrorKeys = ErrorKeys>
export const errorWithKey = <T extends ErrorKeys = ErrorKeys>(
key: ErrorWithKeyLike<T>['key'],
substitutions: ErrorWithKeyLike<T>['substitutions'] = [],
) => ({ key, substitutions });
cause?: ErrorWithKeyLike,
) => ({ key, substitutions, cause });

export const isErrorWithKey = (err: any): err is ErrorWithKeyLike => {
if (!err || typeof err !== 'object') return false;
Expand All @@ -107,7 +110,8 @@ export const isErrorWithKey = (err: any): err is ErrorWithKeyLike => {
};

export const errorWithKeyToJSON = (err: ErrorWithKeyLike): ErrorWithKeyLike => {
return { key: err.key, substitutions: err.substitutions };
const { key, substitutions, cause } = err;
return { key, substitutions, cause };
};

export const success = <TPayload = undefined>(
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
Loading

0 comments on commit 1d014cd

Please sign in to comment.