updates...
parent
597375676e
commit
5b8b4d07c3
|
@ -2,32 +2,32 @@ import "core-js/stable";
|
|||
import "./polifill";
|
||||
import "./css";
|
||||
|
||||
import {ApplicationLoader} from "./loader/loader";
|
||||
import {getUrlParameter} from "./loader/Utils";
|
||||
import { ApplicationLoader } from "./loader/loader";
|
||||
import { getUrlParameter } from "./loader/Utils";
|
||||
|
||||
/* let the loader register himself at the window first */
|
||||
const target = getUrlParameter("loader-target") || "app";
|
||||
console.info("Loading app with loader \"%s\"", target);
|
||||
|
||||
let appLoader: ApplicationLoader;
|
||||
if(target === "empty") {
|
||||
if (target === "empty") {
|
||||
appLoader = new (require("./targets/empty").default);
|
||||
} else if(target === "manifest") {
|
||||
} else if (target === "manifest") {
|
||||
appLoader = new (require("./targets/maifest-target").default);
|
||||
} else {
|
||||
appLoader = new (require("./targets/app").default);
|
||||
}
|
||||
setTimeout(() => appLoader.execute(), 0);
|
||||
|
||||
export {};
|
||||
export { };
|
||||
|
||||
if(__build.target === "client") {
|
||||
if (__build.target === "client") {
|
||||
/* do this so we don't get a react dev tools warning within the client */
|
||||
if(!('__REACT_DEVTOOLS_GLOBAL_HOOK__' in window)) {
|
||||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {};
|
||||
if (!('__REACT_DEVTOOLS_GLOBAL_HOOK__' in window)) {
|
||||
(window as Window).__REACT_DEVTOOLS_GLOBAL_HOOK__ = {};
|
||||
}
|
||||
|
||||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject = function () {};
|
||||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject = function () { };
|
||||
}
|
||||
|
||||
/* Hello World message */
|
||||
|
@ -89,7 +89,7 @@ if(__build.target === "client") {
|
|||
].join(";");
|
||||
|
||||
const display_detect = /./;
|
||||
display_detect.toString = function() { print_security(); return ""; };
|
||||
display_detect.toString = function () { print_security(); return ""; };
|
||||
|
||||
clog("%cLovely to see you using and debugging the TeaSpeak-Web client.", css);
|
||||
clog("%cIf you have some good ideas or already done some incredible changes,", css);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
127
package.json
127
package.json
|
@ -17,74 +17,75 @@
|
|||
"author": "TeaSpeak (WolverinDEV)",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.4",
|
||||
"@babel/plugin-transform-runtime": "^7.10.4",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@babel/core": "^7.23.3",
|
||||
"@babel/plugin-transform-runtime": "^7.23.3",
|
||||
"@babel/preset-env": "^7.23.3",
|
||||
"@google-cloud/translate": "^5.3.0",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@types/dompurify": "^2.0.1",
|
||||
"@types/ejs": "^3.0.2",
|
||||
"@types/emoji-mart": "^3.0.2",
|
||||
"@types/emscripten": "^1.38.0",
|
||||
"@types/fs-extra": "^8.0.1",
|
||||
"@types/dompurify": "^2.4.0",
|
||||
"@types/ejs": "^3.1.5",
|
||||
"@types/emoji-mart": "^3.0.12",
|
||||
"@types/emscripten": "^1.39.10",
|
||||
"@types/fs-extra": "^8.1.5",
|
||||
"@types/html-minifier": "^3.5.3",
|
||||
"@types/jquery": "^3.3.34",
|
||||
"@types/jquery": "^3.5.27",
|
||||
"@types/jsrender": "^1.0.5",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/lodash": "^4.14.201",
|
||||
"@types/moment": "^2.13.0",
|
||||
"@types/node": "^12.7.2",
|
||||
"@types/react-color": "^3.0.4",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
"@types/react-grid-layout": "^1.1.1",
|
||||
"@types/remarkable": "^1.7.4",
|
||||
"@types/sdp-transform": "^2.4.4",
|
||||
"@types/sha256": "^0.2.0",
|
||||
"@types/twemoji": "^12.1.1",
|
||||
"@types/node": "^12.20.55",
|
||||
"@types/react": "^16.14.51",
|
||||
"@types/react-color": "^3.0.10",
|
||||
"@types/react-dom": "^16.9.22",
|
||||
"@types/react-grid-layout": "^1.3.5",
|
||||
"@types/remarkable": "^1.7.6",
|
||||
"@types/sdp-transform": "^2.4.9",
|
||||
"@types/sha256": "^0.2.2",
|
||||
"@types/twemoji": "^12.1.2",
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/xml-parser": "^1.2.29",
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.3.1",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"babel-loader": "^8.1.0",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"clean-css": "^4.2.1",
|
||||
"@types/xml-parser": "^1.2.33",
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.7.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"babel-loader": "^8.3.0",
|
||||
"circular-dependency-plugin": "^5.2.2",
|
||||
"clean-css": "^4.2.4",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"copy-webpack-plugin": "^8.0.0",
|
||||
"copy-webpack-plugin": "^8.1.1",
|
||||
"css-loader": "^3.6.0",
|
||||
"css-minimizer-webpack-plugin": "^1.3.0",
|
||||
"exports-loader": "^0.7.0",
|
||||
"fast-xml-parser": "^3.17.4",
|
||||
"file-loader": "^6.0.0",
|
||||
"fast-xml-parser": "^3.21.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "latest",
|
||||
"gulp": "^4.0.2",
|
||||
"html-loader": "^1.0.0",
|
||||
"html-loader": "^1.3.2",
|
||||
"html-minifier": "^4.0.0",
|
||||
"html-webpack-inline-source-plugin": "0.0.10",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"inline-chunks-html-webpack-plugin": "^1.3.1",
|
||||
"mime-types": "^2.1.24",
|
||||
"mini-css-extract-plugin": "^1.3.9",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mime-types": "^2.1.35",
|
||||
"mini-css-extract-plugin": "^1.6.2",
|
||||
"mkdirp": "^0.5.6",
|
||||
"node-sass": "^4.14.1",
|
||||
"postcss": "^8.3.0",
|
||||
"postcss-loader": "^5.2.0",
|
||||
"potpack": "^1.0.1",
|
||||
"raw-loader": "^4.0.0",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-loader": "^5.3.0",
|
||||
"potpack": "^1.0.2",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass": "1.22.10",
|
||||
"sass-loader": "^8.0.2",
|
||||
"sha256": "^0.2.0",
|
||||
"style-loader": "^1.1.3",
|
||||
"style-loader": "^1.3.0",
|
||||
"svg-inline-loader": "^0.8.2",
|
||||
"terser": "^4.2.1",
|
||||
"terser": "^4.8.1",
|
||||
"terser-webpack-plugin": "4.2.3",
|
||||
"ts-loader": "^6.2.2",
|
||||
"ts-loader": "^8.4.0",
|
||||
"tsd": "^0.13.1",
|
||||
"typescript": "^4.2",
|
||||
"typescript": "^4.9.5",
|
||||
"url-loader": "^4.1.1",
|
||||
"wabt": "^1.0.13",
|
||||
"webpack": "^5.26.1",
|
||||
"webpack-bundle-analyzer": "^3.6.1",
|
||||
"webpack-cli": "^4.5.0",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack": "^5.89.0",
|
||||
"webpack-bundle-analyzer": "^3.9.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "^3.11.3",
|
||||
"webpack-svg-sprite-generator": "^5.0.4",
|
||||
"zip-webpack-plugin": "^4.0.1"
|
||||
},
|
||||
|
@ -97,33 +98,33 @@
|
|||
},
|
||||
"homepage": "https://www.teaspeak.de",
|
||||
"dependencies": {
|
||||
"@types/crypto-js": "^4.0.1",
|
||||
"@types/crypto-js": "^4.2.1",
|
||||
"broadcastchannel-polyfill": "^1.0.1",
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"crypto-js": "^4.0.0",
|
||||
"detect-browser": "^5.2.0",
|
||||
"dompurify": "^2.2.8",
|
||||
"emoji-mart": "git+https://github.com/WolverinDEV/emoji-mart.git",
|
||||
"emoji-regex": "^9.0.0",
|
||||
"highlight.js": "^10.1.1",
|
||||
"ip-regex": "^4.2.0",
|
||||
"jquery": "^3.5.1",
|
||||
"jsrender": "^1.0.7",
|
||||
"moment": "^2.24.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-grid-layout": "^1.2.2",
|
||||
"react-player": "^2.5.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"detect-browser": "^5.3.0",
|
||||
"dompurify": "^2.4.7",
|
||||
"emoji-mart": "^3.0.1",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"highlight.js": "^10.7.3",
|
||||
"ip-regex": "^4.3.0",
|
||||
"jquery": "^3.7.1",
|
||||
"jsrender": "^1.0.13",
|
||||
"moment": "^2.29.4",
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-grid-layout": "^1.4.3",
|
||||
"react-player": "^2.13.0",
|
||||
"remarkable": "^2.0.1",
|
||||
"resize-observer-polyfill": "git+https://github.com/albancreton/resize-observer-polyfill.git#patch-1",
|
||||
"sdp-transform": "^2.14.0",
|
||||
"sdp-transform": "^2.14.1",
|
||||
"simple-jsonp-promise": "^1.1.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"twemoji": "^13.0.0",
|
||||
"twemoji": "^13.1.1",
|
||||
"url-knife": "^3.1.3",
|
||||
"webcrypto-liner": "^1.2.4",
|
||||
"webpack-manifest-plugin": "^3.1.0",
|
||||
"webcrypto-liner": "^1.4.2",
|
||||
"webpack-manifest-plugin": "^3.2.0",
|
||||
"webrtc-adapter": "^7.5.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,15 +40,15 @@ export interface KeyBoardBackend {
|
|||
registerListener(listener: (event: KeyEvent) => void);
|
||||
unregisterListener(listener: (event: KeyEvent) => void);
|
||||
|
||||
registerHook(hook: KeyHook) : () => void;
|
||||
registerHook(hook: KeyHook): () => void;
|
||||
unregisterHook(hook: KeyHook);
|
||||
|
||||
isKeyPressed(key: string | SpecialKey) : boolean;
|
||||
isKeyPressed(key: string | SpecialKey): boolean;
|
||||
}
|
||||
|
||||
export class AbstractKeyBoard implements KeyBoardBackend {
|
||||
protected readonly registeredListener: ((event: KeyEvent) => void)[];
|
||||
protected readonly activeSpecialKeys: { [key: number] : boolean };
|
||||
protected readonly activeSpecialKeys: { [key: number]: boolean };
|
||||
protected readonly activeKeys;
|
||||
|
||||
protected registeredKeyHooks: RegisteredKeyHook[] = [];
|
||||
|
@ -59,10 +59,10 @@ export class AbstractKeyBoard implements KeyBoardBackend {
|
|||
this.registeredListener = [];
|
||||
}
|
||||
|
||||
protected destroy() {}
|
||||
protected destroy() { }
|
||||
|
||||
isKeyPressed(key: string | SpecialKey): boolean {
|
||||
if(typeof(key) === 'string') {
|
||||
if (typeof (key) === 'string') {
|
||||
return typeof this.activeKeys[key] !== "undefined";
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ export class AbstractKeyBoard implements KeyBoardBackend {
|
|||
};
|
||||
|
||||
this.registeredKeyHooks.push(registeredHook);
|
||||
if(this.shouldHookBeActive(registeredHook)) {
|
||||
if (this.shouldHookBeActive(registeredHook)) {
|
||||
registeredHook.triggered = true;
|
||||
registeredHook.callbackPress();
|
||||
}
|
||||
|
@ -85,11 +85,11 @@ export class AbstractKeyBoard implements KeyBoardBackend {
|
|||
}
|
||||
|
||||
unregisterHook(hook: KeyHook) {
|
||||
if(!("triggered" in hook)) {
|
||||
if (!("triggered" in hook)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.registeredKeyHooks.remove(hook);
|
||||
this.registeredKeyHooks.remove(hook as RegisteredKeyHook);
|
||||
}
|
||||
|
||||
registerListener(listener: (event: KeyEvent) => void) {
|
||||
|
@ -101,19 +101,19 @@ export class AbstractKeyBoard implements KeyBoardBackend {
|
|||
}
|
||||
|
||||
private shouldHookBeActive(hook: KeyHook) {
|
||||
if(typeof hook.keyAlt !== "undefined" && hook.keyAlt != this.activeSpecialKeys[SpecialKey.ALT]) {
|
||||
if (typeof hook.keyAlt !== "undefined" && hook.keyAlt != this.activeSpecialKeys[SpecialKey.ALT]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(typeof hook.keyCtrl !== "undefined" && hook.keyCtrl != this.activeSpecialKeys[SpecialKey.CTRL]) {
|
||||
if (typeof hook.keyCtrl !== "undefined" && hook.keyCtrl != this.activeSpecialKeys[SpecialKey.CTRL]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(typeof hook.keyShift !== "undefined" && hook.keyShift != this.activeSpecialKeys[SpecialKey.SHIFT]) {
|
||||
if (typeof hook.keyShift !== "undefined" && hook.keyShift != this.activeSpecialKeys[SpecialKey.SHIFT]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(typeof hook.keyWindows !== "undefined" && hook.keyWindows != this.activeSpecialKeys[SpecialKey.WINDOWS]) {
|
||||
if (typeof hook.keyWindows !== "undefined" && hook.keyWindows != this.activeSpecialKeys[SpecialKey.WINDOWS]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -122,11 +122,11 @@ export class AbstractKeyBoard implements KeyBoardBackend {
|
|||
|
||||
protected fireKeyEvent(event: KeyEvent) {
|
||||
//console.debug("Trigger key event %o", key_event);
|
||||
for(const listener of this.registeredListener) {
|
||||
for (const listener of this.registeredListener) {
|
||||
listener(event);
|
||||
}
|
||||
|
||||
if(event.type == EventType.KEY_TYPED) {
|
||||
if (event.type == EventType.KEY_TYPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -134,26 +134,26 @@ export class AbstractKeyBoard implements KeyBoardBackend {
|
|||
this.activeSpecialKeys[SpecialKey.CTRL] = event.keyCtrl;
|
||||
this.activeSpecialKeys[SpecialKey.SHIFT] = event.keyShift;
|
||||
this.activeSpecialKeys[SpecialKey.WINDOWS] = event.keyWindows;
|
||||
if(event.type == EventType.KEY_PRESS) {
|
||||
if (event.type == EventType.KEY_PRESS) {
|
||||
this.activeKeys[event.keyCode] = event;
|
||||
} else {
|
||||
delete this.activeKeys[event.keyCode];
|
||||
}
|
||||
|
||||
|
||||
for(const hook of this.registeredKeyHooks) {
|
||||
for (const hook of this.registeredKeyHooks) {
|
||||
const hookActive = this.shouldHookBeActive(hook);
|
||||
if(hookActive === hook.triggered) {
|
||||
if (hookActive === hook.triggered) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hook.triggered = hookActive;
|
||||
if(hookActive) {
|
||||
if(hook.callbackPress) {
|
||||
if (hookActive) {
|
||||
if (hook.callbackPress) {
|
||||
hook.callbackPress();
|
||||
}
|
||||
} else {
|
||||
if(hook.callbackRelease) {
|
||||
if (hook.callbackRelease) {
|
||||
hook.callbackRelease();
|
||||
}
|
||||
}
|
||||
|
@ -166,13 +166,13 @@ export class AbstractKeyBoard implements KeyBoardBackend {
|
|||
this.activeSpecialKeys[SpecialKey.SHIFT] = false;
|
||||
this.activeSpecialKeys[SpecialKey.WINDOWS] = false;
|
||||
|
||||
for(const code of Object.keys(this.activeKeys)) {
|
||||
for (const code of Object.keys(this.activeKeys)) {
|
||||
delete this.activeKeys[code];
|
||||
}
|
||||
|
||||
for(const hook of this.registeredKeyHooks) {
|
||||
if(hook.triggered) {
|
||||
if(hook.callbackRelease) {
|
||||
for (const hook of this.registeredKeyHooks) {
|
||||
if (hook.triggered) {
|
||||
if (hook.callbackRelease) {
|
||||
hook.callbackRelease();
|
||||
}
|
||||
|
||||
|
@ -183,7 +183,7 @@ export class AbstractKeyBoard implements KeyBoardBackend {
|
|||
}
|
||||
|
||||
let keyBoardBackend: KeyBoardBackend;
|
||||
export function getKeyBoard() : KeyBoardBackend {
|
||||
export function getKeyBoard(): KeyBoardBackend {
|
||||
return keyBoardBackend;
|
||||
}
|
||||
|
||||
|
@ -193,29 +193,29 @@ export function setKeyBoardBackend(newBackend: KeyBoardBackend) {
|
|||
|
||||
export function getKeyDescription(key: KeyDescriptor) {
|
||||
let result = "";
|
||||
if(key.keyShift) {
|
||||
if (key.keyShift) {
|
||||
result += " + " + tr("Shift");
|
||||
}
|
||||
|
||||
if(key.keyAlt) {
|
||||
if (key.keyAlt) {
|
||||
result += " + " + tr("Alt");
|
||||
}
|
||||
|
||||
if(key.keyCtrl) {
|
||||
if (key.keyCtrl) {
|
||||
result += " + " + tr("CTRL");
|
||||
}
|
||||
|
||||
if(key.keyWindows) {
|
||||
if (key.keyWindows) {
|
||||
result += " + " + tr("Win");
|
||||
}
|
||||
|
||||
if(key.keyCode) {
|
||||
if (key.keyCode) {
|
||||
let keyName;
|
||||
if(key.keyCode.startsWith("Key")) {
|
||||
if (key.keyCode.startsWith("Key")) {
|
||||
keyName = key.keyCode.substr(3);
|
||||
} else if(key.keyCode.startsWith("Digit")) {
|
||||
} else if (key.keyCode.startsWith("Digit")) {
|
||||
keyName = key.keyCode.substr(5);
|
||||
} else if(key.keyCode.startsWith("Numpad")) {
|
||||
} else if (key.keyCode.startsWith("Numpad")) {
|
||||
keyName = "Numpad " + key.keyCode.substr(6);
|
||||
} else {
|
||||
keyName = key.keyCode;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
import {Registry} from "tc-shared/events";
|
||||
import {LogCategory, logTrace, logWarn} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {getAudioBackend} from "tc-shared/audio/Player";
|
||||
import { Registry } from "tc-shared/events";
|
||||
import { LogCategory, logTrace, logWarn } from "tc-shared/log";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
import { getAudioBackend } from "tc-shared/audio/Player";
|
||||
|
||||
export interface TrackClientInfo {
|
||||
media?: number,
|
||||
|
@ -56,34 +56,34 @@ export class RemoteRTPTrack {
|
|||
this.events.destroy();
|
||||
}
|
||||
|
||||
getEvents() : Registry<RemoteRTPTrackEvents> {
|
||||
getEvents(): Registry<RemoteRTPTrackEvents> {
|
||||
return this.events;
|
||||
}
|
||||
|
||||
getState() : RemoteRTPTrackState {
|
||||
getState(): RemoteRTPTrackState {
|
||||
return this.currentState;
|
||||
}
|
||||
|
||||
getSsrc() : number {
|
||||
getSsrc(): number {
|
||||
return this.ssrc >>> 0;
|
||||
}
|
||||
|
||||
getTrack() : MediaStreamTrack {
|
||||
getTrack(): MediaStreamTrack {
|
||||
return this.transceiver.receiver.track;
|
||||
}
|
||||
|
||||
getTransceiver() : RTCRtpTransceiver {
|
||||
getTransceiver(): RTCRtpTransceiver {
|
||||
return this.transceiver;
|
||||
}
|
||||
|
||||
getCurrentAssignment() : TrackClientInfo | undefined {
|
||||
getCurrentAssignment(): TrackClientInfo | undefined {
|
||||
return this.currentAssignment;
|
||||
}
|
||||
|
||||
protected setState(state: RemoteRTPTrackState) {
|
||||
if(this.currentState === state) {
|
||||
if (this.currentState === state) {
|
||||
return;
|
||||
} else if(this.currentState === RemoteRTPTrackState.Destroyed) {
|
||||
} else if (this.currentState === RemoteRTPTrackState.Destroyed) {
|
||||
logWarn(LogCategory.WEBRTC, tr("Tried to change the track state for track %d from destroyed to %s."), this.getSsrc(), RemoteRTPTrackState[state]);
|
||||
return;
|
||||
}
|
||||
|
@ -107,10 +107,9 @@ export class RemoteRTPVideoTrack extends RemoteRTPTrack {
|
|||
track.onended = () => logTrace(LogCategory.VIDEO, "Track %d ended", ssrc);
|
||||
track.onmute = () => logTrace(LogCategory.VIDEO, "Track %d muted", ssrc);
|
||||
track.onunmute = () => logTrace(LogCategory.VIDEO, "Track %d unmuted", ssrc);
|
||||
track.onisolationchange = () => logTrace(LogCategory.VIDEO, "Track %d isolation changed", ssrc);
|
||||
}
|
||||
|
||||
getMediaStream() : MediaStream {
|
||||
getMediaStream(): MediaStream {
|
||||
return this.mediaStream;
|
||||
}
|
||||
|
||||
|
@ -170,7 +169,7 @@ export class RemoteRTPAudioTrack extends RemoteRTPTrack {
|
|||
*/
|
||||
|
||||
getAudioBackend().executeWhenInitialized(() => {
|
||||
if(!this.mediaStream) {
|
||||
if (!this.mediaStream) {
|
||||
/* we've already been destroyed */
|
||||
return;
|
||||
}
|
||||
|
@ -200,7 +199,7 @@ export class RemoteRTPAudioTrack extends RemoteRTPTrack {
|
|||
this.setState(RemoteRTPTrackState.Destroyed);
|
||||
}
|
||||
|
||||
getGain() : GainNode | undefined {
|
||||
getGain(): GainNode | undefined {
|
||||
return this.gainNode;
|
||||
}
|
||||
|
||||
|
@ -213,13 +212,13 @@ export class RemoteRTPAudioTrack extends RemoteRTPTrack {
|
|||
* Mutes this track until the next setGain(..) call or a new sequence begins (state update)
|
||||
*/
|
||||
abortCurrentReplay() {
|
||||
if(this.gainNode) {
|
||||
if (this.gainNode) {
|
||||
this.gainNode.gain.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected updateGainNode() {
|
||||
if(!this.gainNode) {
|
||||
if (!this.gainNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/* Note: This will be included into the controller and renderer process */
|
||||
import {LogCategory, logError, logWarn} from "tc-shared/log";
|
||||
import { LogCategory, logError, logWarn } from "tc-shared/log";
|
||||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import { Stage } from "tc-loader";
|
||||
import * as crypto from "crypto-js";
|
||||
import {tra} from "tc-shared/i18n/localize";
|
||||
import { tra } from "tc-shared/i18n/localize";
|
||||
|
||||
export type LocalAvatarInfo = {
|
||||
fileName: string,
|
||||
|
@ -43,12 +43,12 @@ export type OwnAvatarMode = "uploading" | "server";
|
|||
export class OwnAvatarStorage {
|
||||
private openedCache: Cache | undefined;
|
||||
|
||||
private static generateRequestUrl(serverUniqueId: string, mode: OwnAvatarMode) : string {
|
||||
private static generateRequestUrl(serverUniqueId: string, mode: OwnAvatarMode): string {
|
||||
return "https://_local_avatar/" + serverUniqueId + "/" + mode;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
if(!("caches" in window)) {
|
||||
if (!("caches" in window)) {
|
||||
/* Not available (may unsecure context?) */
|
||||
this.openedCache = undefined;
|
||||
return;
|
||||
|
@ -62,8 +62,8 @@ export class OwnAvatarStorage {
|
|||
}
|
||||
}
|
||||
|
||||
private async loadAvatarRequest(serverUniqueId: string, mode: OwnAvatarMode) : Promise<LocalAvatarLoadResult<Response>> {
|
||||
if(!this.openedCache) {
|
||||
private async loadAvatarRequest(serverUniqueId: string, mode: OwnAvatarMode): Promise<LocalAvatarLoadResult<Response>> {
|
||||
if (!this.openedCache) {
|
||||
return { status: "cache-unavailable" };
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ export class OwnAvatarStorage {
|
|||
ignoreSearch: true,
|
||||
});
|
||||
|
||||
if(!response) {
|
||||
if (!response) {
|
||||
return { status: "empty-result" };
|
||||
}
|
||||
|
||||
|
@ -84,9 +84,9 @@ export class OwnAvatarStorage {
|
|||
}
|
||||
}
|
||||
|
||||
async loadAvatarImage(serverUniqueId: string, mode: OwnAvatarMode) : Promise<LocalAvatarLoadResult<ArrayBuffer>> {
|
||||
async loadAvatarImage(serverUniqueId: string, mode: OwnAvatarMode): Promise<LocalAvatarLoadResult<ArrayBuffer>> {
|
||||
const loadResult = await this.loadAvatarRequest(serverUniqueId, mode);
|
||||
if(loadResult.status !== "success") {
|
||||
if (loadResult.status !== "success") {
|
||||
return loadResult;
|
||||
}
|
||||
|
||||
|
@ -98,9 +98,9 @@ export class OwnAvatarStorage {
|
|||
}
|
||||
}
|
||||
|
||||
async loadAvatar(serverUniqueId: string, mode: OwnAvatarMode, createResourceUrl: boolean) : Promise<LocalAvatarLoadResult<LocalAvatarInfo>> {
|
||||
async loadAvatar(serverUniqueId: string, mode: OwnAvatarMode, createResourceUrl: boolean): Promise<LocalAvatarLoadResult<LocalAvatarInfo>> {
|
||||
const loadResult = await this.loadAvatarRequest(serverUniqueId, mode);
|
||||
if(loadResult.status !== "success") {
|
||||
if (loadResult.status !== "success") {
|
||||
return loadResult;
|
||||
}
|
||||
|
||||
|
@ -112,28 +112,28 @@ export class OwnAvatarStorage {
|
|||
const avatarDateModified = parseInt(headers.get("X-File-Date-Modified"));
|
||||
const avatarDateUploaded = parseInt(headers.get("X-File-Uploaded"));
|
||||
|
||||
if(!avatarHash) {
|
||||
if (!avatarHash) {
|
||||
return { status: "error", reason: tr("missing response header file hash") };
|
||||
}
|
||||
|
||||
if(!avatarName) {
|
||||
if (!avatarName) {
|
||||
return { status: "error", reason: tr("missing response header file name") };
|
||||
}
|
||||
|
||||
if(isNaN(avatarSize)) {
|
||||
if (isNaN(avatarSize)) {
|
||||
return { status: "error", reason: tr("missing/invalid response header file size") };
|
||||
}
|
||||
|
||||
if(isNaN(avatarDateModified)) {
|
||||
if (isNaN(avatarDateModified)) {
|
||||
return { status: "error", reason: tr("missing/invalid response header file modify date") };
|
||||
}
|
||||
|
||||
if(isNaN(avatarDateUploaded)) {
|
||||
if (isNaN(avatarDateUploaded)) {
|
||||
return { status: "error", reason: tr("missing/invalid response header file upload date") };
|
||||
}
|
||||
|
||||
let resourceUrl;
|
||||
if(createResourceUrl) {
|
||||
if (createResourceUrl) {
|
||||
try {
|
||||
resourceUrl = URL.createObjectURL(await loadResult.result.blob());
|
||||
} catch (error) {
|
||||
|
@ -159,12 +159,12 @@ export class OwnAvatarStorage {
|
|||
};
|
||||
}
|
||||
|
||||
async updateAvatar(serverUniqueId: string, mode: OwnAvatarMode, target: File) : Promise<LocalAvatarUpdateResult> {
|
||||
if(!this.openedCache) {
|
||||
async updateAvatar(serverUniqueId: string, mode: OwnAvatarMode, target: File): Promise<LocalAvatarUpdateResult> {
|
||||
if (!this.openedCache) {
|
||||
return { status: "cache-unavailable" };
|
||||
}
|
||||
|
||||
if(target.size > kMaxAvatarSize) {
|
||||
if (target.size > kMaxAvatarSize) {
|
||||
return { status: "error", reason: tra("Image exceeds maximum software size of {} bytes", kMaxAvatarSize) };
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@ export class OwnAvatarStorage {
|
|||
const hasher = crypto.algo.MD5.create();
|
||||
await target.stream().pipeTo(new WritableStream({
|
||||
write(data) {
|
||||
hasher.update(crypto.lib.WordArray.create(data));
|
||||
hasher.update(crypto.lib.WordArray.create(Array.from(data)));
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -203,7 +203,7 @@ export class OwnAvatarStorage {
|
|||
}
|
||||
|
||||
async removeAvatar(serverUniqueId: string, mode: OwnAvatarMode) {
|
||||
if(!this.openedCache) {
|
||||
if (!this.openedCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -219,13 +219,13 @@ export class OwnAvatarStorage {
|
|||
* @param serverUniqueId
|
||||
*/
|
||||
async avatarUploadSucceeded(serverUniqueId: string) {
|
||||
if(!this.openedCache) {
|
||||
if (!this.openedCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
const request = await this.loadAvatarRequest(serverUniqueId, "uploading");
|
||||
if(request.status !== "success") {
|
||||
if(request.status !== "empty-result") {
|
||||
if (request.status !== "success") {
|
||||
if (request.status !== "empty-result") {
|
||||
logError(LogCategory.GENERAL, tr("Failed to save uploaded avatar. Request failed to load: %o"), request);
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {InputStartError} from "tc-shared/voice/RecorderBase";
|
||||
import {LogCategory, logInfo, logWarn} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import { InputStartError } from "tc-shared/voice/RecorderBase";
|
||||
import { LogCategory, logInfo, logWarn } from "tc-shared/log";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
|
||||
export type MediaStreamType = "audio" | "video";
|
||||
|
||||
|
@ -18,15 +18,15 @@ export interface MediaStreamEvents {
|
|||
export const mediaStreamEvents = new Registry<MediaStreamEvents>();
|
||||
*/
|
||||
|
||||
export async function requestMediaStreamWithConstraints(constraints: MediaTrackConstraints, type: MediaStreamType) : Promise<InputStartError | MediaStream> {
|
||||
export async function requestMediaStreamWithConstraints(constraints: MediaTrackConstraints, type: MediaStreamType): Promise<InputStartError | MediaStream> {
|
||||
const beginTimestamp = Date.now();
|
||||
try {
|
||||
logInfo(LogCategory.AUDIO, tr("Requesting a %s stream for device %s in group %s"), type, constraints.deviceId, constraints.groupId);
|
||||
return await navigator.mediaDevices.getUserMedia(type === "audio" ? { audio: constraints } : { video: constraints });
|
||||
} catch(error) {
|
||||
if('name' in error) {
|
||||
if(error.name === "NotAllowedError") {
|
||||
if(Date.now() - beginTimestamp < 250) {
|
||||
} catch (error) {
|
||||
if ('name' in error) {
|
||||
if (error.name === "NotAllowedError") {
|
||||
if (Date.now() - beginTimestamp < 250) {
|
||||
logWarn(LogCategory.AUDIO, tr("Media stream request failed (System denied). Browser message: %o"), error.message);
|
||||
return InputStartError.ESYSTEMDENIED;
|
||||
} else {
|
||||
|
@ -46,21 +46,21 @@ export async function requestMediaStreamWithConstraints(constraints: MediaTrackC
|
|||
|
||||
/* request permission for devices only one per time! */
|
||||
let currentMediaStreamRequest: Promise<MediaStream | InputStartError>;
|
||||
export async function requestMediaStream(deviceId: string | undefined, groupId: string | undefined, type: MediaStreamType) : Promise<MediaStream | InputStartError> {
|
||||
export async function requestMediaStream(deviceId: string | undefined, groupId: string | undefined, type: MediaStreamType): Promise<MediaStream | InputStartError> {
|
||||
/* wait for the current media stream requests to finish */
|
||||
while(currentMediaStreamRequest) {
|
||||
while (currentMediaStreamRequest) {
|
||||
try {
|
||||
await currentMediaStreamRequest;
|
||||
} catch(error) { }
|
||||
} catch (error) { }
|
||||
}
|
||||
|
||||
const constrains: MediaTrackConstraints = {};
|
||||
if(window.detectedBrowser?.name === "firefox") {
|
||||
if (window.detectedBrowser?.name === "firefox") {
|
||||
/*
|
||||
* Firefox only allows to open one mic/video as well deciding whats the input device it.
|
||||
* It does not respect the deviceId nor the groupId
|
||||
*/
|
||||
} else if(deviceId !== undefined) {
|
||||
} else if (deviceId !== undefined) {
|
||||
constrains.deviceId = deviceId;
|
||||
constrains.groupId = groupId;
|
||||
} else {
|
||||
|
@ -75,25 +75,28 @@ export async function requestMediaStream(deviceId: string | undefined, groupId:
|
|||
try {
|
||||
return await currentMediaStreamRequest;
|
||||
} finally {
|
||||
if(currentMediaStreamRequest === promise) {
|
||||
if (currentMediaStreamRequest === promise) {
|
||||
currentMediaStreamRequest = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function queryMediaPermissions(type: MediaStreamType, changeListener?: (value: PermissionState) => void) : Promise<PermissionState> {
|
||||
if('permissions' in navigator && 'query' in navigator.permissions) {
|
||||
try {
|
||||
const result = await navigator.permissions.query({ name: type === "audio" ? "microphone" : "camera" });
|
||||
if(changeListener) {
|
||||
result.addEventListener("change", () => {
|
||||
changeListener(result.state);
|
||||
});
|
||||
}
|
||||
return result.state;
|
||||
} catch (error) {
|
||||
logWarn(LogCategory.GENERAL, tr("Failed to query for %s permissions: %s"), type, error);
|
||||
export async function queryMediaPermissions(type: MediaStreamType, changeListener?: (value: PermissionState) => void): Promise<PermissionState> {
|
||||
try {
|
||||
// @ts-ignore needed, as firefox doesn't allow microphone or camera, caught using the catch below
|
||||
const result = await navigator.permissions.query({ name: type === "audio" ? "microphone" : "camera" });
|
||||
if (changeListener) {
|
||||
result.addEventListener("change", () => {
|
||||
changeListener(result.state);
|
||||
});
|
||||
}
|
||||
return result.state;
|
||||
} catch (error) {
|
||||
// Firefox doesn't support querying for the camera / microphone permission, so return undetermined status
|
||||
if (error instanceof TypeError) {
|
||||
return "prompt";
|
||||
}
|
||||
logWarn(LogCategory.GENERAL, tr("Failed to query for %s permissions: %s"), type, error);
|
||||
}
|
||||
return "prompt";
|
||||
}
|
||||
|
@ -101,7 +104,7 @@ export async function queryMediaPermissions(type: MediaStreamType, changeListene
|
|||
export function stopMediaStream(stream: MediaStream) {
|
||||
stream.getVideoTracks().forEach(track => track.stop());
|
||||
stream.getAudioTracks().forEach(track => track.stop());
|
||||
if('stop' in stream) {
|
||||
if ('stop' in stream) {
|
||||
(stream as any).stop();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/* setup jsrenderer */
|
||||
import "jsrender";
|
||||
import {tr} from "./i18n/localize";
|
||||
import {LogCategory, logError, logTrace} from "tc-shared/log";
|
||||
import { tr } from "./i18n/localize";
|
||||
import { LogCategory, logError, logTrace } from "tc-shared/log";
|
||||
|
||||
(window as any).$ = require("jquery");
|
||||
(window as any).jQuery = $;
|
||||
|
@ -22,7 +22,7 @@ declare global {
|
|||
* @param entry The entry to toggle
|
||||
* @returns `true` if the entry has been inserted and false if the entry has been deleted
|
||||
*/
|
||||
toggle(entry: T) : boolean;
|
||||
toggle(entry: T): boolean;
|
||||
|
||||
/**
|
||||
* @param entry The entry to toggle
|
||||
|
@ -33,21 +33,21 @@ declare global {
|
|||
}
|
||||
|
||||
interface JSON {
|
||||
map_to<T>(object: T, json: any, variables?: string | string[], validator?: (map_field: string, map_value: string) => boolean, variable_direction?: number) : number;
|
||||
map_field_to<T>(object: T, value: any, field: string) : boolean;
|
||||
map_to<T>(object: T, json: any, variables?: string | string[], validator?: (map_field: string, map_value: string) => boolean, variable_direction?: number): number;
|
||||
map_field_to<T>(object: T, value: any, field: string): boolean;
|
||||
}
|
||||
|
||||
type JQueryScrollType = "height" | "width";
|
||||
interface JQuery<TElement = HTMLElement> {
|
||||
renderTag(values?: any) : JQuery<TElement>;
|
||||
hasScrollBar(direction?: JQueryScrollType) : boolean;
|
||||
renderTag(values?: any): JQuery<TElement>;
|
||||
hasScrollBar(direction?: JQueryScrollType): boolean;
|
||||
|
||||
|
||||
visible_height() : number;
|
||||
visible_width() : number;
|
||||
visible_height(): number;
|
||||
visible_width(): number;
|
||||
|
||||
/* first element which matches the selector, could be the element itself or a parent */
|
||||
firstParent(selector: string) : JQuery;
|
||||
firstParent(selector: string): JQuery;
|
||||
}
|
||||
|
||||
interface JQueryStatic<TElement extends Node = HTMLElement> {
|
||||
|
@ -73,9 +73,6 @@ declare global {
|
|||
name: string,
|
||||
version: string
|
||||
};
|
||||
|
||||
mozGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void;
|
||||
webkitGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void;
|
||||
}
|
||||
|
||||
interface ObjectConstructor {
|
||||
|
@ -83,9 +80,9 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
export type IfEquals<X, Y, A=X, B=never> =
|
||||
export type IfEquals<X, Y, A = X, B = never> =
|
||||
(<T>() => T extends X ? 1 : 2) extends
|
||||
(<T>() => T extends Y ? 1 : 2) ? A : B;
|
||||
(<T>() => T extends Y ? 1 : 2) ? A : B;
|
||||
|
||||
export type WritableKeys<T> = {
|
||||
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P, never>
|
||||
|
@ -95,7 +92,7 @@ export type ReadonlyKeys<T> = {
|
|||
[P in keyof T]: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
|
||||
}[keyof T];
|
||||
|
||||
if(!Object.isSimilar) {
|
||||
if (!Object.isSimilar) {
|
||||
Object.isSimilar = function (a, b) {
|
||||
const aType = typeof a;
|
||||
const bType = typeof b;
|
||||
|
@ -106,8 +103,8 @@ if(!Object.isSimilar) {
|
|||
if (aType === "object") {
|
||||
const aKeys = Object.keys(a);
|
||||
const bKeys = Object.keys(b);
|
||||
if(aKeys.length != bKeys.length) { return false; }
|
||||
if(aKeys.findIndex(key => bKeys.indexOf(key) !== -1) !== -1) { return false; }
|
||||
if (aKeys.length != bKeys.length) { return false; }
|
||||
if (aKeys.findIndex(key => bKeys.indexOf(key) !== -1) !== -1) { return false; }
|
||||
return aKeys.findIndex(key => !Object.isSimilar(a[key], b[key])) === -1;
|
||||
} else {
|
||||
return a === b;
|
||||
|
@ -115,7 +112,7 @@ if(!Object.isSimilar) {
|
|||
};
|
||||
}
|
||||
|
||||
if(!JSON.map_to) {
|
||||
if (!JSON.map_to) {
|
||||
JSON.map_to = function <T>(object: T, json: any, variables?: string | string[], validator?: (map_field: string, map_value: string) => boolean, variable_direction?: number): number {
|
||||
if (!validator)
|
||||
validator = () => true;
|
||||
|
@ -145,29 +142,29 @@ if(!JSON.map_to) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if(JSON.map_field_to(object, json[field], field))
|
||||
if (JSON.map_field_to(object, json[field], field))
|
||||
updates++;
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
}
|
||||
|
||||
if(!JSON.map_field_to) {
|
||||
JSON.map_field_to = function<T>(object: T, value: any, field: string) : boolean {
|
||||
if (!JSON.map_field_to) {
|
||||
JSON.map_field_to = function <T>(object: T, value: any, field: string): boolean {
|
||||
let fieldType = typeof object[field];
|
||||
let newValue;
|
||||
if(fieldType == "string" || fieldType == "object" || fieldType == "undefined") {
|
||||
if (fieldType == "string" || fieldType == "object" || fieldType == "undefined") {
|
||||
newValue = value;
|
||||
} else if(fieldType == "number") {
|
||||
} else if (fieldType == "number") {
|
||||
newValue = parseFloat(value);
|
||||
} else if(fieldType == "boolean") {
|
||||
} else if (fieldType == "boolean") {
|
||||
newValue = typeof value === "boolean" && value || value === "1" || value === "true";
|
||||
} else {
|
||||
console.warn(tr("Invalid object type %s for entry %s"), fieldType, field);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(newValue === object[field]) {
|
||||
if (newValue === object[field]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -177,7 +174,7 @@ if(!JSON.map_field_to) {
|
|||
}
|
||||
|
||||
if (!Array.prototype.remove) {
|
||||
Array.prototype.remove = function<T>(elem?: T): boolean {
|
||||
Array.prototype.remove = function <T>(elem?: T): boolean {
|
||||
const index = this.indexOf(elem);
|
||||
if (index > -1) {
|
||||
this.splice(index, 1);
|
||||
|
@ -188,18 +185,18 @@ if (!Array.prototype.remove) {
|
|||
}
|
||||
|
||||
if (!Array.prototype.pop_front) {
|
||||
Array.prototype.pop_front = function<T>(): T {
|
||||
if(this.length == 0) return undefined;
|
||||
Array.prototype.pop_front = function <T>(): T {
|
||||
if (this.length == 0) return undefined;
|
||||
return this.splice(0, 1)[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.prototype.toggle) {
|
||||
Array.prototype.toggle = function<T>(element: T, insert?: boolean): boolean {
|
||||
Array.prototype.toggle = function <T>(element: T, insert?: boolean): boolean {
|
||||
const index = this.findIndex(e => e === element);
|
||||
if((index !== -1) === insert) {
|
||||
if ((index !== -1) === insert) {
|
||||
return false;
|
||||
} else if(index === -1) {
|
||||
} else if (index === -1) {
|
||||
this.push(element);
|
||||
return true;
|
||||
} else {
|
||||
|
@ -209,25 +206,25 @@ if (!Array.prototype.toggle) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!Array.prototype.last){
|
||||
Array.prototype.last = function(){
|
||||
if(this.length == 0) return undefined;
|
||||
if (!Array.prototype.last) {
|
||||
Array.prototype.last = function () {
|
||||
if (this.length == 0) return undefined;
|
||||
return this[this.length - 1];
|
||||
};
|
||||
}
|
||||
|
||||
if(typeof ($) !== "undefined") {
|
||||
if(!$.spawn) {
|
||||
$.spawn = function<K extends keyof HTMLElementTagNameMap>(tagName: K): JQuery<HTMLElementTagNameMap[K]> {
|
||||
if (typeof ($) !== "undefined") {
|
||||
if (!$.spawn) {
|
||||
$.spawn = function <K extends keyof HTMLElementTagNameMap>(tagName: K): JQuery<HTMLElementTagNameMap[K]> {
|
||||
return $(document.createElement(tagName) as any);
|
||||
}
|
||||
}
|
||||
|
||||
if(!$.fn.renderTag) {
|
||||
$.fn.renderTag = function (this: JQuery, values?: any) : JQuery {
|
||||
if (!$.fn.renderTag) {
|
||||
$.fn.renderTag = function (this: JQuery, values?: any): JQuery {
|
||||
let result;
|
||||
const template = $.views.templates[this.attr("id")];
|
||||
if(!template) {
|
||||
if (!template) {
|
||||
console.error("Tried to render template %o, but template is not available!", this.attr("id"));
|
||||
throw "missing template " + this.attr("id");
|
||||
}
|
||||
|
@ -243,30 +240,30 @@ if(typeof ($) !== "undefined") {
|
|||
return result;
|
||||
}
|
||||
}
|
||||
if(!$.fn.hasScrollBar)
|
||||
$.fn.hasScrollBar = function(direction?: "height" | "width") {
|
||||
if(this.length <= 0)
|
||||
if (!$.fn.hasScrollBar)
|
||||
$.fn.hasScrollBar = function (direction?: "height" | "width") {
|
||||
if (this.length <= 0)
|
||||
return false;
|
||||
|
||||
const scroll_height = this.get(0).scrollHeight > this.height();
|
||||
const scroll_width = this.get(0).scrollWidth > this.width();
|
||||
|
||||
if(typeof(direction) === "string") {
|
||||
if(direction === "height")
|
||||
if (typeof (direction) === "string") {
|
||||
if (direction === "height")
|
||||
return scroll_height;
|
||||
if(direction === "width")
|
||||
if (direction === "width")
|
||||
return scroll_width;
|
||||
}
|
||||
return scroll_width || scroll_height;
|
||||
};
|
||||
|
||||
if(!$.fn.visible_height)
|
||||
if (!$.fn.visible_height)
|
||||
$.fn.visible_height = function (this: JQuery<HTMLElement>) {
|
||||
const original_style = this.attr("style");
|
||||
this.css({
|
||||
position: 'absolute!important',
|
||||
position: 'absolute!important',
|
||||
visibility: 'hidden!important',
|
||||
display: 'block!important'
|
||||
display: 'block!important'
|
||||
});
|
||||
|
||||
const result = this.height();
|
||||
|
@ -274,13 +271,13 @@ if(typeof ($) !== "undefined") {
|
|||
return result;
|
||||
};
|
||||
|
||||
if(!$.fn.visible_width)
|
||||
if (!$.fn.visible_width)
|
||||
$.fn.visible_width = function (this: JQuery<HTMLElement>) {
|
||||
const original_style = this.attr("style");
|
||||
this.css({
|
||||
position: 'absolute!important',
|
||||
position: 'absolute!important',
|
||||
visibility: 'hidden!important',
|
||||
display: 'block!important'
|
||||
display: 'block!important'
|
||||
});
|
||||
|
||||
const result = this.width();
|
||||
|
@ -288,20 +285,20 @@ if(typeof ($) !== "undefined") {
|
|||
return result;
|
||||
};
|
||||
|
||||
if(!$.fn.firstParent)
|
||||
if (!$.fn.firstParent)
|
||||
$.fn.firstParent = function (this: JQuery<HTMLElement>, selector: string) {
|
||||
if(this.is(selector))
|
||||
if (this.is(selector))
|
||||
return this;
|
||||
return this.parent(selector);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Object.values) {
|
||||
if (!Object.values) {
|
||||
Object.values = object => Object.keys(object).map(e => object[e]);
|
||||
}
|
||||
|
||||
export function crashOnThrow<T>(promise: Promise<T> | (() => Promise<T>)) : Promise<T> {
|
||||
if(typeof promise === "function") {
|
||||
export function crashOnThrow<T>(promise: Promise<T> | (() => Promise<T>)): Promise<T> {
|
||||
if (typeof promise === "function") {
|
||||
try {
|
||||
promise = promise();
|
||||
} catch (error) {
|
||||
|
@ -314,11 +311,11 @@ export function crashOnThrow<T>(promise: Promise<T> | (() => Promise<T>)) : Prom
|
|||
logError(LogCategory.GENERAL, tr("Critical app error: %o"), error);
|
||||
|
||||
/* Lets make this promise stuck for ever */
|
||||
return new Promise(() => {});
|
||||
return new Promise(() => { });
|
||||
});
|
||||
}
|
||||
|
||||
export function ignorePromise<T>(_promise: Promise<T>) {}
|
||||
export function ignorePromise<T>(_promise: Promise<T>) { }
|
||||
|
||||
export function NoThrow(target: any, methodName: string, descriptor: PropertyDescriptor) {
|
||||
const crashApp = error => {
|
||||
|
@ -332,13 +329,13 @@ export function NoThrow(target: any, methodName: string, descriptor: PropertyDes
|
|||
descriptor.value = function () {
|
||||
try {
|
||||
const result = originalMethod.apply(this, arguments);
|
||||
if(result instanceof Promise) {
|
||||
if (result instanceof Promise) {
|
||||
promiseAccepted.value = true;
|
||||
return result.catch(error => {
|
||||
crashApp(error);
|
||||
|
||||
/* Lets make this promise stuck for ever since we're in a not well defined state */
|
||||
return new Promise(() => {});
|
||||
return new Promise(() => { });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -346,14 +343,14 @@ export function NoThrow(target: any, methodName: string, descriptor: PropertyDes
|
|||
} catch (error) {
|
||||
crashApp(error);
|
||||
|
||||
if(!promiseAccepted.value) {
|
||||
if (!promiseAccepted.value) {
|
||||
throw error;
|
||||
} else {
|
||||
/*
|
||||
* We don't know if we can return a promise or if just the object is expected.
|
||||
* Since we don't know that, we're just rethrowing the error for now.
|
||||
*/
|
||||
return new Promise(() => {});
|
||||
return new Promise(() => { });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -365,7 +362,7 @@ export function CallOnce(target: any, methodName: string, descriptor: PropertyDe
|
|||
|
||||
const originalMethod: Function = descriptor.value;
|
||||
descriptor.value = function () {
|
||||
if(callOnceData[methodName]) {
|
||||
if (callOnceData[methodName]) {
|
||||
debugger;
|
||||
throw "method " + methodName + " has already been called";
|
||||
}
|
||||
|
@ -378,7 +375,7 @@ const kNonNullSymbol = Symbol("non-null-data");
|
|||
export function NonNull(target: any, methodName: string, parameterIndex: number) {
|
||||
const nonNullInfo = target[kNonNullSymbol] || (target[kNonNullSymbol] = {});
|
||||
const methodInfo = nonNullInfo[methodName] || (nonNullInfo[methodName] = {});
|
||||
if(!Array.isArray(methodInfo.indexes)) {
|
||||
if (!Array.isArray(methodInfo.indexes)) {
|
||||
/* Initialize method info */
|
||||
methodInfo.overloaded = false;
|
||||
methodInfo.indexes = [];
|
||||
|
@ -386,7 +383,7 @@ export function NonNull(target: any, methodName: string, parameterIndex: number)
|
|||
|
||||
methodInfo.indexes.push(parameterIndex);
|
||||
setImmediate(() => {
|
||||
if(methodInfo.overloaded || methodInfo.missingWarned) {
|
||||
if (methodInfo.overloaded || methodInfo.missingWarned) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -401,21 +398,21 @@ export function NonNull(target: any, methodName: string, parameterIndex: number)
|
|||
*/
|
||||
export function ParameterConstrained(target: any, methodName: string, descriptor: PropertyDescriptor) {
|
||||
const nonNullInfo = target[kNonNullSymbol];
|
||||
if(!nonNullInfo) {
|
||||
if (!nonNullInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const methodInfo = nonNullInfo[methodName] || (nonNullInfo[methodName] = {});
|
||||
if(!methodInfo) {
|
||||
if (!methodInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
methodInfo.overloaded = true;
|
||||
const originalMethod: Function = descriptor.value;
|
||||
descriptor.value = function () {
|
||||
for(let index = 0; index < methodInfo.indexes.length; index++) {
|
||||
for (let index = 0; index < methodInfo.indexes.length; index++) {
|
||||
const argument = arguments[methodInfo.indexes[index]];
|
||||
if(typeof argument === undefined || typeof argument === null) {
|
||||
if (typeof argument === undefined || typeof argument === null) {
|
||||
throw "parameter " + methodInfo.indexes[index] + " should not be null or undefined";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import * as React from "react";
|
||||
import {IpcInviteInfo, IpcInviteInfoLoaded} from "tc-shared/text/bbcode/InviteDefinitions";
|
||||
import {ChannelMessage, getIpcInstance, IPCChannel} from "tc-shared/ipc/BrowserIPC";
|
||||
import { IpcInviteInfo, IpcInviteInfoLoaded } from "tc-shared/text/bbcode/InviteDefinitions";
|
||||
import { ChannelMessage, getIpcInstance, IPCChannel } from "tc-shared/ipc/BrowserIPC";
|
||||
import * as loader from "tc-loader";
|
||||
import {useEffect, useState} from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import _ = require("lodash");
|
||||
import {Translatable} from "tc-shared/ui/react-elements/i18n";
|
||||
import {Button} from "tc-shared/ui/react-elements/Button";
|
||||
import {SimpleUrlRenderer} from "tc-shared/text/bbcode/url";
|
||||
import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots";
|
||||
import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons";
|
||||
import {ClientIcon} from "svg-sprites/client-icons";
|
||||
import { Translatable } from "tc-shared/ui/react-elements/i18n";
|
||||
import { Button } from "tc-shared/ui/react-elements/Button";
|
||||
import { SimpleUrlRenderer } from "tc-shared/text/bbcode/url";
|
||||
import { LoadingDots } from "tc-shared/ui/react-elements/LoadingDots";
|
||||
import { ClientIconRenderer } from "tc-shared/ui/react-elements/Icons";
|
||||
import { ClientIcon } from "svg-sprites/client-icons";
|
||||
|
||||
const cssStyle = require("./InviteRenderer.scss");
|
||||
const kInviteUrlRegex = /^(https:\/\/)?(teaspeak.de\/|join.teaspeak.de\/(invite\/)?)([a-zA-Z0-9]{4})$/gm;
|
||||
|
||||
export function isInviteLink(url: string) : boolean {
|
||||
export function isInviteLink(url: string): boolean {
|
||||
kInviteUrlRegex.lastIndex = 0;
|
||||
return !!url.match(kInviteUrlRegex);
|
||||
}
|
||||
|
@ -26,25 +26,25 @@ const localInviteCache: { [key: string]: InviteCacheEntry } = {};
|
|||
const localInviteCallbacks: { [key: string]: (() => void)[] } = {};
|
||||
|
||||
const useInviteLink = (linkId: string): LocalInviteInfo => {
|
||||
if(!localInviteCache[linkId]) {
|
||||
if (!localInviteCache[linkId]) {
|
||||
localInviteCache[linkId] = { status: { status: "loading" }, timeout: setTimeout(() => delete localInviteCache[linkId], 60 * 1000) };
|
||||
ipcChannel?.sendMessage("query", { linkId });
|
||||
}
|
||||
|
||||
const [ value, setValue ] = useState(localInviteCache[linkId].status);
|
||||
const [value, setValue] = useState(localInviteCache[linkId].status);
|
||||
|
||||
useEffect(() => {
|
||||
if(typeof localInviteCache[linkId]?.status === "undefined") {
|
||||
if (typeof localInviteCache[linkId]?.status === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_.isEqual(value, localInviteCache[linkId].status)) {
|
||||
if (!_.isEqual(value, localInviteCache[linkId].status)) {
|
||||
setValue(localInviteCache[linkId].status);
|
||||
}
|
||||
|
||||
const callback = () => setValue(localInviteCache[linkId].status);
|
||||
(localInviteCallbacks[linkId] || (localInviteCallbacks[linkId] = [])).push(callback);
|
||||
return () => localInviteCallbacks[linkId]?.remove(callback);
|
||||
return () => { localInviteCallbacks[linkId]?.remove(callback); }
|
||||
}, [linkId]);
|
||||
|
||||
return value;
|
||||
|
@ -69,14 +69,14 @@ const LoadedInviteRenderer = React.memo((props: { info: IpcInviteInfoLoaded }) =
|
|||
</div>
|
||||
);
|
||||
|
||||
const [, setRevision ] = useState(0);
|
||||
const [, setRevision] = useState(0);
|
||||
useEffect(() => {
|
||||
if(props.info.expireTimestamp === 0) {
|
||||
if (props.info.expireTimestamp === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeout = props.info.expireTimestamp - (Date.now() / 1000);
|
||||
if(timeout <= 0) {
|
||||
if (timeout <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ const LoadedInviteRenderer = React.memo((props: { info: IpcInviteInfoLoaded }) =
|
|||
return () => clearTimeout(timeoutId);
|
||||
});
|
||||
|
||||
if(props.info.expireTimestamp > 0 && Date.now() / 1000 >= props.info.expireTimestamp) {
|
||||
if (props.info.expireTimestamp > 0 && Date.now() / 1000 >= props.info.expireTimestamp) {
|
||||
return (
|
||||
<InviteErrorRenderer noTitle={true} key={"expired"}>
|
||||
<Translatable>Link expired</Translatable>
|
||||
|
@ -92,7 +92,7 @@ const LoadedInviteRenderer = React.memo((props: { info: IpcInviteInfoLoaded }) =
|
|||
);
|
||||
}
|
||||
|
||||
if(props.info.channelName) {
|
||||
if (props.info.channelName) {
|
||||
return (
|
||||
<div className={cssStyle.container + " " + cssStyle.info} key={"with-channel"}>
|
||||
<div className={cssStyle.left}>
|
||||
|
@ -215,8 +215,8 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
|||
|
||||
|
||||
function handleIpcMessage(remoteId: string, broadcast: boolean, message: ChannelMessage) {
|
||||
if(message.type === "query-result") {
|
||||
if(!localInviteCache[message.data.linkId]) {
|
||||
if (message.type === "query-result") {
|
||||
if (!localInviteCache[message.data.linkId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -8,14 +8,27 @@
|
|||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": "../../",
|
||||
"paths": {
|
||||
"tc-shared/*": ["shared/js/*"],
|
||||
"tc-loader": ["loader/exports/loader.d.ts"],
|
||||
"svg-sprites/*": ["shared/svg-sprites/*"],
|
||||
"vendor/xbbcode/*": ["vendor/xbbcode/src/*"],
|
||||
"tc-events": ["vendor/TeaEventBus/src/index.ts"],
|
||||
"tc-services": ["vendor/TeaClientServices/src/index.ts"]
|
||||
"tc-shared/*": [
|
||||
"shared/js/*"
|
||||
],
|
||||
"tc-loader": [
|
||||
"loader/exports/loader.d.ts"
|
||||
],
|
||||
"svg-sprites/*": [
|
||||
"shared/svg-sprites/*"
|
||||
],
|
||||
"vendor/xbbcode/*": [
|
||||
"vendor/xbbcode/src/*"
|
||||
],
|
||||
"tc-events": [
|
||||
"vendor/TeaEventBus/src/index.ts"
|
||||
],
|
||||
"tc-services": [
|
||||
"vendor/TeaClientServices/src/index.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"plugins": [ /* ttypescript */
|
||||
{
|
||||
"transform": "../../tools/trgen/ttsc_transformer.js",
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"lib": ["es6"],
|
||||
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"typeRoots": [],
|
||||
"types": [],
|
||||
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"files": [
|
||||
|
|
|
@ -4,20 +4,22 @@
|
|||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"sourceMap": true,
|
||||
"lib": ["es6", "dom"],
|
||||
"lib": [
|
||||
"es6",
|
||||
"dom"
|
||||
],
|
||||
"removeComments": false,
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"webpack.config.ts",
|
||||
"webpack-client.config.ts",
|
||||
"webpack-web.config.ts",
|
||||
|
||||
"webpack/build-definitions.d.ts",
|
||||
"webpack/HtmlWebpackInlineSource.ts",
|
||||
"webpack/WatLoader.ts",
|
||||
"webpack/ManifestPlugin.ts",
|
||||
|
||||
"babel.config.ts",
|
||||
"postcss.config.ts",
|
||||
"file.ts"
|
||||
|
|
|
@ -5,19 +5,35 @@
|
|||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"sourceMap": true,
|
||||
"lib": ["ES7", "dom", "dom.iterable"],
|
||||
"lib": [
|
||||
"ES7",
|
||||
"dom",
|
||||
"dom.iterable"
|
||||
],
|
||||
"removeComments": true, /* we dont really need them within the target files */
|
||||
"jsx": "react",
|
||||
"esModuleInterop": true,
|
||||
"baseUrl": ".",
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"tc-shared/*": ["shared/js/*"],
|
||||
"tc-loader": ["loader/exports/loader.d.ts"],
|
||||
"tc-events": ["vendor/TeaEventBus/src/index.ts"],
|
||||
"tc-services": ["vendor/TeaClientServices/src/index.ts"],
|
||||
|
||||
"svg-sprites/*": ["shared/svg-sprites/*"],
|
||||
"vendor/xbbcode/*": ["vendor/xbbcode/src/*"]
|
||||
"tc-shared/*": [
|
||||
"shared/js/*"
|
||||
],
|
||||
"tc-loader": [
|
||||
"loader/exports/loader.d.ts"
|
||||
],
|
||||
"tc-events": [
|
||||
"vendor/TeaEventBus/src/index.ts"
|
||||
],
|
||||
"tc-services": [
|
||||
"vendor/TeaClientServices/src/index.ts"
|
||||
],
|
||||
"svg-sprites/*": [
|
||||
"shared/svg-sprites/*"
|
||||
],
|
||||
"vendor/xbbcode/*": [
|
||||
"vendor/xbbcode/src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
|
|
Loading…
Reference in New Issue