/* This file is part of GNU Taler (C) 2022 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ import { Logger, TalerUri } from "@gnu-taler/taler-util"; import { WalletOperations } from "@gnu-taler/taler-wallet-core"; import { BackgroundOperations } from "../wxApi.js"; import { BackgroundPlatformAPI, ForegroundPlatformAPI, MessageFromBackend, MessageFromFrontend, MessageResponse, defaultSettings, } from "./api.js"; const logger = new Logger("dev.ts"); const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { runningOnPrivateMode: () => false, isFirefox: () => false, getSettingsFromStorage: () => Promise.resolve(defaultSettings), keepAlive: (cb: VoidFunction) => cb(), findTalerUriInActiveTab: async () => undefined, findTalerUriInClipboard: async () => undefined, listenNetworkConnectionState, openNewURLFromPopup: () => undefined, triggerWalletEvent: () => undefined, setAlertedIcon: () => undefined, setNormalIcon : () => undefined, getPermissionsApi: () => ({ containsClipboardPermissions: async () => true, removeClipboardPermissions: async () => false, requestClipboardPermissions: async () => false, }), getWalletWebExVersion: () => ({ version: "none", }), notifyWhenAppIsReady: () => { const knownFrames = ["popup", "wallet"]; let total = knownFrames.length; return new Promise((fn) => { function waitAndNotify(): void { total--; logger.trace(`waitAndNotify ${total}`); if (total < 1) { fn(); } } knownFrames.forEach((f) => { const theFrame = window.frames[f as any]; if (theFrame.location.href === "about:blank") { waitAndNotify(); } else { theFrame.addEventListener("load", waitAndNotify); } }); }); }, openWalletPage: (page: string) => { // @ts-ignore window.parent.redirectWallet(`wallet.html#${page}`); }, openWalletPageFromPopup: (page: string) => { // @ts-ignore window.parent.redirectWallet(`wallet.html#${page}`); // close the popup // @ts-ignore window.parent.closePopup(); }, openWalletURIFromPopup: (page: TalerUri) => { alert("openWalletURIFromPopup not implemented yet"); }, redirectTabToWalletPage: (tabId: number, page: string) => { alert("redirectTabToWalletPage not implemented yet"); }, registerAllIncomingConnections: () => undefined, registerOnInstalled: () => Promise.resolve(), registerReloadOnNewVersion: () => undefined, useServiceWorkerAsBackgroundProcess: () => false, listenToAllChannels: ( notifyNewMessage: (message: any) => Promise, ) => { window.addEventListener( "message", (event: MessageEvent) => { if (event.data.type !== "command") return; const sender = event.data.header.replyMe; notifyNewMessage(event.data.body as any).then((resp) => { logger.trace(`listenToAllChannels: from ${sender}`, event); if (event.source) { const msg: IframeMessageResponse = { type: "response", header: { responseId: sender }, body: resp, }; window.parent.postMessage(msg); } }); }, ); }, sendMessageToAllChannels: (message: MessageFromBackend) => { Array.from(window.frames).forEach((w) => { try { w.postMessage({ header: {}, body: message, }); } catch (e) { console.error(e); } }); }, listenToWalletBackground: (onNewMessage: (m: MessageFromBackend) => void) => { function listener(event: MessageEvent): void { logger.trace(`listenToWalletBackground: `, event); if (event.data.type !== "notification") return; onNewMessage(event.data.body); } window.parent.addEventListener("message", listener); return () => { window.parent.removeEventListener("message", listener); }; }, sendMessageToBackground: async < Op extends WalletOperations | BackgroundOperations, >( payload: MessageFromFrontend, ): Promise => { const replyMe = `reply-${Math.floor(Math.random() * 100000)}`; const message: IframeMessageCommand = { type: "command", header: { replyMe }, body: payload, }; logger.trace(`sendMessageToBackground: `, message); return new Promise((res, rej) => { function listener(event: MessageEvent): void { if ( event.data.type !== "response" || event.data.header.responseId !== replyMe ) { return; } res(event.data.body); window.parent.removeEventListener("message", listener); } window.parent.addEventListener("message", listener, {}); window.parent.postMessage(message); }); }, }; type IframeMessageType = | IframeMessageNotification | IframeMessageResponse | IframeMessageCommand; interface IframeMessageNotification { type: "notification"; header: Record; body: MessageFromBackend; } interface IframeMessageResponse { type: "response"; header: { responseId: string; }; body: MessageResponse; } interface IframeMessageCommand { type: "command"; header: { replyMe: string; }; body: MessageFromFrontend; } export default api; function listenNetworkConnectionState( notify: (state: "on" | "off") => void, ): () => void { function notifyOffline() { notify("off"); } function notifyOnline() { notify("on"); } window.addEventListener("offline", notifyOffline); window.addEventListener("online", notifyOnline); return () => { window.removeEventListener("offline", notifyOffline); window.removeEventListener("online", notifyOnline); }; }