Improved client entry handling
parent
30357018c4
commit
7a97a74cd5
|
@ -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;
|
|
@ -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);
|
||||
import "tc-shared/main";
|
|
@ -42,6 +42,8 @@ export type Task = {
|
|||
function: (taskId?: number) => Promise<void>
|
||||
};
|
||||
|
||||
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];
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 = {};
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue