diff --git a/client/css/static/main.scss b/client/css/static/main.scss index 8797911f..aa2e8051 100644 --- a/client/css/static/main.scss +++ b/client/css/static/main.scss @@ -4,10 +4,14 @@ html, body { } .app-container { + right: 0; + left: 0; + top: 0; + bottom: 0; + width: 100%; height: 100%; position: absolute; - width: 100%; display: flex; justify-content: center; diff --git a/client/declarations/exports.d.ts b/client/declarations/exports.d.ts index e0074fc1..6ac56514 100644 --- a/client/declarations/exports.d.ts +++ b/client/declarations/exports.d.ts @@ -1,25 +1,6 @@ -declare namespace native { - function client_version(): Promise; -} + +/* File: client/js/teaforo.ts */ declare namespace forum { - interface UserData { - session_id: string; - username: string; - application_data: string; - application_data_sign: string; - } -} -declare namespace audio.player { - interface Device { - device_id: string; - name: string; - } - function initialized(): boolean; - function context(): AudioContext; - function destination(): AudioNode; - function on_ready(cb: () => any): void; - function initialize(): boolean; - function available_devices(): Promise; - function set_device(device_id?: string): Promise; - function current_device(device_id?: string): Device; + export function register_callback(callback: () => any); + export function open(); } diff --git a/client/generate_packed.sh b/client/generate_packed.sh new file mode 100755 index 00000000..27dd7155 --- /dev/null +++ b/client/generate_packed.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +BASEDIR=$(dirname "$0") +cd "$BASEDIR" +source ../scripts/resolve_commands.sh + +if [[ ! -e declarations/imports_shared.d.ts ]]; then + echo "generate the declarations first!" + echo "Execute: /scripts/build_declarations.sh" + exit 1 +fi + +if [[ ! -e ../shared/generated/shared.js ]]; then + echo "generate the shared packed file first!" + echo "Execute: /shared/generate_packed.sh" + exit 1 +fi + +execute_tsc -p tsconfig/tsconfig_packed.json +if [[ $? -ne 0 ]]; then + echo "Failed to build file" + exit 1 +fi \ No newline at end of file diff --git a/client/js/teaforo.ts b/client/js/teaforo.ts index a6f608a8..e00b1d90 100644 --- a/client/js/teaforo.ts +++ b/client/js/teaforo.ts @@ -1,25 +1,25 @@ -import TeaForumIdentity = profiles.identities.TeaForumIdentity; +namespace forum { + const ipc = require("electron").ipcRenderer; + let callback_listener: (() => any)[] = []; -const ipc = require("electron").ipcRenderer; -let callback_listener: (() => any)[] = []; + ipc.on('teaforo-update', (event, data) => { + console.log("Got data update: %o", data); + profiles.identities.set_static_identity(data ? new profiles.identities.TeaForumIdentity(data.application_data, data.application_data_sign) : undefined); + try { + for(let listener of callback_listener) + setImmediate(listener); + } catch(e) { + console.log(e); + } -ipc.on('teaforo-update', (event, data: forum.UserData) => { - console.log("Got data update: %o", data); - profiles.identities.set_static_identity(data ? new TeaForumIdentity(data.application_data, data.application_data_sign) : undefined); - try { - for(let listener of callback_listener) - setImmediate(listener); - } catch(e) { - console.log(e); + callback_listener = []; + }); + + export function register_callback(callback: () => any) { + callback_listener.push(callback); } - callback_listener = []; -}); - -export function register_callback(callback: () => any) { - callback_listener.push(callback); -} - -export function open() { - ipc.send("teaforo-login"); + export function open() { + ipc.send("teaforo-login"); + } } \ No newline at end of file diff --git a/client/tsconfig/dtsconfig.json b/client/tsconfig/dtsconfig.json new file mode 100644 index 00000000..638d0b08 --- /dev/null +++ b/client/tsconfig/dtsconfig.json @@ -0,0 +1,6 @@ +{ + "source_files": [ + "../js/**/*.ts" + ], + "target_file": "../declarations/exports.d.ts" +} \ No newline at end of file diff --git a/client/tsconfig/tsconfig.json b/client/tsconfig/tsconfig.json new file mode 100644 index 00000000..6a450df4 --- /dev/null +++ b/client/tsconfig/tsconfig.json @@ -0,0 +1,12 @@ +/* general web project config */ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "sourceMap": true + }, + "include": [ + "../declarations/imports_*.d.ts", + "../js/**/*.ts" + ] +} \ No newline at end of file diff --git a/client/tsconfig/tsconfig_packed.json b/client/tsconfig/tsconfig_packed.json new file mode 100644 index 00000000..9f42a081 --- /dev/null +++ b/client/tsconfig/tsconfig_packed.json @@ -0,0 +1,14 @@ +/* packed web project config */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "none", + "outFile": "../generated/client.js", + "allowJs": true + }, + "include": [ + "../declarations/imports_*.d.ts", + "../js/**/*.ts", + "../../shared/generated/shared.js" + ] +} \ No newline at end of file diff --git a/files.php b/files.php index f5db3c73..76d2d2a2 100644 --- a/files.php +++ b/files.php @@ -1,7 +1,6 @@ "html", "search-pattern" => "/^([a-zA-Z]+)\.(html|php|json)$/", @@ -124,9 +123,10 @@ "path" => "i18n/", "local-path" => "./shared/i18n/" - ], + ] + ]; - /* vendors */ + $APP_FILE_LIST_SHARED_VENDORS = [ [ "type" => "js", "search-pattern" => "/.*\.js$/", @@ -142,10 +142,11 @@ "path" => "vendor/", "local-path" => "./vendor/" - ], + ] + ]; - /* client specific */ - [ + $APP_FILE_LIST_CLIENT_SOURCE = [ + [ /* client css files */ "client-only" => true, "type" => "css", "search-pattern" => "/.*\.css$/", @@ -154,17 +155,39 @@ "path" => "css/", "local-path" => "./client/css/" ], - [ + [ /* client js files */ "client-only" => true, "type" => "js", "search-pattern" => "/.*\.js/", - "build-target" => "dev|rel", + "build-target" => "dev", "path" => "js/", "local-path" => "./client/js/" ], - /* web specific */ + /* release specific */ + [ /* web merged javascript files (shared inclusive) */ + "client-only" => true, + "type" => "js", + "search-pattern" => "/.*\.js$/", + "build-target" => "rel", + + "path" => "js/", + "local-path" => "./client/generated/" + ], + [ /* Add the shared generated files. Exclude the shared file because we're including it already */ + "client-only" => true, + "type" => "js", + "search-pattern" => "/.*\.js$/", + "search-exclude" => "/shared\.js(.map)?$/", + "build-target" => "rel", + + "path" => "js/", + "local-path" => "./shared/generated/" + ], + ]; + + $APP_FILE_LIST_WEB_SOURCE = [ [ /* web javascript files (development mode only) */ "web-only" => true, "type" => "js", @@ -210,10 +233,10 @@ "path" => "./", "local-path" => "./web/html/" - ], - - + ] + ]; + $APP_FILE_LIST_WEB_TEASPEAK = [ /* special web.teaspeak.de only auth files */ [ /* login page and api */ "web-only" => true, @@ -255,9 +278,17 @@ "path" => "certs/", "local-path" => "./auth/certs/", "req-parm" => ["-xf"] - ], + ] ]; + $APP_FILE_LIST = array_merge( + $APP_FILE_LIST_SHARED_SOURCE, + $APP_FILE_LIST_SHARED_VENDORS, + $APP_FILE_LIST_CLIENT_SOURCE, + $APP_FILE_LIST_WEB_SOURCE, + $APP_FILE_LIST_WEB_TEASPEAK + ); + function systemify_path($path) { return str_replace("/", DIRECTORY_SEPARATOR, $path); } diff --git a/scripts/build_declarations.sh b/scripts/build_declarations.sh index ae1f35a1..6be7bf94 100755 --- a/scripts/build_declarations.sh +++ b/scripts/build_declarations.sh @@ -35,6 +35,11 @@ npm run dtsgen -- --config web/tsconfig/dtsconfig.json -v replace_tribble web/declarations/exports.d.ts echo "Generated web declarations" +#Client +npm run dtsgen -- --config client/tsconfig/dtsconfig.json -v +replace_tribble client/declarations/exports.d.ts +echo "Generated client declarations" + #Shared npm run dtsgen -- --config shared/tsconfig/dtsconfig.json -v replace_tribble shared/declarations/exports.d.ts @@ -54,4 +59,5 @@ generate_link web/declarations/exports.d.ts shared/declarations/imports_web.d.ts #Last but not least the client imports -generate_link shared/declarations/exports.d.ts web/declarations/imports_shared.d.ts \ No newline at end of file +generate_link shared/declarations/exports.d.ts web/declarations/imports_shared.d.ts +generate_link shared/declarations/exports.d.ts client/declarations/imports_shared.d.ts \ No newline at end of file diff --git a/scripts/client_build.sh b/scripts/client_build.sh new file mode 100755 index 00000000..f997be84 --- /dev/null +++ b/scripts/client_build.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +source `dirname $0`/resolve_commands.sh +BASEDIR=$(dirname "$0") +cd "$BASEDIR/../" + +source_path="client-api/environment/ui-files/raw" +if [[ "$1" == "development" ]] || [[ "$1" == "dev" ]] || [[ "$1" == "debug" ]]; then + type="development" +elif [[ "$1" == "release" ]] || [[ "$1" == "rel" ]]; then + type="release" +else + if [[ $# -lt 1 ]]; then + echo "Invalid argument count!" + else + echo "Invalid option $1" + fi + echo 'Available options are: "development" or "dev", "release" or "rel"' + exit 1 +fi + +echo "Generating style files" +npm run compile-sass +if [[ $? -ne 0 ]]; then + echo "Failed to generate style files" + exit 1 +fi + +echo "Generating web workers" +npm run build-worker-codec +if [[ $? -ne 0 ]]; then + echo "Failed to build web worker codec" + exit 1 +fi +npm run build-worker-pow +if [[ $? -ne 0 ]]; then + echo "Failed to build web worker pow" + exit 1 +fi + +#Lets build some tools +#dtsgen should be already build by build_declarations.sh +./tools/build_trgen.sh +if [[ $? -ne 0 ]]; then + echo "Failed to build typescript translation generator" + exit 1 +fi + +#Now lets build the declarations +echo "Building declarations" +./scripts/build_declarations.sh +if [[ $? -ne 0 ]]; then + echo "Failed to generate declarations" + exit 1 +fi + +if [[ "$type" == "release" ]]; then #Compile everything for release mode + #Compile the shared source first + echo "Building shared source" + ./shared/generate_packed.sh + if [[ $? -ne 0 ]]; then + echo "Failed to build shared source" + exit 1 + fi + + #Now compile the web client itself + echo "Building web client" + ./client/generate_packed.sh + if [[ $? -ne 0 ]]; then + echo "Failed to build web client" + exit 1 + fi +elif [[ "$type" == "development" ]]; then + echo "Building shared source" + execute_ttsc -p ./shared/tsconfig/tsconfig.json + if [[ $? -ne 0 ]]; then + echo "Failed to compile shared sources" + exit 1 + fi + + echo "Building client client source" + execute_ttsc -p ./client/tsconfig/tsconfig.json + if [[ $? -ne 0 ]]; then + echo "Failed to compile web sources" + exit 1 + fi +fi + +echo "Generating environment" +php files.php generate client ${type} +if [[ $? -ne 0 ]]; then + echo "Failed to generate environment" + exit 1 +fi + +echo "Successfully build!" \ No newline at end of file diff --git a/scripts/deploy_ui_files.sh b/scripts/deploy_ui_files.sh index 9b022e18..f052e935 100755 --- a/scripts/deploy_ui_files.sh +++ b/scripts/deploy_ui_files.sh @@ -13,7 +13,7 @@ if [[ "$#" -ne 3 ]]; then exit 1 fi -if [[ ! -d client/environment/ui-files/ ]]; then +if [[ ! -d client-api/environment/ui-files/ ]]; then echo "Missing UI Files" exit 1 fi diff --git a/shared/css/static/general.scss b/shared/css/static/general.scss index a7d704e9..e5ce36f2 100644 --- a/shared/css/static/general.scss +++ b/shared/css/static/general.scss @@ -331,7 +331,6 @@ $animation_length: .5s; .app-container { right: 0; left: 0; - bottom: 25px; top: 0; transition: all $animation_length linear; diff --git a/shared/js/connection/HandshakeHandler.ts b/shared/js/connection/HandshakeHandler.ts index d64cbb04..c2b88a77 100644 --- a/shared/js/connection/HandshakeHandler.ts +++ b/shared/js/connection/HandshakeHandler.ts @@ -1,3 +1,4 @@ +declare const native: any; //FIXME: Read client declarations! namespace connection { export interface HandshakeIdentityHandler { connection: AbstractServerConnection; diff --git a/shared/js/load.ts b/shared/js/load.ts index d89690da..a29263fa 100644 --- a/shared/js/load.ts +++ b/shared/js/load.ts @@ -407,26 +407,41 @@ function displayCriticalError(message: string) { /* 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); + if(window.require) { + const request = new Request("js/proto.js"); + let file_path = request.url; + if(!file_path.startsWith("file://")) + throw "Invalid file path (" + file_path + ")"; + file_path = file_path.substring(7); - await new Promise((resolve, reject) => { - request.onreadystatechange = () => { - if (request.readyState === 4){ - if (request.status === 404) { - app.type = app.Type.WEB_RELEASE; - } else { - app.type = app.Type.WEB_DEBUG; + const fs = require('fs'); + if(fs.existsSync(file_path)) { + app.type = app.Type.CLIENT_DEBUG; + } else { + app.type = app.Type.CLIENT_RELEASE; + } + } else { + /* 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){ + if (request.status === 404) { + app.type = app.Type.WEB_RELEASE; + } else { + app.type = app.Type.WEB_DEBUG; + } + resolve(); } - resolve(); - } - }; - request.onerror = () => { - reject("Failed to detect app type"); - }; - request.send(); - }); + }; + request.onerror = () => { + reject("Failed to detect app type"); + }; + request.send(); + }); + } }, load_scripts: async () => { /* @@ -468,7 +483,7 @@ const loader_javascript = { loader.register_task(loader.Stage.JAVASCRIPT, { name: "scripts release", priority: 20, - function: loader_javascript.loadRelease + function: loader_javascript.load_release }); } else { loader.register_task(loader.Stage.JAVASCRIPT, { @@ -596,7 +611,7 @@ const loader_javascript = { ]); }, - loadRelease: async () => { + load_release: async () => { console.log("Load for release!"); await loader.load_scripts([ @@ -809,24 +824,6 @@ loader.register_task(loader.Stage.INITIALIZING, { priority: 50 }); -window["Module"] = window["Module"] || {}; -/* TeaClient */ -if(window.require) { - const path = require("path"); - const remote = require('electron').remote; - module.paths.push(path.join(remote.app.getAppPath(), "/modules")); - module.paths.push(path.join(path.dirname(remote.getGlobal("browser-root")), "js")); - - const connector = require("renderer"); - console.log(connector); - - loader.register_task(loader.Stage.INITIALIZING, { - name: "teaclient initialize", - function: connector.initialize, - priority: 40 - }); -} - loader.register_task(loader.Stage.INITIALIZING, { name: "Browser detection", function: async () => { @@ -855,9 +852,12 @@ loader.register_task(loader.Stage.INITIALIZING, { name: "secure tester", function: async () => { /* we need https or localhost to use some things like the storage API */ - if(location.protocol !== 'https:' && location.hostname !== 'localhost') { + if(typeof isSecureContext === "undefined") + (window)["isSecureContext"] = location.protocol !== 'https:' && location.hostname !== 'localhost'; + + if(!isSecureContext) { display_critical_load("TeaWeb cant run on unsecured sides.", "App requires to be loaded via HTTPS!"); - throw "App requires to be loaded via HTTPS!" + throw "App requires a secure context!" } }, priority: 20 @@ -918,80 +918,99 @@ loader.register_task(loader.Stage.LOADED, { priority: 20 }); -const hello_world = () => { - const print_security = () => { + +window["Module"] = window["Module"] || {}; +/* TeaClient */ +if(window.require) { + const path = require("path"); + const remote = require('electron').remote; + module.paths.push(path.join(remote.app.getAppPath(), "/modules")); + module.paths.push(path.join(path.dirname(remote.getGlobal("browser-root")), "js")); + + const connector = require("renderer"); + console.log(connector); + + loader.register_task(loader.Stage.INITIALIZING, { + name: "teaclient initialize", + function: connector.initialize, + priority: 40 + }); +} else { + const hello_world = () => { + const print_security = () => { + { + const css = [ + "display: block", + "text-align: center", + "font-size: 42px", + "font-weight: bold", + "-webkit-text-stroke: 2px black", + "color: red" + ].join(";"); + console.log("%c ", "font-size: 100px;"); + console.log("%cSecurity warning:", css); + } + { + const css = [ + "display: block", + "text-align: center", + "font-size: 18px", + "font-weight: bold" + ].join(";"); + + console.log("%cPasting anything in here could give attackers access to your data.", css); + console.log("%cUnless you understand exactly what you are doing, close this window and stay safe.", css); + console.log("%c ", "font-size: 100px;"); + } + }; + + /* print the hello world */ { const css = [ "display: block", "text-align: center", - "font-size: 42px", + "font-size: 72px", "font-weight: bold", "-webkit-text-stroke: 2px black", - "color: red" + "color: #18BC9C" ].join(";"); - console.log("%c ", "font-size: 100px;"); - console.log("%cSecurity warning:", css); + console.log("%cHey, hold on!", css); } { const css = [ "display: block", "text-align: center", - "font-size: 18px", + "font-size: 26px", "font-weight: bold" ].join(";"); - console.log("%cPasting anything in here could give attackers access to your data.", css); - console.log("%cUnless you understand exactly what you are doing, close this window and stay safe.", css); - console.log("%c ", "font-size: 100px;"); + const css_2 = [ + "display: block", + "text-align: center", + "font-size: 26px", + "font-weight: bold", + "color: blue" + ].join(";"); + + const display_detect = /./; + display_detect.toString = function() { print_security(); return ""; } + + console.log("%cLovely to see you using and debugging the TeaSpeak Web client.", css); + console.log("%cIf you have some good ideas or already done some incredible changes,", css); + console.log("%cyou'll be may interested to share them here: %chttps://github.com/TeaSpeak/TeaWeb", css, css_2); + console.log("%c ", display_detect); } }; - /* print the hello world */ - { - const css = [ - "display: block", - "text-align: center", - "font-size: 72px", - "font-weight: bold", - "-webkit-text-stroke: 2px black", - "color: #18BC9C" - ].join(";"); - console.log("%cHey, hold on!", css); + try { /* lets try to print it as VM code :)*/ + let hello_world_code = hello_world.toString(); + hello_world_code = hello_world_code.substr(hello_world_code.indexOf('() => {') + 8); + hello_world_code = hello_world_code.substring(0, hello_world_code.lastIndexOf("}")); + hello_world_code = hello_world_code.replace(/(?!"\S*) {2,}(?!\S*")/g, " ").replace(/[\n\r]/g, ""); + eval(hello_world_code); + } catch(e) { + hello_world(); } - { - const css = [ - "display: block", - "text-align: center", - "font-size: 26px", - "font-weight: bold" - ].join(";"); - - const css_2 = [ - "display: block", - "text-align: center", - "font-size: 26px", - "font-weight: bold", - "color: blue" - ].join(";"); - - const display_detect = /./; - display_detect.toString = function() { print_security(); return ""; } - - console.log("%cLovely to see you using and debugging the TeaSpeak Web client.", css); - console.log("%cIf you have some good ideas or already done some incredible changes,", css); - console.log("%cyou'll be may interested to share them here: %chttps://github.com/TeaSpeak/TeaWeb", css, css_2); - console.log("%c ", display_detect); - } -}; - -try { /* lets try to print it as VM code :)*/ - let hello_world_code = hello_world.toString(); - hello_world_code = hello_world_code.substr(hello_world_code.indexOf('() => {') + 8); - hello_world_code = hello_world_code.substring(0, hello_world_code.lastIndexOf("}")); - hello_world_code = hello_world_code.replace(/(?!"\S*) {2,}(?!\S*")/g, " ").replace(/[\n\r]/g, ""); - eval(hello_world_code); -} catch(e) { - hello_world(); } loader.execute().then(() => { diff --git a/shared/js/main.ts b/shared/js/main.ts index 82d93ae0..ce851f7d 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -23,6 +23,11 @@ function getUserMediaFunction() { return (navigator as any).getUserMedia || (navigator as any).webkitGetUserMedia || (navigator as any).mozGetUserMedia; } +interface Window { + open_connected_question: () => Promise; +} + + function setup_close() { window.onbeforeunload = event => { if(profiles.requires_save()) @@ -34,23 +39,18 @@ function setup_close() { if(!native_client) { event.returnValue = "Are you really sure?
You're still connected!"; } else { - event.preventDefault(); - event.returnValue = "question"; + if(window.open_connected_question) { + event.preventDefault(); + event.returnValue = "question"; + window.open_connected_question().then(result => { + if(result) { + window.onbeforeunload = undefined; - const {remote} = require('electron'); - const dialog = remote.dialog; - - dialog.showMessageBox(remote.getCurrentWindow(), { - type: 'question', - buttons: ['Yes', 'No'], - title: 'Confirm', - message: 'Are you really sure?\nYou\'re still connected!' - }, choice => { - if(choice === 0) { - window.onbeforeunload = undefined; - remote.getCurrentWindow().close(); - } - }); + const {remote} = require('electron'); + remote.getCurrentWindow().close(); + } + }); + } else { /* we're in debugging mode */ } } } }; diff --git a/shared/js/ui/modal/ModalSettings.ts b/shared/js/ui/modal/ModalSettings.ts index c04d8179..0d0f8711 100644 --- a/shared/js/ui/modal/ModalSettings.ts +++ b/shared/js/ui/modal/ModalSettings.ts @@ -1,3 +1,4 @@ +/// /// /// /// @@ -936,7 +937,6 @@ namespace Modals { if (native_client) { teaforo_tag.find(".native-teaforo-login").on('click', event => { setTimeout(() => { - const forum = require("teaforo.js"); const call = () => { if (modal.shown) { display_settings(selected_profile); diff --git a/test/js/load.js b/test/js/load.js index 3b4e10db..e6170996 100644 --- a/test/js/load.js +++ b/test/js/load.js @@ -511,7 +511,7 @@ const loader_javascript = { ["js/WebPPTListener.js"] ]); }), - loadRelease: () => __awaiter(this, void 0, void 0, function* () { + load_release: () => __awaiter(this, void 0, void 0, function* () { console.log("Load for release!"); yield loader.load_scripts([ //Load general API's diff --git a/vendor/jqueryjquery.min.js b/vendor/jqueryjquery.min.js deleted file mode 120000 index 36dde48f..00000000 --- a/vendor/jqueryjquery.min.js +++ /dev/null @@ -1 +0,0 @@ -C:/Users/WolverinDEV/TeaSpeak/TeaWeb/vendor/jquery/jquery.min.js \ No newline at end of file diff --git a/web/css/static/main.scss b/web/css/static/main.scss index ccb39c19..5f87694c 100644 --- a/web/css/static/main.scss +++ b/web/css/static/main.scss @@ -29,3 +29,10 @@ html, body { display: flex; flex-direction: column; resize: both; } } + +$small_device: 650px; +@media only screen and (max-width: $small_device) { + .app-container { + bottom: 25px; + } +} \ No newline at end of file