Adding W2G support for the native client

canary
WolverinDEV 2020-08-08 01:03:19 +02:00
parent ed94f6dcd2
commit 4b7cd15a24
19 changed files with 273 additions and 150 deletions

View File

@ -1,4 +1,8 @@
# Changelog:
* **08.08.20**
- Added a watch to gether mode
- Added API support for the popout able browsers for the native client
* **05.08.20**
- Putting the CSS files within the assets. No extra load needed any more
- Revoked the async file loading limit

View File

@ -129,6 +129,11 @@ export function finished() {
export function running() { return typeof(currentStage) !== "undefined"; }
export function register_task(stage: Stage, task: Task) {
if(!task.function) {
debugger;
throw "tried to register a loader task without a function";
}
if(currentStage > stage) {
if(config.error)
console.warn("Register loading task, but it had already been finished. Executing task anyways!");

View File

@ -1,6 +1,6 @@
import "./shared";
import * as loader from "../loader/loader";
import {ApplicationLoader, config, SourcePath} from "../loader/loader";
import {ApplicationLoader, SourcePath} from "../loader/loader";
import {script_name} from "../loader/utils";
import {loadManifest, loadManifestTarget} from "../maifest";
@ -10,8 +10,6 @@ declare global {
}
}
const node_require: typeof require = window.require;
function cache_tag() {
const ui = ui_version();
return "?_ts=" + (!!ui && ui !== "unknown" ? ui : Date.now());
@ -168,18 +166,18 @@ loader.register_task(loader.Stage.SETUP, {
export default class implements ApplicationLoader {
execute() {
/* TeaClient */
if(node_require) {
if(window.require) {
if(__build.target !== "client") {
loader.critical_error("App seems not to be compiled for the client.", "This app has been compiled for " + __build.target);
return;
}
window.native_client = true;
const path = node_require("path");
const remote = node_require('electron').remote;
const path = __non_webpack_require__("path");
const remote = __non_webpack_require__('electron').remote;
const render_entry = path.join(remote.app.getAppPath(), "/modules/", "renderer");
const render = node_require(render_entry);
const render = __non_webpack_require__(render_entry);
loader.register_task(loader.Stage.INITIALIZING, {
name: "teaclient initialize",

View File

@ -5,6 +5,8 @@ import {loadManifest, loadManifestTarget} from "../maifest";
import {getUrlParameter} from "../loader/utils";
export default class implements ApplicationLoader {
execute() {
loader.register_task(Stage.SETUP, {
function: async taskId => {
@ -27,24 +29,28 @@ export default class implements ApplicationLoader {
name: "page setup",
function: async () => {
const body = document.body;
/* top menu */
{
const container = document.createElement("div");
container.setAttribute('id', "top-menu-bar");
body.append(container);
}
/* template containers */
{
const container = document.createElement("div");
container.setAttribute('id', "templates");
body.append(container);
}
/* sounds container */
{
const container = document.createElement("div");
container.setAttribute('id', "sounds");
body.append(container);
}
/* mouse move container */
{
const container = document.createElement("div");
@ -52,6 +58,7 @@ export default class implements ApplicationLoader {
body.append(container);
}
/* tooltip container */
{
const container = document.createElement("div");
@ -78,6 +85,26 @@ export default class implements ApplicationLoader {
priority: 10
});
if(__build.target === "client") {
loader.register_task(Stage.SETUP, {
name: "native setup",
function: async () => {
const path = __non_webpack_require__("path");
const remote = __non_webpack_require__('electron').remote;
const render_entry = path.join(remote.app.getAppPath(), "/modules/", "renderer-manifest", "index");
const render = __non_webpack_require__(render_entry);
loader.register_task(loader.Stage.SETUP, {
name: "teaclient setup",
function: async () => await render.initialize(getUrlParameter("chunk")),
priority: 40
});
},
priority: 50
});
}
loader.execute_managed();
}
}

View File

@ -13,7 +13,7 @@ $setup-time: 80s / 24; /* 24 frames / sec; the initial sequence is 80 seconds */
user-select: none;
z-index: 10000;
z-index: 10000000;
display: flex;
flex-direction: column;

View File

@ -1,5 +1,5 @@
#overlay-no-js, #critical-load {
z-index: 10000;
z-index: 100000000;
display: none;
position: fixed;

View File

@ -21,13 +21,13 @@ export class SingletonEvent implements Event<SingletonEvents, "singletone-instan
as<T extends keyof SingletonEvents>() : SingletonEvents[T] { return; }
}
export interface EventReceiver<Events = { [key: string]: any }> {
export interface EventReceiver<Events extends { [key: string]: any } = { [key: string]: any }> {
fire<T extends keyof Events>(event_type: T, data?: Events[T], overrideTypeKey?: boolean);
fire_async<T extends keyof Events>(event_type: T, data?: Events[T], callback?: () => void);
}
const event_annotation_key = guid();
export class Registry<Events = { [key: string]: any }> implements EventReceiver<Events> {
export class Registry<Events extends { [key: string]: any } = { [key: string]: any }> implements EventReceiver<Events> {
private readonly registryUuid;
private handler: {[key: string]: ((event) => void)[]} = {};
@ -309,12 +309,6 @@ export function ReactEventHandler<ObjectClass = React.Component<any, any>, Event
}
}
export namespace sidebar {
export interface music {
}
}
export namespace modal {
export type BotStatusType = "name" | "description" | "volume" | "country_code" | "channel_commander" | "priority_speaker";
export type PlaylistStatusType = "replay_mode" | "finished" | "delete_played" | "max_size" | "notify_song_change";

View File

@ -2,7 +2,6 @@ import * as log from "tc-shared/log";
import {LogCategory} from "tc-shared/log";
import {guid} from "tc-shared/crypto/uid";
import {Settings, StaticSettings} from "tc-shared/settings";
import {createErrorModal} from "tc-shared/ui/elements/Modal";
import * as loader from "tc-loader";
import {formatMessage, formatMessageString} from "tc-shared/ui/frames/chat";
@ -309,7 +308,9 @@ export async function initialize() {
} catch (error) {
console.error(tr("Failed to initialize selected translation: %o"), error);
const show_error = () => {
createErrorModal(tr("Translation System"), tra("Failed to load current selected translation file.{:br:}File: {0}{:br:}Error: {1}{:br:}{:br:}Using default fallback translations.", cfg.current_translation_url, error)).open()
import("../ui/elements/Modal").then(Modal => {
Modal.createErrorModal(tr("Translation System"), tra("Failed to load current selected translation file.{:br:}File: {0}{:br:}Error: {1}{:br:}{:br:}Using default fallback translations.", cfg.current_translation_url, error)).open()
});
};
if(loader.running())
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {

View File

@ -152,6 +152,27 @@ export function error(category: LogCategory, message: string, ...optionalParams:
log(LogType.ERROR, category, message, ...optionalParams);
}
/* methods for direct import */
export function logTrace(category: LogCategory, message: string, ...optionalParams: any[]) {
log(LogType.TRACE, category, message, ...optionalParams);
}
export function logDebug(category: LogCategory, message: string, ...optionalParams: any[]) {
log(LogType.DEBUG, category, message, ...optionalParams);
}
export function logInfo(category: LogCategory, message: string, ...optionalParams: any[]) {
log(LogType.INFO, category, message, ...optionalParams);
}
export function logWarn(category: LogCategory, message: string, ...optionalParams: any[]) {
log(LogType.WARNING, category, message, ...optionalParams);
}
export function logError(category: LogCategory, message: string, ...optionalParams: any[]) {
log(LogType.ERROR, category, message, ...optionalParams);
}
export function group(level: LogType, category: LogCategory, name: string, ...optionalParams: any[]) : Group {
name = "[%s] " + name;
optionalParams.unshift(category_mapping.get(category));

View File

@ -284,12 +284,14 @@ export function format_time(time: number, default_value: string) {
return result.length > 0 ? result.substring(1) : default_value;
}
let _icon_size_style: JQuery<HTMLStyleElement>;
let _icon_size_style: HTMLStyleElement;
export function set_icon_size(size: string) {
if(!_icon_size_style)
_icon_size_style = $.spawn("style").appendTo($("#style"));
if(!_icon_size_style) {
_icon_size_style = document.createElement("style");
document.head.append(_icon_size_style);
}
_icon_size_style.text("\n" +
_icon_size_style.innerText = ("\n" +
".chat-emoji {\n" +
" height: " + size + "!important;\n" +
" width: " + size + "!important;\n" +

View File

@ -2,7 +2,6 @@ import * as log from "tc-shared/log";
import {LogCategory} from "tc-shared/log";
import * as ipc from "tc-shared/ipc/BrowserIPC";
import {ChannelMessage} from "tc-shared/ipc/BrowserIPC";
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
import {Registry} from "tc-shared/events";
import {
EventControllerBase,
@ -11,21 +10,17 @@ import {
} from "tc-shared/ui/react-elements/external-modal/IPCMessage";
import {ModalController, ModalEvents, ModalOptions, ModalState} from "tc-shared/ui/react-elements/Modal";
export class ExternalModalController extends EventControllerBase<"controller"> implements ModalController {
export abstract class AbstractExternalModalController extends EventControllerBase<"controller"> implements ModalController {
public readonly modalType: string;
public readonly userData: any;
private modalState: ModalState = ModalState.DESTROYED;
private readonly modalEvents: Registry<ModalEvents>;
private modalState: ModalState = ModalState.DESTROYED;
private currentWindow: Window;
private readonly documentUnloadListener: () => void;
private callbackWindowInitialized: (error?: string) => void;
private readonly documentQuitListener: () => void;
private windowClosedTestInterval: number = 0;
private windowClosedTimeout: number;
constructor(modal: string, localEventRegistry: Registry<any>, userData: any) {
protected constructor(modal: string, localEventRegistry: Registry, userData: any) {
super(localEventRegistry);
this.modalEvents = new Registry<ModalEvents>();
@ -36,7 +31,7 @@ export class ExternalModalController extends EventControllerBase<"controller"> i
this.ipcChannel = ipc.getInstance().createChannel();
this.ipcChannel.messageHandler = this.handleIPCMessage.bind(this);
this.documentQuitListener = () => this.currentWindow?.close();
this.documentUnloadListener = () => this.destroy();
}
getOptions(): Readonly<ModalOptions> {
@ -51,66 +46,21 @@ export class ExternalModalController extends EventControllerBase<"controller"> i
return this.modalState;
}
private trySpawnWindow() : Window | null {
const parameters = {
"loader-target": "manifest",
"chunk": "modal-external",
"modal-target": this.modalType,
"ipc-channel": this.ipcChannel.channelId,
"ipc-address": ipc.getInstance().getLocalAddress(),
"disableGlobalContextMenu": __build.mode === "debug" ? 1 : 0,
"loader-abort": __build.mode === "debug" ? 1 : 0,
};
const features = {
status: "no",
location: "no",
toolbar: "no",
menubar: "no",
/*
width: 600,
height: 400
*/
};
let baseUrl = location.origin + location.pathname + "?";
return window.open(
baseUrl + Object.keys(parameters).map(e => e + "=" + encodeURIComponent(parameters[e])).join("&"),
this.modalType,
Object.keys(features).map(e => e + "=" + features[e]).join(",")
);
}
protected abstract spawnWindow() : Promise<boolean>;
protected abstract focusWindow() : void;
protected abstract destroyWindow() : void;
async show() {
if(this.currentWindow) {
this.currentWindow.focus();
if(this.modalState === ModalState.SHOWN) {
this.focusWindow();
return;
}
this.modalState = ModalState.SHOWN;
this.currentWindow = this.trySpawnWindow();
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.trySpawnWindow();
if(this.currentWindow) {
reject(tr("Failed to spawn window"));
} else {
resolve();
}
}).close_listener.push(() => reject(tr("user aborted")));
})
}
if(!this.currentWindow) {
/* some shitty popup blocker or whatever */
if(!await this.spawnWindow()) {
this.modalState = ModalState.DESTROYED;
throw tr("failed to create window");
}
window.addEventListener("unload", this.documentQuitListener);
try {
await new Promise((resolve, reject) => {
@ -126,50 +76,25 @@ export class ExternalModalController extends EventControllerBase<"controller"> i
};
});
} catch (e) {
this.currentWindow?.close();
this.currentWindow = undefined;
this.modalState = ModalState.DESTROYED;
this.doDestroyWindow();
throw e;
}
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) {
window.removeEventListener("unload", this.documentQuitListener);
this.currentWindow = undefined;
this.destroy(); /* TODO: Test if we should do this */
}
}, 100);
};
this.modalState = ModalState.SHOWN;
window.addEventListener("unload", this.documentUnloadListener);
this.modalEvents.fire("open");
}
private destroyPopUp() {
if(this.currentWindow) {
clearInterval(this.windowClosedTestInterval);
this.windowClosedTestInterval = 0;
window.removeEventListener("beforeunload", this.documentQuitListener);
this.currentWindow.close();
this.currentWindow = undefined;
}
private doDestroyWindow() {
this.destroyWindow();
window.removeEventListener("beforeunload", this.documentUnloadListener);
}
async hide() {
if(this.modalState == ModalState.DESTROYED || this.modalState === ModalState.HIDDEN)
return;
this.destroyPopUp();
this.doDestroyWindow();
this.modalState = ModalState.HIDDEN;
this.modalEvents.fire("close");
}
@ -178,7 +103,7 @@ export class ExternalModalController extends EventControllerBase<"controller"> i
if(this.modalState === ModalState.DESTROYED)
return;
this.destroyPopUp();
this.doDestroyWindow();
if(this.ipcChannel)
ipc.getInstance().deleteChannel(this.ipcChannel);
@ -187,6 +112,10 @@ export class ExternalModalController extends EventControllerBase<"controller"> i
this.modalEvents.fire("destroy");
}
protected handleWindowClosed() {
this.destroy();
}
protected handleIPCMessage(remoteId: string, broadcast: boolean, message: ChannelMessage) {
if(broadcast)
return;
@ -195,14 +124,6 @@ export class ExternalModalController extends EventControllerBase<"controller"> i
log.debug(LogCategory.IPC, tr("Remote window connected with id %s"), remoteId);
this.ipcRemoteId = remoteId;
} else if(this.ipcRemoteId !== remoteId) {
if(this.windowClosedTestInterval > 0) {
clearInterval(this.windowClosedTestInterval);
this.windowClosedTestInterval = 0;
log.debug(LogCategory.IPC, tr("Remote window got reconnected. Client reloaded it."));
} else {
log.warn(LogCategory.IPC, tr("Remote window got a new id. Maybe a reload?"));
}
this.ipcRemoteId = remoteId;
}

View File

@ -33,18 +33,18 @@ export abstract class EventControllerBase<Type extends "controller" | "popout">
protected ipcChannel: IPCChannel;
protected ipcRemoteId: string;
protected readonly localEventRegistry: Registry<string>;
private readonly localEventReceiver: EventReceiver<string>;
protected readonly localEventRegistry: Registry;
private readonly localEventReceiver: EventReceiver;
private omitEventType: string = undefined;
private omitEventData: any;
private eventFiredListeners: {[key: string]:{ callback: () => void, timeout: number }} = {};
protected constructor(localEventRegistry: Registry<string>) {
protected constructor(localEventRegistry: Registry) {
this.localEventRegistry = localEventRegistry;
let refThis = this;
this.localEventReceiver = new class implements EventReceiver<{}> {
this.localEventReceiver = new class implements EventReceiver {
fire<T extends keyof {}>(eventType: T, data?: any[T], overrideTypeKey?: boolean) {
if(refThis.omitEventType === eventType && refThis.omitEventData === data) {
refThis.omitEventType = undefined;
@ -70,7 +70,7 @@ export abstract class EventControllerBase<Type extends "controller" | "popout">
}
}
};
this.localEventRegistry.connectAll(this.localEventReceiver as any);
this.localEventRegistry.connectAll(this.localEventReceiver);
}
protected handleIPCMessage(remoteId: string, broadcast: boolean, message: ChannelMessage) {
@ -112,7 +112,7 @@ export abstract class EventControllerBase<Type extends "controller" | "popout">
}
protected destroyIPC() {
this.localEventRegistry.disconnectAll(this.localEventReceiver as any);
this.localEventRegistry.disconnectAll(this.localEventReceiver);
this.ipcChannel = undefined;
this.ipcRemoteId = undefined;
this.eventFiredListeners = {};

View File

@ -1,6 +1,17 @@
import {Registry} from "tc-shared/events";
import {ExternalModalController} from "tc-shared/ui/react-elements/external-modal/Controller";
import {ModalController} from "tc-shared/ui/react-elements/Modal";
import "./Controller"; /* we've to reference him here, else the client would not */
export function spawnExternalModal<EventClass>(modal: string, events: Registry<EventClass>, userData: any) : ExternalModalController {
return new ExternalModalController(modal, events as any, userData);
export type ControllerFactory = (modal: string, events: Registry, userData: any) => ModalController;
let modalControllerFactory: ControllerFactory;
export function setExternalModalControllerFactory(factory: ControllerFactory) {
modalControllerFactory = factory;
}
export function spawnExternalModal<EventClass extends { [key: string]: any }>(modal: string, events: Registry<EventClass>, userData: any) : ModalController {
if(typeof modalControllerFactory === "undefined")
throw tr("No external modal factory has been set");
return modalControllerFactory(modal, events as any, userData);
}

View File

@ -1,5 +1,5 @@
import * as log from "tc-shared/log";
import {LogCategory} from "tc-shared/log";
import {LogCategory, logError} from "tc-shared/log";
import {spawnExternalModal} from "tc-shared/ui/react-elements/external-modal";
import {EventHandler, Registry} from "tc-shared/events";
import {VideoViewerEvents} from "./Definitions";
@ -66,7 +66,6 @@ class VideoViewer {
this.events.fire_async("notify_following", { watcherId: undefined });
this.events.fire_async("notify_video", { url: url });
this.modal.show();
}
async open() {
@ -313,6 +312,7 @@ let currentVideoViewer: VideoViewer;
export function openVideoViewer(connection: ConnectionHandler, url: string) {
if(currentVideoViewer?.connection === connection) {
currentVideoViewer.setWatchingVideo(url);
currentVideoViewer.open(); /* draw focus */
return;
} else if(currentVideoViewer) {
currentVideoViewer.destroy();
@ -323,9 +323,13 @@ export function openVideoViewer(connection: ConnectionHandler, url: string) {
currentVideoViewer.events.on("notify_destroy", () => {
currentVideoViewer = undefined;
});
currentVideoViewer.open();
currentVideoViewer.open().catch(error => {
logError(LogCategory.GENERAL, tr("Failed to open video viewer: %o"), error);
currentVideoViewer.destroy();
currentVideoViewer = undefined;
});
}
window.onunload = () => {
window.onbeforeunload = () => {
currentVideoViewer?.destroy();
};

View File

@ -1,19 +1,19 @@
interface PlayerStatusPlaying {
export interface PlayerStatusPlaying {
status: "playing";
timestampPlay: number;
timestampBuffer: number;
}
interface PlayerStatusBuffering {
export interface PlayerStatusBuffering {
status: "buffering";
}
interface PlayerStatusStopped {
export interface PlayerStatusStopped {
status: "stopped";
}
interface PlayerStatusPaused {
export interface PlayerStatusPaused {
status: "paused";
}

View File

@ -91,10 +91,10 @@ const WatcherInfo = React.memo((props: { events: Registry<VideoViewerEvents>, wa
let renderedAvatar;
if(clientInfo === "loading") {
renderedAvatar = <AvatarRenderer avatar={"loading"} key={"loading-avatar"} />;
renderedAvatar = <AvatarRenderer className={cssStyle.avatar} avatar={"loading"} key={"loading-avatar"} />;
} else {
const avatar = getGlobalAvatarManagerFactory().getManager(props.handlerId).resolveClientAvatar({ id: clientInfo.clientId, clientUniqueId: clientInfo.uniqueId });
renderedAvatar = <AvatarRenderer avatar={avatar} key={"client-avatar"} />;
renderedAvatar = <AvatarRenderer className={cssStyle.avatar} avatar={avatar} key={"client-avatar"} />;
}
let renderedClientName;
@ -154,9 +154,7 @@ const WatcherInfo = React.memo((props: { events: Registry<VideoViewerEvents>, wa
}}
>
<div className={cssStyle.containerAvatar}>
<div className={cssStyle.avatar}>
{renderedAvatar}
</div>
{renderedAvatar}
</div>
<div className={cssStyle.containerDetail}>
<a className={cssStyle.username}>

View File

@ -40,7 +40,7 @@ function eliminate_imports(node: ts.Node, ctx: ts.TransformationContext, data: I
case SyntaxKind.ImportDeclaration:
const import_decl = node as ts.ImportDeclaration;
const clause = import_decl.importClause;
if(!clause.namedBindings) return node;
if(!clause?.namedBindings) return node;
let new_binding;
if(clause.namedBindings.kind === SyntaxKind.NamedImports) {
@ -260,6 +260,9 @@ function analyze_type_node(node: ts.TypeNode | ts.LeftHandSideExpression, data:
analyze_type_node(ct.type, data);
break;
case SyntaxKind.TupleType:
break;
default:
throw "Unknown type " + SyntaxKind[node.kind] + ". Extend me :)";
}

View File

@ -0,0 +1,133 @@
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 * as loader from "tc-loader";
import {Stage} from "tc-loader";
import {setExternalModalControllerFactory} from "tc-shared/ui/react-elements/external-modal";
import {ChannelMessage} from "tc-shared/ipc/BrowserIPC";
import {LogCategory, logDebug, logWarn} from "tc-shared/log";
class ExternalModalController extends AbstractExternalModalController {
private currentWindow: Window;
private windowClosedTestInterval: number = 0;
private windowClosedTimeout: number;
constructor(a, b, c) {
super(a, b, c);
}
protected async spawnWindow() : Promise<boolean> {
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.getInstance().getLocalAddress(),
"disableGlobalContextMenu": __build.mode === "debug" ? 1 : 0,
"loader-abort": __build.mode === "debug" ? 1 : 0,
};
const features = {
status: "no",
location: "no",
toolbar: "no",
menubar: "no",
/*
width: 600,
height: 400
*/
};
let baseUrl = location.origin + location.pathname + "?";
return window.open(
baseUrl + Object.keys(parameters).map(e => e + "=" + encodeURIComponent(parameters[e])).join("&"),
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);
}
}
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
priority: 50,
name: "external modal controller factory setup",
function: async () => {
setExternalModalControllerFactory((modal, events, userData) => new ExternalModalController(modal, events, userData));
}
});

View File

@ -1,5 +1,6 @@
import "webrtc-adapter";
import "./index.scss";
import "./FileTransfer";
import "./ExternalModalFactory";
export = require("tc-shared/main");