import {AbstractExternalModalController} from "tc-shared/ui/react-elements/external-modal/Controller"; import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; import * as ipc from "tc-shared/ipc/BrowserIPC"; import {ChannelMessage} from "tc-shared/ipc/BrowserIPC"; import {LogCategory, logDebug, logWarn} from "tc-shared/log"; import {Popout2ControllerMessages, PopoutIPCMessage} from "tc-shared/ui/react-elements/external-modal/IPCMessage"; import {tr, tra} from "tc-shared/i18n/localize"; import {ModalOptions} from "tc-shared/ui/react-elements/modal/Definitions"; export class ExternalModalController extends AbstractExternalModalController { private readonly options: ModalOptions; private currentWindow: Window; private windowClosedTestInterval: number = 0; private windowClosedTimeout: number; constructor(modalType: string, constructorArguments: any[] | undefined, options: ModalOptions | undefined) { super(modalType, constructorArguments); this.options = options || {}; } protected async spawnWindow() : Promise { if(this.currentWindow) { return true; } this.currentWindow = this.trySpawnWindow0(); if(!this.currentWindow) { await new Promise((resolve, reject) => { spawnYesNo(tr("Would you like to open the popup?"), tra("Would you like to open popup {}?", this.modalType), callback => { if(!callback) { reject("user aborted"); return; } this.currentWindow = this.trySpawnWindow0(); if(window) { reject(tr("Failed to spawn window")); } else { resolve(); } }).close_listener.push(() => reject(tr("user aborted"))); }); } if(!this.currentWindow) { return false; } this.currentWindow.onbeforeunload = () => { clearInterval(this.windowClosedTestInterval); this.windowClosedTimeout = Date.now() + 5000; this.windowClosedTestInterval = setInterval(() => { if(!this.currentWindow) { clearInterval(this.windowClosedTestInterval); this.windowClosedTestInterval = 0; return; } if(this.currentWindow.closed || Date.now() > this.windowClosedTimeout) { clearInterval(this.windowClosedTestInterval); this.windowClosedTestInterval = 0; this.handleWindowClosed(); } }, 100); }; return true; } protected destroyWindow() { clearInterval(this.windowClosedTestInterval); this.windowClosedTestInterval = 0; if(this.currentWindow) { this.currentWindow.close(); this.currentWindow = undefined; } } protected focusWindow() { this.currentWindow?.focus(); } private trySpawnWindow0() : Window | null { const parameters = { "loader-target": "manifest", "chunk": "modal-external", "modal-target": this.modalType, "ipc-channel": this.ipcChannel.channelId, "ipc-address": ipc.getIpcInstance().getLocalAddress(), "disableGlobalContextMenu": __build.mode === "debug" ? 1 : 0, "loader-abort": __build.mode === "debug" ? 1 : 0, }; const options = this.getOptions(); const features = { status: "no", location: "no", toolbar: "no", menubar: "no", resizable: "yes", width: options.defaultSize?.width, height: options.defaultSize?.height }; let baseUrl = location.origin + location.pathname + "?"; return window.open( baseUrl + Object.keys(parameters).map(e => e + "=" + encodeURIComponent(parameters[e])).join("&"), this.options?.uniqueId || this.modalType, Object.keys(features).map(e => e + "=" + features[e]).join(",") ); } protected handleIPCMessage(remoteId: string, broadcast: boolean, message: ChannelMessage) { if(!broadcast && this.ipcRemoteId !== remoteId) { if(this.windowClosedTestInterval > 0) { clearInterval(this.windowClosedTestInterval); this.windowClosedTestInterval = 0; logDebug(LogCategory.IPC, tr("Remote window got reconnected. Client reloaded it.")); } else { logWarn(LogCategory.IPC, tr("Remote window got a new id. Maybe a reload?")); } } super.handleIPCMessage(remoteId, broadcast, message); } protected handleTypedIPCMessage(type: T, payload: PopoutIPCMessage[T]) { super.handleTypedIPCMessage(type, payload); switch (type) { case "invoke-modal-action": const data = payload as PopoutIPCMessage["invoke-modal-action"]; switch (data.action) { case "close": this.destroy(); break; case "minimize": window.focus(); break; } break; } } }