Skip to content

Commit

Permalink
refactor(bg/openPayments): use Promise.withResolvers
Browse files Browse the repository at this point in the history
  • Loading branch information
sidvishnoi committed Sep 26, 2024
1 parent dd58393 commit 1559718
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 41 deletions.
88 changes: 47 additions & 41 deletions src/background/services/openPayments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ import type { Browser, Tabs } from 'webextension-polyfill';
import { getExchangeRates, getRateOfPay, toAmount } from '../utils';
import { exportJWK, generateEd25519KeyPair } from '@/shared/crypto';
import { bytesToHex } from '@noble/hashes/utils';
import { ErrorWithKey, getWalletInformation } from '@/shared/helpers';
import {
ErrorWithKey,
getWalletInformation,
withResolvers,
} from '@/shared/helpers';
import { AddFundsPayload, ConnectWalletPayload } from '@/shared/messages';
import {
DEFAULT_RATE_OF_PAY,
Expand Down Expand Up @@ -609,48 +613,50 @@ export class OpenPaymentsService {
}

private async getInteractionInfo(url: string): Promise<InteractionParams> {
return await new Promise((resolve, reject) => {
this.browser.tabs.create({ url }).then((tab) => {
if (!tab.id) return;
const tabCloseListener: TabRemovedCallback = (tabId) => {
if (tabId !== tab.id) return;
const { resolve, reject, promise } = withResolvers<InteractionParams>();

const tab = await this.browser.tabs.create({ url });
if (!tab.id) {
reject(new Error('Could not create tab'));
return promise;
}

const tabCloseListener: TabRemovedCallback = (tabId) => {
if (tabId !== tab.id) return;

this.browser.tabs.onRemoved.removeListener(tabCloseListener);
reject(new ErrorWithKey('connectWallet_error_tabClosed'));
};

const getInteractionInfo: TabUpdateCallback = async (tabId, changeInfo) => {
if (tabId !== tab.id) return;
try {
const tabUrl = new URL(changeInfo.url || '');
const interactRef = tabUrl.searchParams.get('interact_ref');
const hash = tabUrl.searchParams.get('hash');
const result = tabUrl.searchParams.get('result');

if (
(interactRef && hash) ||
result === 'grant_rejected' ||
result === 'grant_invalid'
) {
this.browser.tabs.onUpdated.removeListener(getInteractionInfo);
this.browser.tabs.onRemoved.removeListener(tabCloseListener);
reject(new ErrorWithKey('connectWallet_error_tabClosed'));
};

const getInteractionInfo: TabUpdateCallback = async (
tabId,
changeInfo,
) => {
if (tabId !== tab.id) return;
try {
const tabUrl = new URL(changeInfo.url || '');
const interactRef = tabUrl.searchParams.get('interact_ref');
const hash = tabUrl.searchParams.get('hash');
const result = tabUrl.searchParams.get('result');

if (
(interactRef && hash) ||
result === 'grant_rejected' ||
result === 'grant_invalid'
) {
this.browser.tabs.onUpdated.removeListener(getInteractionInfo);
this.browser.tabs.onRemoved.removeListener(tabCloseListener);
}

if (interactRef && hash) {
resolve({ interactRef, hash, tabId });
}
} catch {
/* do nothing */
}
};

this.browser.tabs.onRemoved.addListener(tabCloseListener);
this.browser.tabs.onUpdated.addListener(getInteractionInfo);
});
});
}

if (interactRef && hash) {
resolve({ interactRef, hash, tabId });
}
} catch {
/* do nothing */
}
};

this.browser.tabs.onRemoved.addListener(tabCloseListener);
this.browser.tabs.onUpdated.addListener(getInteractionInfo);

return promise;
}

async disconnectWallet() {
Expand Down
17 changes: 17 additions & 0 deletions src/shared/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
objectEquals,
removeQueryParams,
getNextOccurrence,
withResolvers,
} from './helpers';

describe('objectEquals', () => {
Expand Down Expand Up @@ -34,6 +35,22 @@ describe('removeQueryParams', () => {
});
});

describe('withResolvers', () => {
it('resolves', async () => {
const r = withResolvers<boolean>();
r.resolve(true);
r.reject(false);
await expect(r.promise).resolves.toBe(true);
});

it('rejects', async () => {
const r = withResolvers<boolean>();
r.reject(false);
r.resolve(true);
await expect(r.promise).rejects.toBe(false);
});
});

describe('isOkState', () => {
it('should return true if no state is set', () => {
expect(isOkState({})).toBe(true);
Expand Down
14 changes: 14 additions & 0 deletions src/shared/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,20 @@ export const removeQueryParams = (urlString: string) => {
return url.origin + url.pathname;
};

/**
* Polyfill for `Promise.withResolvers()`
*/
export function withResolvers<T>() {
let resolve: (value: T | PromiseLike<T>) => void;
let reject: (reason?: any) => void;
const promise = new Promise<T>((res, rej) => {
resolve = res;
reject = rej;
});
// @ts-expect-error we know TypeScript!
return { resolve, reject, promise };
}

export const isOkState = (state: Storage['state']) => {
return Object.values(state).every((value) => value === false);
};
Expand Down

0 comments on commit 1559718

Please sign in to comment.