From 7a97a74cd556bf97d3ec569059a6e40ed9df6901 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Wed, 30 Sep 2020 22:51:04 +0200 Subject: [PATCH] Improved client entry handling --- client/app/external.d.ts | 8 ++ client/app/index.ts | 7 +- loader/app/loader/loader.ts | 34 ++++--- loader/app/targets/app.ts | 50 +--------- loader/app/targets/certaccept.ts | 97 ------------------- loader/app/targets/maifest-target.ts | 22 ----- loader/app/targets/shared.ts | 29 +++++- .../external-modal/PopoutEntrypoint.ts | 4 + web/app/ui/context-menu/ReactRenderer.scss | 5 +- webpack-client.config.ts | 4 +- 10 files changed, 70 insertions(+), 190 deletions(-) create mode 100644 client/app/external.d.ts delete mode 100644 loader/app/targets/certaccept.ts diff --git a/client/app/external.d.ts b/client/app/external.d.ts new file mode 100644 index 00000000..539aeb10 --- /dev/null +++ b/client/app/external.d.ts @@ -0,0 +1,8 @@ +interface Window { + __native_client_init_hook: () => void; + __native_client_init_shared: (webpackRequire: any) => void; +} + +declare const __webpack_require__; +declare const __teaclient_preview_notice: any; +declare const __teaclient_preview_error: any; \ No newline at end of file diff --git a/client/app/index.ts b/client/app/index.ts index ffc8c0c6..091fd57e 100644 --- a/client/app/index.ts +++ b/client/app/index.ts @@ -1,7 +1,4 @@ -declare const __webpack_require__; -window["shared-require"] = __webpack_require__; +window.__native_client_init_shared(__webpack_require__); import "./index.scss"; - -/* firstly assign the shared-require */ -setTimeout(() => require("tc-shared/main"), 0); \ No newline at end of file +import "tc-shared/main"; \ No newline at end of file diff --git a/loader/app/loader/loader.ts b/loader/app/loader/loader.ts index bfc6362c..66e8a898 100644 --- a/loader/app/loader/loader.ts +++ b/loader/app/loader/loader.ts @@ -42,6 +42,8 @@ export type Task = { function: (taskId?: number) => Promise }; +type InternalTask = Task & { initiator: string } + export enum Stage { /* loading loader required files (incl this) @@ -88,7 +90,7 @@ export enum Stage { let cache_tag: string | undefined; let currentStage: Stage = undefined; -const tasks: {[key:number]:Task[]} = {}; +const tasks: {[key:number]: InternalTask[]} = {}; /* test if all files shall be load from cache or fetch again */ function loader_cache_tag() { @@ -129,18 +131,23 @@ export function finished() { export function running() { return typeof(currentStage) !== "undefined"; } export function register_task(stage: Stage, task: Task) { + let callee = new Error().stack.split("\n")[2].replace(/^\s*at (Object\.\.\/)?/, ""); + if(callee.match(/^.* \(([:\\/_\-+0-9a-zA-Z.]+):([0-9]+):([0-9]+)\)$/)) { + callee = callee.replace(/^.* \(([:\\/_\-+0-9a-zA-Z.]+):([0-9]+):([0-9]+)\)$/, "$1:$2:$3"); + } if(!task.function) { debugger; - throw "tried to register a loader task without a function"; + throw "tried to register a loader task without a function at " + callee; } if(currentStage > stage) { - if(config.error) - console.warn("Register loading task, but it had already been finished. Executing task anyways!"); + if(config.error) { + console.warn("Register loading task, but it had already been finished. Executing task anyways! Registerer: %s", callee); + } const promise = task.function(); if(!promise) { - console.error("Loading task %s hasn't returned a promise!", task.name); + console.error("Loading task %s (%s) hasn't returned a promise!", task.name, callee); return; } promise.catch(error => { @@ -155,7 +162,9 @@ export function register_task(stage: Stage, task: Task) { } const task_array = tasks[stage] || []; - task_array.push(task); + task_array.push(Object.assign({ + initiator: callee + }, task)); tasks[stage] = task_array.sort((a, b) => a.priority - b.priority); } @@ -190,7 +199,7 @@ export async function execute() { let end: number = Date.now(); while(currentStage <= Stage.LOADED || typeof(currentStage) === "undefined") { - let pendingTasks: Task[] = []; + let pendingTasks: InternalTask[] = []; while((tasks[currentStage] || []).length > 0) { if(pendingTasks.length == 0 || pendingTasks[0].priority == tasks[currentStage][0].priority) { pendingTasks.push(tasks[currentStage].pop()); @@ -199,7 +208,7 @@ export async function execute() { const errors: { error: any, - task: Task + task: InternalTask }[] = []; for(const task of pendingTasks) { @@ -210,8 +219,9 @@ export async function execute() { } as RunningTask; try { - if(config.verbose) + if(config.verbose) { console.debug("Executing loader %s (%d)", task.name, task.priority); + } runningTasks.push(rTask); const promise = task.function(rTask.taskId); @@ -256,9 +266,11 @@ export async function execute() { if(errors.length > 0) { if(config.loader_groups) console.groupEnd(); + console.error("Failed to execute loader. The following tasks failed (%d):", errors.length); - for(const error of errors) - console.error(" - %s: %o", error.task.name, error.error); + for(const error of errors) { + console.error(" - %s (%s): %o", error.task.name, error.task.initiator, error.error); + } throw "failed to process step " + Stage[currentStage]; } diff --git a/loader/app/targets/app.ts b/loader/app/targets/app.ts index deb76aba..c8e4454f 100644 --- a/loader/app/targets/app.ts +++ b/loader/app/targets/app.ts @@ -10,23 +10,8 @@ declare global { } } -function cache_tag() { - const ui = ui_version(); - return "?_ts=" + (!!ui && ui !== "unknown" ? ui : Date.now()); -} - -let _ui_version; -export function ui_version() { - if(typeof(_ui_version) !== "string") { - const version_node = document.getElementById("app_version"); - if(!version_node) return undefined; - - const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; - if(!version) return undefined; - - return (_ui_version = version); - } - return _ui_version; +function getCacheTag() { + return "?_ts=" + (__build.mode === "debug" ? Date.now() : __build.timestamp); } const LoaderTaskCallback = taskId => (script: SourcePath, state) => { @@ -101,7 +86,7 @@ loader.register_task(loader.Stage.TEMPLATES, { "templates/modal/musicmanage.html", "templates/modal/newcomer.html", ], { - cache_tag: cache_tag(), + cache_tag: getCacheTag(), max_parallel_requests: -1 }, LoaderTaskCallback(taskId)); }, @@ -154,7 +139,6 @@ loader.register_task(loader.Stage.SETUP, { loader.register_task(loader.Stage.SETUP, { name: "TeaClient tester", function: async () => { - //@ts-ignore if(typeof __teaclient_preview_notice !== "undefined" && typeof __teaclient_preview_error !== "undefined") { loader.critical_error("Why you're opening TeaWeb within the TeaSpeak client?!"); throw "we're already a TeaClient!"; @@ -165,34 +149,6 @@ loader.register_task(loader.Stage.SETUP, { export default class implements ApplicationLoader { execute() { - /* TeaClient */ - 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 = __non_webpack_require__("path"); - const remote = __non_webpack_require__('electron').remote; - - const render_entry = path.join(remote.app.getAppPath(), "/modules/", "renderer"); - const render = __non_webpack_require__(render_entry); - - loader.register_task(loader.Stage.INITIALIZING, { - name: "teaclient initialize", - function: render.initialize, - priority: 40 - }); - } else { - if(__build.target !== "web") { - loader.critical_error("App seems not to be compiled for the web.", "This app has been compiled for " + __build.target); - return; - } - - window.native_client = false; - } - loader.execute_managed(); } } \ No newline at end of file diff --git a/loader/app/targets/certaccept.ts b/loader/app/targets/certaccept.ts deleted file mode 100644 index b64d93e6..00000000 --- a/loader/app/targets/certaccept.ts +++ /dev/null @@ -1,97 +0,0 @@ -import * as loader from "../loader/loader"; - -let is_debug = false; - -/* all javascript loaders */ -const loader_javascript = { - detect_type: async () => { - /* test if js/proto.js is available. If so we're in debug mode */ - const request = new XMLHttpRequest(); - request.open('GET', 'js/proto.js', true); - - await new Promise((resolve, reject) => { - request.onreadystatechange = () => { - if (request.readyState === 4){ - is_debug = request.status !== 404; - resolve(); - } - }; - request.onerror = () => { - reject("Failed to detect app type"); - }; - request.send(); - }); - }, - - load_scripts: async () => { - await loader.scripts.load_multiple(["vendor/jquery/jquery.min.js"], {}); - await loader.scripts.load_multiple([ - ["dist/certificate-popup.js"], - ], {}); - } -}; - -const loader_style = { - load_style: async () => { - if(is_debug) { - await loader_style.load_style_debug(); - } else { - await loader_style.load_style_release(); - } - }, - - load_style_debug: async () => { - await loader.style.load_multiple([ - "css/static/main.css", - ], {}); - }, - - load_style_release: async () => { - await loader.style.load_multiple([ - "css/static/main.css", - ], {}); - } -}; - -loader.register_task(loader.Stage.INITIALIZING, { - name: "app type test", - function: loader_javascript.detect_type, - priority: 20 -}); - -loader.register_task(loader.Stage.JAVASCRIPT, { - name: "javascript", - function: loader_javascript.load_scripts, - priority: 10 -}); - -loader.register_task(loader.Stage.STYLE, { - name: "style", - function: loader_style.load_style, - priority: 10 -}); - -/* register tasks */ -loader.register_task(loader.Stage.INITIALIZING, { - name: "safari fix", - function: async () => { - /* safari remove "fix" */ - if(Element.prototype.remove === undefined) - Object.defineProperty(Element.prototype, "remove", { - enumerable: false, - configurable: false, - writable: false, - value: function(){ - this.parentElement.removeChild(this); - } - }); - }, - priority: 50 -}); - -if(!loader.running()) { - /* we know that we want to load the app */ - loader.execute_managed(); -} - -export = {}; \ No newline at end of file diff --git a/loader/app/targets/maifest-target.ts b/loader/app/targets/maifest-target.ts index 70432e5a..c3703e13 100644 --- a/loader/app/targets/maifest-target.ts +++ b/loader/app/targets/maifest-target.ts @@ -5,8 +5,6 @@ import {loadManifest, loadManifestTarget} from "../maifest"; import {getUrlParameter} from "../loader/utils"; export default class implements ApplicationLoader { - - execute() { loader.register_task(Stage.SETUP, { function: async taskId => { @@ -67,26 +65,6 @@ 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(); } } \ No newline at end of file diff --git a/loader/app/targets/shared.ts b/loader/app/targets/shared.ts index 0fe91c55..19bced8e 100644 --- a/loader/app/targets/shared.ts +++ b/loader/app/targets/shared.ts @@ -1,9 +1,6 @@ import * as loader from "../loader/loader"; import {Stage} from "../loader/loader"; -import { - BrowserInfo, - detect as detectBrowser, -} from "detect-browser"; +import {BrowserInfo, detect as detectBrowser,} from "detect-browser"; declare global { interface Window { @@ -12,6 +9,30 @@ declare global { } } +loader.register_task(Stage.SETUP, { + name: "app init", + function: async () => { + /* TeaClient */ + if(window.require || window.__native_client_init_hook) { + 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_init_hook(); + window.native_client = true; + } else { + if(__build.target !== "web") { + loader.critical_error("App seems not to be compiled for the web.", "This app has been compiled for " + __build.target); + return; + } + + window.native_client = false; + } + }, + priority: 1000 +}); + if(__build.target === "web") { loader.register_task(Stage.SETUP, { name: "outdated browser checker", diff --git a/shared/js/ui/react-elements/external-modal/PopoutEntrypoint.ts b/shared/js/ui/react-elements/external-modal/PopoutEntrypoint.ts index 2cc55111..b24650f7 100644 --- a/shared/js/ui/react-elements/external-modal/PopoutEntrypoint.ts +++ b/shared/js/ui/react-elements/external-modal/PopoutEntrypoint.ts @@ -15,6 +15,10 @@ import {setupJSRender} from "../../../ui/jsrender"; import "../../../file/RemoteAvatars"; import "../../../file/RemoteIcons"; +if("__native_client_init_shared" in window) { + window.__native_client_init_shared(__webpack_require__); +} + let modalRenderer: ModalRenderer; let modalInstance: AbstractModal; let modalClass: new (events: RegistryMap, userData: any) => AbstractModal; diff --git a/web/app/ui/context-menu/ReactRenderer.scss b/web/app/ui/context-menu/ReactRenderer.scss index 96c7fae2..7434647f 100644 --- a/web/app/ui/context-menu/ReactRenderer.scss +++ b/web/app/ui/context-menu/ReactRenderer.scss @@ -1,5 +1,3 @@ -@import "../../../css/static/mixin"; - .globalContainer { display: flex; flex-direction: column; @@ -140,7 +138,8 @@ cursor: pointer; font-size: 22px; - @include user-select(none); + user-select: none; + -webkit-user-select: none; /* Hide the browser's default checkbox */ input { diff --git a/webpack-client.config.ts b/webpack-client.config.ts index 17949cdd..0661607e 100644 --- a/webpack-client.config.ts +++ b/webpack-client.config.ts @@ -16,8 +16,10 @@ export = () => config_base.config("client").then(config => { throw "invalid config"; config.externals.push((context, request, callback) => { - if (request.startsWith("tc-backend/")) + if (request.startsWith("tc-backend/")) { return callback(null, `window["backend-loader"].require("${request}")`); + } + callback(undefined, undefined); });