diff options
Diffstat (limited to 'packages/taler-wallet-webextension')
9 files changed, 222 insertions, 150 deletions
diff --git a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx index 372ca7cb7..c94010ede 100644 --- a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx +++ b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx @@ -42,7 +42,10 @@ interface Props extends JSX.HTMLAttributes { */ const cache = { tx: [] as Transaction[] }; -export function PendingTransactions({ goToTransaction, goToURL }: Props): VNode { +export function PendingTransactions({ + goToTransaction, + goToURL, +}: Props): VNode { const api = useBackendContext(); const state = useAsyncAsHook(() => api.wallet.call(WalletApiOperation.GetTransactions, {}), @@ -59,8 +62,8 @@ export function PendingTransactions({ goToTransaction, goToURL }: Props): VNode !state || state.hasError ? cache.tx : state.response.transactions.filter( - (t) => t.txState.major === TransactionMajorState.Pending, - ); + (t) => t.txState.major === TransactionMajorState.Pending, + ); if (state && !state.hasError) { cache.tx = transactions; @@ -87,50 +90,52 @@ export function PendingTransactionsView({ transactions: Transaction[]; }): VNode { const { i18n } = useTranslationContext(); - const kycTransaction = transactions.find(tx => tx.kycUrl) + const kycTransaction = transactions.find((tx) => tx.kycUrl); if (kycTransaction) { - return <div - style={{ - backgroundColor: "lightcyan", - display: "flex", - justifyContent: "center", - }} - > - <Banner - titleHead={i18n.str`KYC requirement`} + return ( + <div style={{ - backgroundColor: "lightred", - maxHeight: 150, - padding: 8, - flexGrow: 1, - maxWidth: 500, - overflowY: transactions.length > 3 ? "scroll" : "hidden", + backgroundColor: "#fff3cd", + color: "#664d03", + display: "flex", + justifyContent: "center", }} > - <Grid - container - item - xs={1} - wrap="nowrap" - role="button" - spacing={1} - alignItems="center" - onClick={() => { - goToURL(kycTransaction.kycUrl ?? "#") + <Banner + titleHead={i18n.str`KYC requirement`} + style={{ + backgroundColor: "lightred", + maxHeight: 150, + padding: 8, + flexGrow: 1, //#fff3cd //#ffecb5 + maxWidth: 500, + overflowY: transactions.length > 3 ? "scroll" : "hidden", }} > - <Grid item> - <Typography inline bold> - One or more transaction require a KYC step to complete - </Typography> + <Grid + container + item + xs={1} + wrap="nowrap" + role="button" + spacing={1} + alignItems="center" + onClick={() => { + goToURL(kycTransaction.kycUrl ?? "#"); + }} + > + <Grid item> + <Typography inline bold> + One or more transaction require a KYC step to complete + </Typography> + </Grid> </Grid> - - </Grid> - </Banner> - </div> + </Banner> + </div> + ); } - if (!goToTransaction) return <Fragment /> + if (!goToTransaction) return <Fragment />; return ( <div diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts index e92903981..3c116fab2 100644 --- a/packages/taler-wallet-webextension/src/platform/api.ts +++ b/packages/taler-wallet-webextension/src/platform/api.ts @@ -18,11 +18,9 @@ import { CoreApiResponse, TalerUri, WalletNotification, - WalletRunConfig + WalletRunConfig, } from "@gnu-taler/taler-util"; -import { - WalletOperations -} from "@gnu-taler/taler-wallet-core"; +import { WalletOperations } from "@gnu-taler/taler-wallet-core"; import { ExtensionOperations, MessageFromExtension, @@ -46,11 +44,9 @@ export interface Permissions { * Compatibility API that works on multiple browsers. */ export interface CrossBrowserPermissionsApi { - containsClipboardPermissions(): Promise<boolean>; requestClipboardPermissions(): Promise<boolean>; removeClipboardPermissions(): Promise<boolean>; - } export enum ExtensionNotificationType { @@ -67,25 +63,29 @@ export interface ClearNotificaitonNotification { type: ExtensionNotificationType.ClearNotifications; } -export type ExtensionNotification = SettingsChangeNotification | ClearNotificaitonNotification +export type ExtensionNotification = + | SettingsChangeNotification + | ClearNotificaitonNotification; -export type MessageFromBackend = { - type: "wallet", - notification: WalletNotification -} | { - type: "web-extension", - notification: ExtensionNotification -}; +export type MessageFromBackend = + | { + type: "wallet"; + notification: WalletNotification; + } + | { + type: "web-extension"; + notification: ExtensionNotification; + }; export type MessageFromFrontend< Op extends BackgroundOperations | WalletOperations | ExtensionOperations, > = Op extends BackgroundOperations ? MessageFromFrontendBackground<keyof BackgroundOperations> : Op extends ExtensionOperations - ? MessageFromExtension<keyof ExtensionOperations> - : Op extends WalletOperations - ? MessageFromFrontendWallet<keyof WalletOperations> - : never; + ? MessageFromExtension<keyof ExtensionOperations> + : Op extends WalletOperations + ? MessageFromFrontendWallet<keyof WalletOperations> + : never; export type MessageFromFrontendBackground< Op extends keyof BackgroundOperations, @@ -109,7 +109,6 @@ export interface WalletWebExVersion { } type F = WalletRunConfig["features"]; -type kf = keyof F; type WebexWalletConfig = { [P in keyof F as `wallet${Capitalize<P>}`]: F[P]; }; @@ -231,7 +230,13 @@ export interface BackgroundPlatformAPI { ) => Promise<MessageResponse>, ): void; + /** + * Change web extension Icon + */ + setAlertedIcon(): void; + setNormalIcon(): void; } + export interface ForegroundPlatformAPI { /** * Check if the extension is running under @@ -270,7 +275,7 @@ export interface ForegroundPlatformAPI { /** * Open a page and close the popup - * @param url + * @param url */ openNewURLFromPopup(url: URL): void; /** @@ -309,9 +314,9 @@ export interface ForegroundPlatformAPI { ): Promise<MessageResponse>; /** - * Used by the wallet frontend to send notification about new information - * @param message - */ + * Used by the wallet frontend to send notification about new information + * @param message + */ triggerWalletEvent(message: MessageFromBackend): void; /** diff --git a/packages/taler-wallet-webextension/src/platform/background.ts b/packages/taler-wallet-webextension/src/platform/background.ts index 9f3764c25..13808af2b 100644 --- a/packages/taler-wallet-webextension/src/platform/background.ts +++ b/packages/taler-wallet-webextension/src/platform/background.ts @@ -16,7 +16,8 @@ import { BackgroundPlatformAPI } from "./api.js"; -export let platform: BackgroundPlatformAPI = undefined as any; +// it should never be undefined :) +export let platform: BackgroundPlatformAPI = undefined!; export function setupPlatform(impl: BackgroundPlatformAPI): void { platform = impl; } diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts index 6c5510eb6..e63040f5c 100644 --- a/packages/taler-wallet-webextension/src/platform/chrome.ts +++ b/packages/taler-wallet-webextension/src/platform/chrome.ts @@ -53,7 +53,7 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { redirectTabToWalletPage, registerAllIncomingConnections, registerOnInstalled, - listenToAllChannels: listenToAllChannels as any, + listenToAllChannels , registerReloadOnNewVersion, sendMessageToAllChannels, openNewURLFromPopup, @@ -61,6 +61,8 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { useServiceWorkerAsBackgroundProcess, keepAlive, listenNetworkConnectionState, + setAlertedIcon, + setNormalIcon, }; export default api; @@ -69,7 +71,7 @@ const logger = new Logger("chrome.ts"); const WALLET_STORAGE_KEY = "wallet-settings"; -function jsonParseOrDefault(unparsed: any, def: any) { +function jsonParseOrDefault(unparsed: string, def: unknown) { if (!unparsed) return def; try { return JSON.parse(unparsed); @@ -85,7 +87,7 @@ async function getSettingsFromStorage(): Promise<Settings> { return jsonParseOrDefault(settings, defaultSettings); } -function keepAlive(callback: any): void { +function keepAlive(callback: () => void): void { if (extensionIsManifestV3()) { chrome.alarms.create("wallet-worker", { periodInMinutes: 1 }); @@ -103,7 +105,7 @@ function isFirefox(): boolean { } export function containsClipboardPermissions(): Promise<boolean> { - return new Promise((res, rej) => { + return new Promise((res) => { res(false); // chrome.permissions.contains({ permissions: ["clipboardRead"] }, (resp) => { // const le = chrome.runtime.lastError?.message; @@ -116,7 +118,7 @@ export function containsClipboardPermissions(): Promise<boolean> { } export async function requestClipboardPermissions(): Promise<boolean> { - return new Promise((res, rej) => { + return new Promise((res) => { res(false); // chrome.permissions.request({ permissions: ["clipboardRead"] }, (resp) => { // const le = chrome.runtime.lastError?.message; @@ -129,7 +131,7 @@ export async function requestClipboardPermissions(): Promise<boolean> { } export function removeClipboardPermissions(): Promise<boolean> { - return new Promise((res, rej) => { + return new Promise((res) => { res(true); // chrome.permissions.remove({ permissions: ["clipboardRead"] }, (resp) => { // const le = chrome.runtime.lastError?.message; @@ -154,7 +156,7 @@ function getPermissionsApi(): CrossBrowserPermissionsApi { * @param callback function to be called */ function notifyWhenAppIsReady(): Promise<void> { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { if (extensionIsManifestV3()) { resolve(); } else { @@ -276,7 +278,7 @@ async function sendMessageToBackground< nextMessageIndex = (nextMessageIndex + 1) % (Number.MAX_SAFE_INTEGER - 100); const messageWithId = { ...message, id: `id_${nextMessageIndex}` }; - return new Promise<any>((resolve, reject) => { + return new Promise<MessageResponse>((resolve, reject) => { logger.trace("send operation to the wallet background", message); let timedout = false; const timerId = setTimeout(() => { @@ -307,7 +309,7 @@ async function sendMessageToBackground< * To be used by the foreground */ let notificationPort: chrome.runtime.Port | undefined; -function listenToWalletBackground(listener: (m: any) => void): () => void { +function listenToWalletBackground(listener: (message: MessageFromBackend) => void): () => void { if (notificationPort === undefined) { notificationPort = chrome.runtime.connect({ name: "notifications" }); } @@ -369,7 +371,7 @@ function registerAllIncomingConnections(): void { notification: { type: ExtensionNotificationType.SettingsChange, currentValue: jsonParseOrDefault( - event[WALLET_STORAGE_KEY], + event[WALLET_STORAGE_KEY].newValue, defaultSettings, ), }, @@ -415,12 +417,12 @@ function registerReloadOnNewVersion(): void { }); } -async function redirectCurrentTabToWalletPage(page: string): Promise<void> { - let queryOptions = { active: true, lastFocusedWindow: true }; - let [tab] = await chrome.tabs.query(queryOptions); +// async function redirectCurrentTabToWalletPage(page: string): Promise<void> { +// let queryOptions = { active: true, lastFocusedWindow: true }; +// let [tab] = await chrome.tabs.query(queryOptions); - return redirectTabToWalletPage(tab.id!, page); -} +// return redirectTabToWalletPage(tab.id!, page); +// } async function redirectTabToWalletPage( tabId: number, @@ -666,7 +668,7 @@ async function findTalerUriInTab(tabId: number): Promise<string | undefined> { return; } } else { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { //manifest v2 chrome.tabs.executeScript( tabId, @@ -692,9 +694,9 @@ async function findTalerUriInTab(tabId: number): Promise<string | undefined> { } } -async function timeout(ms: number): Promise<void> { - return new Promise((resolve) => setTimeout(resolve, ms)); -} +// async function timeout(ms: number): Promise<void> { +// return new Promise((resolve) => setTimeout(resolve, ms)); +// } async function findTalerUriInClipboard(): Promise<string | undefined> { //FIXME: add clipboard feature // try { diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts b/packages/taler-wallet-webextension/src/platform/dev.ts index 1e43476ea..d6e743147 100644 --- a/packages/taler-wallet-webextension/src/platform/dev.ts +++ b/packages/taler-wallet-webextension/src/platform/dev.ts @@ -38,6 +38,8 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { listenNetworkConnectionState, openNewURLFromPopup: () => undefined, triggerWalletEvent: () => undefined, + setAlertedIcon: () => undefined, + setNormalIcon : () => undefined, getPermissionsApi: () => ({ containsClipboardPermissions: async () => true, removeClipboardPermissions: async () => false, diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx index 2d80e4c36..0d0a31a2d 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx @@ -274,6 +274,7 @@ function AdvanceSettings(): VNode { <Checkbox label={label} name={name} + key={name} description={description} enabled={settings[settingsName]} onToggle={async () => { diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index af6868434..1f0293352 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -242,53 +242,56 @@ function TransactionTemplate({ )} /> ) : undefined} - {transaction.txState.minor === TransactionMinorState.KycRequired && ( - <AlertView - alert={{ - type: "warning", - message: i18n.str`KYC check required for the transaction to complete.`, - description: - transaction.kycUrl && typeof transaction.kycUrl === "string" ? ( - <div> - <i18n.Translate> - Follow this link to the{` `} - <a - rel="noreferrer" - target="_bank" - href={transaction.kycUrl} - > - KYC verifier. - </a> - </i18n.Translate> - </div> - ) : ( - i18n.str`No additional information has been provided.` - ), - }} - /> - )} - {transaction.txState.minor === TransactionMinorState.AmlRequired && ( - <WarningBox> - <i18n.Translate> - The transaction has been blocked since the account required an AML - check. - </i18n.Translate> - </WarningBox> - )} - {transaction.txState.major === TransactionMajorState.Pending && ( - <WarningBox> - <div style={{ justifyContent: "center", lineHeight: "25px" }}> - <i18n.Translate>This transaction is not completed</i18n.Translate> - <Link onClick={onRetry} style={{ padding: 0 }}> - <SvgIcon - title={i18n.str`Retry`} - dangerouslySetInnerHTML={{ __html: refreshIcon }} - color="black" - /> - </Link> - </div> - </WarningBox> - )} + {transaction.txState.major === TransactionMajorState.Pending && + (transaction.txState.minor === TransactionMinorState.KycRequired ? ( + <AlertView + alert={{ + type: "warning", + message: i18n.str`KYC check required for the transaction to complete.`, + description: + transaction.kycUrl && + typeof transaction.kycUrl === "string" ? ( + <div> + <i18n.Translate> + Follow this link to the{` `} + <a + rel="noreferrer" + target="_bank" + href={transaction.kycUrl} + > + KYC verifier. + </a> + </i18n.Translate> + </div> + ) : ( + i18n.str`No additional information has been provided.` + ), + }} + /> + ) : transaction.txState.minor === + TransactionMinorState.AmlRequired ? ( + <WarningBox> + <i18n.Translate> + The transaction has been blocked since the account required an + AML check. + </i18n.Translate> + </WarningBox> + ) : ( + <WarningBox> + <div style={{ justifyContent: "center", lineHeight: "25px" }}> + <i18n.Translate> + This transaction is not completed + </i18n.Translate> + <Link onClick={onRetry} style={{ padding: 0 }}> + <SvgIcon + title={i18n.str`Retry`} + dangerouslySetInnerHTML={{ __html: refreshIcon }} + color="black" + /> + </Link> + </div> + </WarningBox> + ))} {transaction.txState.major === TransactionMajorState.Aborted && ( <InfoBox> <i18n.Translate>This transaction was aborted.</i18n.Translate> diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index 5588eb55e..195efecd4 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -31,8 +31,7 @@ import { TalerError, TalerErrorCode, TalerErrorDetail, - WalletDiagnostics, - WalletNotification, + WalletNotification } from "@gnu-taler/taler-util"; import { WalletCoreApiClient, diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts index a78e85aba..315ab5332 100644 --- a/packages/taler-wallet-webextension/src/wxBackend.ts +++ b/packages/taler-wallet-webextension/src/wxBackend.ts @@ -25,24 +25,30 @@ */ import { AbsoluteTime, + BalanceFlag, LogLevel, Logger, + NotificationType, OpenedPromise, SetTimeoutTimerAPI, TalerError, TalerErrorCode, TalerErrorDetail, + TransactionMajorState, + TransactionMinorState, + WalletNotification, getErrorDetailFromException, makeErrorDetail, openPromise, setGlobalLogLevelFromString, - setLogLevelFromString + setLogLevelFromString, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary } from "@gnu-taler/taler-util/http"; import { DbAccess, SynchronousCryptoWorkerFactoryPlain, Wallet, + WalletApiOperation, WalletOperations, WalletStoresV1, deleteTalerDatabase, @@ -65,11 +71,6 @@ let currentWallet: Wallet | undefined; let currentDatabase: DbAccess<typeof WalletStoresV1> | undefined; -/** - * Last version of an outdated DB, if applicable. - */ -let outdatedDbVersion: number | undefined; - const walletInit: OpenedPromise<void> = openPromise<void>(); const logger = new Logger("wxBackend.ts"); @@ -92,16 +93,15 @@ async function resetDb(): Promise<void> { } //FIXME: maybe circular buffer -const notifications: WalletEvent[] = [] +const notifications: WalletEvent[] = []; async function getNotifications(): Promise<WalletEvent[]> { - return notifications + return notifications; } async function clearNotifications(): Promise<void> { - notifications.splice(0, notifications.length) + notifications.splice(0, notifications.length); } - async function runGarbageCollector(): Promise<void> { const dbBeforeGc = currentDatabase; if (!dbBeforeGc) { @@ -229,8 +229,10 @@ async function dispatch< case "wallet": { const w = currentWallet; if (!w) { - const lastError: TalerErrorDetail = walletInit.lastError instanceof TalerError ? - walletInit.lastError.errorDetail : undefined + const lastError: TalerErrorDetail = + walletInit.lastError instanceof TalerError + ? walletInit.lastError.errorDetail + : undefined; return { type: "error", @@ -239,16 +241,22 @@ async function dispatch< error: makeErrorDetail( TalerErrorCode.WALLET_CORE_NOT_AVAILABLE, { lastError }, - `wallet core not available${!lastError ? "" : `,last error: ${lastError.hint}`}`, + `wallet core not available${ + !lastError ? "" : `,last error: ${lastError.hint}` + }`, ), }; } //multiple client can create the same id, send the wallet an unique key - const newId = `${req.id}_${nextMessageIndex}` - const resp = await w.handleCoreApiRequest(req.operation, newId, req.payload); + const newId = `${req.id}_${nextMessageIndex}`; + const resp = await w.handleCoreApiRequest( + req.operation, + newId, + req.payload, + ); //return to the client the original id - resp.id = req.id - return resp + resp.id = req.id; + return resp; } } @@ -310,7 +318,7 @@ async function reinitWallet(): Promise<void> { features: { allowHttp: settings.walletAllowHttp, }, - } + }, }); } catch (e) { logger.error("could not initialize wallet", e); @@ -321,10 +329,12 @@ async function reinitWallet(): Promise<void> { if (settings.showWalletActivity) { notifications.push({ notification: message, - when: AbsoluteTime.now() - }) + when: AbsoluteTime.now(), + }); } + processWalletNotification(message); + platform.sendMessageToAllChannels({ type: "wallet", notification: message, @@ -341,6 +351,7 @@ async function reinitWallet(): Promise<void> { (window as any).talerWallet = wallet; } currentWallet = wallet; + updateIconBasedOnBalance(); return walletInit.resolve(); } @@ -378,3 +389,46 @@ export async function wxMain(): Promise<void> { console.error(e); } } + +async function updateIconBasedOnBalance() { + const balance = await currentWallet?.client.call( + WalletApiOperation.GetBalances, + {}, + ); + if (balance) { + let showAlert = false; + for (const b of balance.balances) { + if (b.flags.length > 0) { + console.log("b.flags", JSON.stringify(b.flags)) + showAlert = true; + break; + } + } + + if (showAlert) { + platform.setAlertedIcon(); + } else { + platform.setNormalIcon(); + } + } +} + +/** + * All the actions triggered by notification that need to be + * run in the background. + * + * @param message + */ +async function processWalletNotification(message: WalletNotification) { + if ( + message.type === NotificationType.TransactionStateTransition && + (message.newTxState.minor === TransactionMinorState.KycRequired || + message.oldTxState.minor === TransactionMinorState.KycRequired || + message.newTxState.minor === TransactionMinorState.AmlRequired || + message.oldTxState.minor === TransactionMinorState.AmlRequired || + message.newTxState.minor === TransactionMinorState.BankConfirmTransfer || + message.oldTxState.minor === TransactionMinorState.BankConfirmTransfer) + ) { + await updateIconBasedOnBalance(); + } +} |