/*
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 = {
isFirefox: () => false,
getSettingsFromStorage: () => Promise.resolve(defaultSettings),
keepAlive: (cb: VoidFunction) => cb(),
findTalerUriInActiveTab: async () => undefined,
findTalerUriInClipboard: async () => undefined,
listenNetworkConnectionState,
getPermissionsApi: () => ({
addPermissionsListener: () => undefined,
containsHostPermissions: async () => true,
removeHostPermissions: async () => false,
requestHostPermissions: async () => false,
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);
};
}