Merge pull request #182 from TeaSpeak/develop
Finally merge these changes since TeaSpeak 1.5 is released as stable. Note: This merge will break compatibility with all 1.4 TeaSpeak server versions!master
|
@ -14,6 +14,7 @@ node_modules/
|
|||
|
||||
# Some build output
|
||||
/dist/
|
||||
/dist-package/
|
||||
/declarations/
|
||||
/travis-build/
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
dist: trusty
|
||||
dist: bionic
|
||||
|
||||
language: node_js
|
||||
node_js:
|
||||
- "12"
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get -y install coreutils
|
||||
- chmod +x ./scripts/travis/build.sh
|
||||
- chmod +x ./scripts/travis/deploy_server.sh
|
||||
- chmod +x ./scripts/travis/deploy_github.sh
|
||||
|
|
79
ChangeLog.md
|
@ -1,4 +1,83 @@
|
|||
# Changelog:
|
||||
* **26.05.21**
|
||||
- Fixed automated builds
|
||||
- Fixed the bookmark UI popout window
|
||||
- Added a context menu for the general bookmark container
|
||||
|
||||
* **05.05.21**
|
||||
- Reworked the icon modal
|
||||
- Fixed some minor icon and avatar related issues
|
||||
- Improved icon modal performance
|
||||
|
||||
* **29.04.21**
|
||||
- Fixed a bug which caused chat messages to appear twice
|
||||
- Adding support for poping out channel conversations
|
||||
|
||||
* **27.04.21**
|
||||
- Implemented support for showing the video feed watchers
|
||||
- Updating the channel tree if the channel client order changes
|
||||
|
||||
* **24.04.21**
|
||||
- Removed the old server info modal and using the new React based and popoutable modal
|
||||
- Using the new React modal for the server info dialog. The modal now also has improved permission and error visualisation
|
||||
- Improved tooltip handling
|
||||
|
||||
* **19.04.21**
|
||||
- Fixed a bug that the client video box is shown as active even though the client does not stream any video
|
||||
- Fixed a bug that the video fullscreen windows pops open when a client leaves/joins the channel
|
||||
- Removed extra new line after blockquote for the markdown renderer
|
||||
|
||||
* **05.04.21**
|
||||
- Fixed the mute but for the webclient
|
||||
- Fixed that "always active" microphone filter now works reliably
|
||||
- Improved the recorder API
|
||||
|
||||
* **29.03.21**
|
||||
- Acquiring the default input recorder when opening the settings
|
||||
- Adding new modal Input Processing Properties for the native client
|
||||
- Fixed that you can't finish off the name editing by pressing enter
|
||||
|
||||
* **25.03.21**
|
||||
- Allowing to directly select the speaker output device
|
||||
- Saving the speaker output device
|
||||
|
||||
* **24.03.21**
|
||||
- Improved the avatar upload modal (now way more intuitive)
|
||||
- Fixed a bug which cause client avatars to be stuck within the loading screen
|
||||
- Don't spam permission errors if we don't have the permission to view the channel description
|
||||
- Showing channel group inheritance within the client info frame
|
||||
|
||||
* **23.03.21**
|
||||
- Made the permission editor popoutable
|
||||
- Now using SVG flags for higher quality.
|
||||
- Fixed issue [#74](https://github.com/TeaSpeak/TeaWeb/issues/74) (Swiss flag box has black background)
|
||||
- Fixed issue that middle clicking on the channel does not shows the channel file browser instead it shows the global one
|
||||
|
||||
* **21.03.21**
|
||||
- Reworked the server group assignment modal. It now better reacts to the user input as well is now popoutable
|
||||
|
||||
* **18.03.21**
|
||||
- Finally, got fully rid of the initial backend glue and changes all systems to provider
|
||||
|
||||
* **17.03.21**
|
||||
- Updated from webpack 4 to webpack 5
|
||||
- Reworked the client build process
|
||||
- Using webpack dev server from now on
|
||||
|
||||
* **14.03.21**
|
||||
- Enchanted the bookmark system
|
||||
- Added support for auto connect on startup
|
||||
- Cleaned and simplified up the bookmark UI
|
||||
- Added support for importing/exporting bookmarks
|
||||
- Added support for duplicating bookmarks
|
||||
- Adding support for default channels and passwords
|
||||
|
||||
* **12.03.21**
|
||||
- Added a new video spotlight mode which allows showing multiple videos at the same time as well as
|
||||
dragging and resizing them
|
||||
- Fixed a minor bug within the permission editor
|
||||
- Fixed the creation of channel groups
|
||||
|
||||
* **20.02.21**
|
||||
- Improved the browser IPC module
|
||||
- Added support for client invite links
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
export = api => {
|
||||
export default api => {
|
||||
api.cache(false);
|
||||
const presets = [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"corejs": { "version":3 },
|
||||
"corejs": {"version": 3},
|
||||
"useBuiltIns": "usage",
|
||||
"targets": {
|
||||
"edge": "17",
|
||||
|
@ -16,10 +16,12 @@ export = api => {
|
|||
}
|
||||
]
|
||||
];
|
||||
|
||||
const plugins = [
|
||||
["@babel/transform-runtime"],
|
||||
["@babel/plugin-transform-modules-commonjs"]
|
||||
];
|
||||
|
||||
return {
|
||||
presets,
|
||||
plugins
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
window.__native_client_init_shared(__webpack_require__);
|
||||
|
||||
import "../AppMain.scss";
|
||||
import "tc-shared/entry-points/MainApp";
|
|
@ -1,4 +1,2 @@
|
|||
window.__native_client_init_shared(__webpack_require__);
|
||||
|
||||
import "./index.scss";
|
||||
import "tc-shared/main";
|
||||
import "tc-shared/entry-points/ModalWindow";
|
|
@ -1,42 +0,0 @@
|
|||
# File structure
|
||||
The TeaSpeak web client is separated into 2 different parts.
|
||||
|
||||
## I) Application files
|
||||
Application files are all files which directly belong to the app itself.
|
||||
Like the javascript files who handle the UI stuff or even translation templates.
|
||||
Theses files are separated into two type of files.
|
||||
1. [Shared application files](#1-shared-application-files)
|
||||
2. [Web application files](#2-web-application-files)
|
||||
|
||||
### 1. Shared application files
|
||||
Containing all files used by the TeaSpeak client and the Web client.
|
||||
All of these files will be found within the folder `shared`.
|
||||
This folder follows the general application file structure.
|
||||
More information could be found [here](#application-file-structure)
|
||||
|
||||
### 2. Web application files
|
||||
All files which only belong to a browser only instance.
|
||||
All of these files will be found within the folder `web`.
|
||||
This folder follows the general application file structure.
|
||||
More information could be found [here](#application-file-structure)
|
||||
|
||||
### application file structure
|
||||
Every application root contains several subfolders.
|
||||
In the following list will be listed which files belong to which folder
|
||||
|
||||
| Folder | Description |
|
||||
| --- | --- |
|
||||
| `audio` | This folder contains all audio files used by the application. More information could be found [here](). |
|
||||
| `css` | This folder contains all style sheets used by the application. More information could be found [here](). |
|
||||
| `js` | This folder contains all javascript files used by the application. More information could be found [here](). |
|
||||
| `html` | This folder contains all HTML and PHP files used by the application. More information could be found [here](). |
|
||||
| `i18n` | This folder contains all default translations. Information about the translation system could be found [here](). |
|
||||
| `img` | This folder contains all image files. |
|
||||
|
||||
## I) Additional tools
|
||||
|
||||
## Environment builder
|
||||
The environment builder is one of the most important tools of the entire project.
|
||||
This tool, basically implemented in the file `files.php`, will be your helper while live developing.
|
||||
What this tool does is, it creates a final environment where you could navigate to with your browser.
|
||||
It merges all the type separated files, which had been listed above ([here](#application-file-structure)).
|
58
file.ts
|
@ -30,49 +30,26 @@ type ProjectResource = {
|
|||
}
|
||||
|
||||
const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [
|
||||
{ /* javascript files as manifest.json */
|
||||
"type": "js",
|
||||
"search-pattern": /.*\.(js|json|svg|png|css)$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "js/",
|
||||
"local-path": "./dist/"
|
||||
},
|
||||
|
||||
{ /* shared html files */
|
||||
"type": "html",
|
||||
"search-pattern": /^.*([a-zA-Z]+)\.(html|json)$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "./",
|
||||
"local-path": "./shared/html/"
|
||||
},
|
||||
{ /* javascript files as manifest.json */
|
||||
"type": "js",
|
||||
"search-pattern": /.*\.(js|json|svg)$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "js/",
|
||||
"local-path": "./dist/"
|
||||
},
|
||||
{ /* javascript files as manifest.json */
|
||||
"type": "html",
|
||||
"search-pattern": /.*\.html$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "./",
|
||||
"local-path": "./dist/"
|
||||
},
|
||||
{ /* Loader css file (only required in dev mode. In release it gets inlined) */
|
||||
"type": "css",
|
||||
"search-pattern": /.*\.css$/,
|
||||
"build-target": "dev",
|
||||
|
||||
"path": "css/",
|
||||
"local-path": "./loader/css/"
|
||||
},
|
||||
{ /* shared sound files */
|
||||
"type": "wav",
|
||||
"search-pattern": /.*\.wav$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "audio/",
|
||||
"local-path": "./shared/audio/"
|
||||
},
|
||||
{ /* shared data sound files */
|
||||
"type": "json",
|
||||
"search-pattern": /.*\.json/,
|
||||
"search-pattern": /.*\.(wav|json)$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "audio/",
|
||||
|
@ -87,15 +64,6 @@ const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [
|
|||
"path": "img/",
|
||||
"local-path": "./shared/img/"
|
||||
},
|
||||
{ /* assembly files */
|
||||
"web-only": true,
|
||||
"type": "wasm",
|
||||
"search-pattern": /.*\.(wasm)/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "js/",
|
||||
"local-path": "./dist/"
|
||||
}
|
||||
];
|
||||
|
||||
const APP_FILE_LIST_SHARED_VENDORS: ProjectResource[] = [];
|
||||
|
@ -297,7 +265,7 @@ namespace server {
|
|||
} else {
|
||||
server = http.createServer(handleHTTPRequest);
|
||||
}
|
||||
await new Promise((resolve, reject) => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.on('error', reject);
|
||||
server.listen(options.port, () => {
|
||||
server.off("error", reject);
|
||||
|
@ -308,7 +276,7 @@ namespace server {
|
|||
|
||||
export async function shutdown() {
|
||||
if(server) {
|
||||
await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve()));
|
||||
await new Promise<void>((resolve, reject) => server.close(error => error ? reject(error) : resolve()));
|
||||
server = undefined;
|
||||
}
|
||||
}
|
||||
|
@ -428,7 +396,7 @@ namespace watcher {
|
|||
this._process.addListener("error", this.handle_error.bind(this));
|
||||
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const id = setTimeout(reject, 5000, "timeout");
|
||||
this._callback_init = () => {
|
||||
clearTimeout(id);
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import * as path from "path";
|
||||
import EJSGenerator = require("../webpack/EJSGenerator");
|
||||
|
||||
class IndexGenerator extends EJSGenerator {
|
||||
constructor(options: {
|
||||
buildTarget: string;
|
||||
output: string,
|
||||
isDevelopment: boolean
|
||||
}) {
|
||||
super({
|
||||
variables: {
|
||||
build_target: options.buildTarget
|
||||
},
|
||||
output: options.output,
|
||||
initialJSEntryChunk: "loader",
|
||||
input: path.join(__dirname, "html/index.html.ejs"),
|
||||
minify: !options.isDevelopment,
|
||||
|
||||
embedInitialJSEntryChunk: !options.isDevelopment,
|
||||
embedInitialCSSFile: !options.isDevelopment,
|
||||
|
||||
initialCSSFile: {
|
||||
localFile: path.join(__dirname, "css/index.css"),
|
||||
publicFile: "css/index.css"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export = IndexGenerator;
|
|
@ -1,6 +1,6 @@
|
|||
import * as loader from "./loader/loader";
|
||||
import {Stage} from "./loader/loader";
|
||||
import {getUrlParameter} from "./loader/utils";
|
||||
import {getUrlParameter} from "./loader/Utils";
|
||||
|
||||
let overlay: HTMLDivElement;
|
||||
let setupContainer: HTMLDivElement;
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
import "core-js/stable";
|
||||
import "./polifill";
|
||||
import "./css";
|
||||
|
||||
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") {
|
||||
appLoader = new (require("./targets/empty").default);
|
||||
} else if(target === "manifest") {
|
||||
appLoader = new (require("./targets/maifest-target").default);
|
||||
} else {
|
||||
appLoader = new (require("./targets/app").default);
|
||||
}
|
||||
setTimeout(() => appLoader.execute(), 0);
|
||||
|
||||
export {};
|
||||
|
||||
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__ = {};
|
||||
}
|
||||
|
||||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject = function () {};
|
||||
}
|
||||
|
||||
/* Hello World message */
|
||||
{
|
||||
const clog = console.log;
|
||||
const print_security = () => {
|
||||
{
|
||||
const css = [
|
||||
"display: block",
|
||||
"text-align: center",
|
||||
"font-size: 42px",
|
||||
"font-weight: bold",
|
||||
"-webkit-text-stroke: 2px black",
|
||||
"color: red"
|
||||
].join(";");
|
||||
clog("%c ", "font-size: 100px;");
|
||||
clog("%cSecurity warning:", css);
|
||||
}
|
||||
{
|
||||
const css = [
|
||||
"display: block",
|
||||
"text-align: center",
|
||||
"font-size: 18px",
|
||||
"font-weight: bold"
|
||||
].join(";");
|
||||
|
||||
clog("%cPasting anything in here could give attackers access to your data.", css);
|
||||
clog("%cUnless you understand exactly what you are doing, close this window and stay safe.", css);
|
||||
clog("%c ", "font-size: 100px;");
|
||||
}
|
||||
};
|
||||
|
||||
/* 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(";");
|
||||
clog("%cHey, hold on!", css);
|
||||
}
|
||||
{
|
||||
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 ""; };
|
||||
|
||||
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);
|
||||
clog("%cyou'll be may interested to share them here: %chttps://github.com/TeaSpeak/TeaWeb", css, css_2);
|
||||
clog("%c ", display_detect);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,3 @@ body {
|
|||
box-sizing: border-box;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@import "loader";
|
||||
@import "overlay";
|
|
@ -0,0 +1,3 @@
|
|||
import "./index.scss";
|
||||
import "./loader.scss";
|
||||
import "./overlay.scss";
|
|
@ -0,0 +1,223 @@
|
|||
:global {
|
||||
$setup-time-normal: 80s / 24; /* 24 frames / sec; the initial sequence is 80 frames */
|
||||
$setup-time-halloween: 323s / 24;
|
||||
$loop-time-halloween: 25s / 24;
|
||||
|
||||
#loader-overlay {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
background: #1e1e1e;
|
||||
|
||||
user-select: none;
|
||||
|
||||
z-index: 10000000;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
-webkit-app-region: drag;
|
||||
|
||||
.container {
|
||||
flex-shrink: 0;
|
||||
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
width: 1000px;
|
||||
height: 1000px;
|
||||
|
||||
align-self: center;
|
||||
margin-bottom: 10vh;
|
||||
|
||||
transition-duration: .5s;
|
||||
|
||||
img {
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.setup, .idle {
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
display: none;
|
||||
|
||||
&.visible {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.setup.visible {
|
||||
&.normal {
|
||||
animation: loader-initial-sequence 0s cubic-bezier(.81,.01,.65,1.16) $setup-time-normal forwards;
|
||||
}
|
||||
|
||||
&.halloween {
|
||||
animation: loader-initial-sequence 0s cubic-bezier(.81,.01,.65,1.16) $setup-time-halloween forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.idle.animation-normal {
|
||||
img {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.steam {
|
||||
position: absolute;
|
||||
|
||||
top: 282px;
|
||||
left: 380px;
|
||||
|
||||
width: 249px;
|
||||
height: 125px;
|
||||
background: url("../../images/steam.png") 0 0;
|
||||
|
||||
animation: sprite-steam 2.5s steps(50) forwards infinite;
|
||||
}
|
||||
}
|
||||
|
||||
&.finishing {
|
||||
.idle {
|
||||
.steam {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bowl {
|
||||
animation: swipe-out-bowl .5s both;
|
||||
}
|
||||
|
||||
.text {
|
||||
animation: swipe-out-text .5s .12s both;
|
||||
}
|
||||
}
|
||||
|
||||
pointer-events: none;
|
||||
animation: overlay-fade .3s .2s both;
|
||||
}
|
||||
|
||||
.loader-stage {
|
||||
position: absolute;
|
||||
|
||||
left: 5px;
|
||||
bottom: 5px;
|
||||
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
/* Automated loader timeout */
|
||||
#loader-overlay:not(.initialized) + #critical-load:not(.shown) {
|
||||
display: block !important;
|
||||
opacity: 0;
|
||||
|
||||
animation: loader-setup-timeout 0s ease-in $setup-time-normal forwards;
|
||||
|
||||
.error::before {
|
||||
content: 'Failed to startup loader!';
|
||||
}
|
||||
|
||||
.detail::before {
|
||||
content: 'Lookup the console for more details';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 850px) {
|
||||
:global {
|
||||
#loader-overlay .container {
|
||||
transform: scale(.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-height: 700px) {
|
||||
:global {
|
||||
#loader-overlay .container {
|
||||
transform: scale(.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 400px) {
|
||||
:global {
|
||||
#loader-overlay .container {
|
||||
transform: scale(.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes :global(loader-initial-sequence) {
|
||||
to {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes :global(sprite-steam) {
|
||||
to {
|
||||
background-position: 0 -6250px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes :global(swipe-out-bowl) {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 1;
|
||||
transform: translate3d(-60px, 0, 0) skew(-5deg, 0) rotateY(-6deg);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate3d(700px, 0, 0) skew(30deg, 0) rotateZ(-6deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes :global(swipe-out-text) {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 1;
|
||||
transform: translate3d(-30px, 20px, 0) skew(-5deg, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate3d(550px, 0, 0) skew(30deg, 0) scale(.96, 1.25) rotateZ(6deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes :global(animation-nothing) {
|
||||
to {
|
||||
background-position: 0 -6250px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes :global(overlay-fade) {
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes :global(loader-setup-timeout) {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
:global {
|
||||
#overlay-no-js, #critical-load {
|
||||
z-index: 100000000;
|
||||
display: none;
|
||||
position: fixed;
|
||||
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
background: #1e1e1e;
|
||||
text-align: center;
|
||||
|
||||
-webkit-app-region: drag;
|
||||
|
||||
h1, h3, a {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
top: 20%;
|
||||
}
|
||||
|
||||
&.shown {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
#overlay-no-js {
|
||||
display: block;
|
||||
color: #999;
|
||||
|
||||
svg {
|
||||
fill: #999;
|
||||
}
|
||||
}
|
||||
|
||||
#critical-load {
|
||||
.img {
|
||||
height: 12em
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #bd1515;
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
.detail {
|
||||
color: #696363;
|
||||
margin-top: .5em
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (max-height: 750px) {
|
||||
:global {
|
||||
#critical-load .container {
|
||||
top: unset;
|
||||
}
|
||||
|
||||
#critical-load {
|
||||
font-size: .8rem;
|
||||
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#critical-load.shown {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +1,14 @@
|
|||
import "core-js/stable";
|
||||
import "./polifill";
|
||||
|
||||
import * as loader from "./loader/loader";
|
||||
import {ApplicationLoader} from "./loader/loader";
|
||||
import {getUrlParameter} from "./loader/utils";
|
||||
|
||||
window["loader"] = loader;
|
||||
/* 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") {
|
||||
appLoader = new (require("./targets/empty").default);
|
||||
} else if(target === "manifest") {
|
||||
appLoader = new (require("./targets/maifest-target").default);
|
||||
} else {
|
||||
appLoader = new (require("./targets/app").default);
|
||||
if(window["loader"]) {
|
||||
throw "an loader instance has already been defined";
|
||||
}
|
||||
setTimeout(() => appLoader.execute(), 0);
|
||||
|
||||
export {};
|
||||
export * from "./loader/loader";
|
||||
export * as loaderAnimation from "./animation";
|
||||
|
||||
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__ = {};
|
||||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject = function () {};
|
||||
import "./bootstrap";
|
||||
|
||||
/* FIXME: This is glue! */
|
||||
if(window["loader"]) {
|
||||
throw "an loader instance has already been defined";
|
||||
}
|
||||
window["loader"] = module.exports;
|
|
@ -0,0 +1,128 @@
|
|||
export type ResourceRequestResult = {
|
||||
status: "success"
|
||||
} | {
|
||||
status: "unknown-error",
|
||||
message: string
|
||||
} | {
|
||||
status: "error-event"
|
||||
} | {
|
||||
status: "timeout",
|
||||
givenTimeout: number
|
||||
};
|
||||
|
||||
export type ResourceType = "script" | "css" | "json";
|
||||
|
||||
export class ResourceRequest {
|
||||
private readonly type: ResourceType;
|
||||
private readonly name: string;
|
||||
|
||||
private status: "unset" | "pending" | "executing" | "executed";
|
||||
private result: ResourceRequestResult | undefined;
|
||||
|
||||
private timestampEnqueue: number;
|
||||
private timestampExecuting: number;
|
||||
private timestampExecuted: number;
|
||||
|
||||
constructor(type: ResourceType, name: string) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
|
||||
this.status = "unset";
|
||||
}
|
||||
|
||||
markEnqueue() {
|
||||
if(this.status !== "unset") {
|
||||
console.warn("ResourceRequest %s status isn't unset.", this.name);
|
||||
return;
|
||||
}
|
||||
|
||||
this.timestampEnqueue = performance.now();
|
||||
this.status = "pending";
|
||||
}
|
||||
|
||||
markExecuting() {
|
||||
switch (this.status) {
|
||||
case "unset":
|
||||
/* the markEnqueue() invoke has been skipped */
|
||||
break;
|
||||
|
||||
case "pending":
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn("ResourceRequest %s has invalid status to call markExecuting.", this.name);
|
||||
return;
|
||||
}
|
||||
|
||||
this.timestampExecuting = performance.now();
|
||||
this.status = "executing";
|
||||
}
|
||||
|
||||
markExecuted(result: ResourceRequestResult) {
|
||||
switch (this.status) {
|
||||
case "unset":
|
||||
/* the markEnqueue() invoke has been skipped */
|
||||
break;
|
||||
|
||||
case "pending":
|
||||
/* the markExecuting() invoke has been skipped */
|
||||
break;
|
||||
|
||||
case "executing":
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn("ResourceRequest %s has invalid status to call markExecuted.", this.name);
|
||||
return;
|
||||
}
|
||||
|
||||
this.result = result;
|
||||
this.timestampExecuted = performance.now();
|
||||
this.status = "executed";
|
||||
}
|
||||
|
||||
generateReportString() {
|
||||
let timeEnqueued, timeExecuted;
|
||||
if(this.timestampEnqueue === 0) {
|
||||
timeEnqueued = "unknown";
|
||||
} else {
|
||||
let endTimestamp = Math.min(this.timestampExecuting, this.timestampExecuted);
|
||||
if (endTimestamp === 0) {
|
||||
timeEnqueued = "pending";
|
||||
} else {
|
||||
timeEnqueued = endTimestamp - this.timestampEnqueue;
|
||||
}
|
||||
}
|
||||
|
||||
if(this.timestampExecuted === 0) {
|
||||
timeExecuted = "unknown";
|
||||
} else {
|
||||
if( this.timestampExecuted === 0) {
|
||||
timeExecuted = "pending";
|
||||
} else {
|
||||
timeExecuted = this.timestampExecuted - this.timestampEnqueue;
|
||||
}
|
||||
}
|
||||
|
||||
return `ResourceRequest{ type: ${this.type}, time enqueued: ${timeEnqueued}, time executed: ${timeExecuted}, name: ${this.name} }`;
|
||||
}
|
||||
}
|
||||
|
||||
export class LoaderPerformanceLogger {
|
||||
private readonly resourceTimings: ResourceRequest[] = [];
|
||||
private eventTimeBase: number;
|
||||
|
||||
constructor() {
|
||||
this.eventTimeBase = performance.now();
|
||||
}
|
||||
|
||||
getResourceTimings() : ResourceRequest[] {
|
||||
return this.resourceTimings;
|
||||
}
|
||||
|
||||
logResourceRequest(type: ResourceType, name: string) : ResourceRequest {
|
||||
const request = new ResourceRequest(type, name);
|
||||
this.resourceTimings.push(request);
|
||||
return request;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
import {config, critical_error, loaderPerformance, SourcePath} from "./loader";
|
||||
import {executeParallelLoad, LoadCallback, LoadSyntaxError, ParallelOptions} from "./Utils";
|
||||
|
||||
export function loadScript(url: SourcePath) : Promise<void> {
|
||||
const givenTimeout = 120 * 1000;
|
||||
|
||||
const resourceRequest = loaderPerformance.logResourceRequest("script", url);
|
||||
resourceRequest.markEnqueue();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const scriptTag = document.createElement("script");
|
||||
scriptTag.type = "application/javascript";
|
||||
scriptTag.async = true;
|
||||
scriptTag.defer = true;
|
||||
|
||||
const cleanup = () => {
|
||||
scriptTag.onerror = undefined;
|
||||
scriptTag.onload = undefined;
|
||||
|
||||
clearTimeout(timeoutHandle);
|
||||
};
|
||||
|
||||
const timeoutHandle = setTimeout(() => {
|
||||
resourceRequest.markExecuted({ status: "timeout", givenTimeout: givenTimeout });
|
||||
cleanup();
|
||||
reject("timeout");
|
||||
}, givenTimeout);
|
||||
|
||||
/* TODO: Test if on syntax error the parameters contain extra info */
|
||||
scriptTag.onerror = () => {
|
||||
resourceRequest.markExecuted({ status: "error-event" });
|
||||
scriptTag.remove();
|
||||
cleanup();
|
||||
reject();
|
||||
};
|
||||
|
||||
scriptTag.onload = () => {
|
||||
resourceRequest.markExecuted({ status: "success" });
|
||||
cleanup();
|
||||
resolve();
|
||||
};
|
||||
|
||||
scriptTag.onloadstart = () => {
|
||||
}
|
||||
|
||||
scriptTag.src = config.baseUrl + url;
|
||||
document.body.appendChild(scriptTag);
|
||||
resourceRequest.markExecuting();
|
||||
});
|
||||
}
|
||||
|
||||
type MultipleOptions = ParallelOptions;
|
||||
export async function loadScripts(paths: SourcePath[], options: MultipleOptions, callback?: LoadCallback<SourcePath>) : Promise<void> {
|
||||
const result = await executeParallelLoad<SourcePath>(paths, e => loadScript(e), e => e, options, callback);
|
||||
if(result.failed.length > 0) {
|
||||
if(config.error) {
|
||||
console.error("Failed to load the following scripts:");
|
||||
for(const script of result.failed) {
|
||||
const sname = script.request;
|
||||
if(script.error instanceof LoadSyntaxError) {
|
||||
const source = script.error.source as Error;
|
||||
if(source.name === "TypeError") {
|
||||
let prefix = "";
|
||||
while(prefix.length < sname.length + 7) prefix += " ";
|
||||
console.log(" - %s: %s:\n%s", sname, source.message, source.stack.split("\n").map(e => prefix + e.trim()).slice(1).join("\n"));
|
||||
} else if(typeof source === "string") {
|
||||
console.log(" - %s: %s", sname, source);
|
||||
} else {
|
||||
console.log(" - %s: %o", sname, source);
|
||||
}
|
||||
} else {
|
||||
console.log(" - %s: %o", sname, script.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let errorMessage;
|
||||
{
|
||||
const error = result.failed[0].error;
|
||||
if(error instanceof LoadSyntaxError) {
|
||||
errorMessage = error.source.message;
|
||||
} else if(typeof error === "string") {
|
||||
errorMessage = error;
|
||||
} else {
|
||||
console.error("Script %s loading error: %o", result.failed[0].request, error);
|
||||
errorMessage = "View the browser console for more information!";
|
||||
}
|
||||
critical_error("Failed to load script " + result.failed[0].request, errorMessage);
|
||||
}
|
||||
throw "failed to load script " + result.failed[0].request + " (" + errorMessage + ")";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import {config, critical_error, loaderPerformance, SourcePath} from "./loader";
|
||||
import {executeParallelLoad, LoadCallback, LoadSyntaxError, ParallelOptions} from "./Utils";
|
||||
|
||||
export function loadStyle(path: SourcePath) : Promise<void> {
|
||||
const givenTimeout = 120 * 1000;
|
||||
|
||||
const resourceRequest = loaderPerformance.logResourceRequest("script", path);
|
||||
resourceRequest.markEnqueue();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const linkTag = document.createElement("link");
|
||||
|
||||
linkTag.type = "text/css";
|
||||
linkTag.rel = "stylesheet";
|
||||
linkTag.href = config.baseUrl + path;
|
||||
|
||||
const cleanup = () => {
|
||||
linkTag.onerror = undefined;
|
||||
linkTag.onload = undefined;
|
||||
|
||||
clearTimeout(timeoutHandle);
|
||||
};
|
||||
|
||||
const errorCleanup = () => {
|
||||
linkTag.remove();
|
||||
cleanup();
|
||||
};
|
||||
|
||||
const timeoutHandle = setTimeout(() => {
|
||||
resourceRequest.markExecuted({ status: "timeout", givenTimeout: givenTimeout });
|
||||
cleanup();
|
||||
reject("timeout");
|
||||
}, givenTimeout);
|
||||
|
||||
/* TODO: Test if on syntax error the parameters contain extra info */
|
||||
linkTag.onerror = () => {
|
||||
resourceRequest.markExecuted({ status: "error-event" });
|
||||
errorCleanup();
|
||||
reject();
|
||||
};
|
||||
|
||||
linkTag.onload = () => {
|
||||
resourceRequest.markExecuted({ status: "success" });
|
||||
cleanup();
|
||||
resolve();
|
||||
};
|
||||
|
||||
document.head.appendChild(linkTag);
|
||||
resourceRequest.markExecuting();
|
||||
});
|
||||
}
|
||||
|
||||
export type MultipleOptions = ParallelOptions;
|
||||
export async function loadStyles(paths: SourcePath[], options: MultipleOptions, callback?: LoadCallback<SourcePath>) : Promise<void> {
|
||||
const result = await executeParallelLoad<SourcePath>(paths, e => loadStyle(e), e => e, options, callback);
|
||||
if(result.failed.length > 0) {
|
||||
if(config.error) {
|
||||
console.error("Failed to load the following style sheets:");
|
||||
for(const style of result.failed) {
|
||||
const sname = style.request;
|
||||
if(style.error instanceof LoadSyntaxError) {
|
||||
console.log(" - %s: %o", sname, style.error.source);
|
||||
} else {
|
||||
console.log(" - %s: %o", sname, style.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
critical_error("Failed to load style <code>" + result.failed[0].request + "</code><br>" + "View the browser console for more information!");
|
||||
throw "failed to load style " + result.failed[0].request;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,8 @@
|
|||
import {SourcePath} from "./loader";
|
||||
import {Options} from "./script_loader";
|
||||
|
||||
export const getUrlParameter = key => {
|
||||
const match = location.search.match(new RegExp("(.*[?&]|^)" + key + "=([^&]+)($|&.*)"));
|
||||
if(!match)
|
||||
if(!match) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return match[2];
|
||||
};
|
||||
|
@ -16,24 +14,15 @@ export class LoadSyntaxError {
|
|||
}
|
||||
}
|
||||
|
||||
export function script_name(path: SourcePath, html: boolean) {
|
||||
if(Array.isArray(path)) {
|
||||
return path.filter(e => !!e).map(e => script_name(e, html)).join(" or ");
|
||||
} else if(typeof(path) === "string")
|
||||
return html ? "<code>" + path + "</code>" : path;
|
||||
else
|
||||
return html ? "<code>" + path.url + "</code>" : path.url;
|
||||
}
|
||||
|
||||
export interface ParallelOptions extends Options {
|
||||
max_parallel_requests?: number
|
||||
export interface ParallelOptions {
|
||||
maxParallelRequests?: number
|
||||
}
|
||||
|
||||
export interface ParallelResult<T> {
|
||||
succeeded: T[];
|
||||
failed: {
|
||||
request: T,
|
||||
error: T
|
||||
error: any
|
||||
}[],
|
||||
|
||||
skipped: T[];
|
||||
|
@ -41,15 +30,22 @@ export interface ParallelResult<T> {
|
|||
|
||||
export type LoadCallback<T> = (entry: T, state: "loading" | "loaded") => void;
|
||||
|
||||
export async function load_parallel<T>(requests: T[], executor: (_: T) => Promise<void>, stringify: (_: T) => string, options: ParallelOptions, callback?: LoadCallback<T>) : Promise<ParallelResult<T>> {
|
||||
export async function executeParallelLoad<T>(
|
||||
requests: T[],
|
||||
executor: (_: T) => Promise<void>,
|
||||
stringify: (_: T) => string,
|
||||
options: ParallelOptions,
|
||||
callback?: LoadCallback<T>
|
||||
) : Promise<ParallelResult<T>> {
|
||||
const result: ParallelResult<T> = { failed: [], succeeded: [], skipped: [] };
|
||||
const pendingRequests = requests.slice(0).reverse(); /* we're only able to pop from the back */
|
||||
const currentRequests = {};
|
||||
|
||||
if(typeof callback === "undefined")
|
||||
if(typeof callback === "undefined") {
|
||||
callback = () => {};
|
||||
}
|
||||
|
||||
const maxParallelRequests = typeof options.max_parallel_requests === "number" && options.max_parallel_requests > 0 ? options.max_parallel_requests : Number.MAX_SAFE_INTEGER;
|
||||
const maxParallelRequests = typeof options.maxParallelRequests === "number" && options.maxParallelRequests > 0 ? options.maxParallelRequests : Number.MAX_SAFE_INTEGER;
|
||||
while (pendingRequests.length > 0) {
|
||||
while(Object.keys(currentRequests).length < maxParallelRequests) {
|
||||
const element = pendingRequests.pop();
|
||||
|
@ -60,18 +56,21 @@ export async function load_parallel<T>(requests: T[], executor: (_: T) => Promis
|
|||
delete currentRequests[name];
|
||||
callback(element, "loaded");
|
||||
});
|
||||
if(pendingRequests.length == 0)
|
||||
|
||||
if(pendingRequests.length == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait 'till a new "slot" for downloading is free.
|
||||
* This should also not throw because any errors will be caught before.
|
||||
*/
|
||||
await Promise.race(Object.keys(currentRequests).map(e => currentRequests[e]));
|
||||
if(result.failed.length > 0)
|
||||
if(result.failed.length > 0) {
|
||||
break; /* finish loading the other requests and than show the error */
|
||||
}
|
||||
}
|
||||
await Promise.all(Object.keys(currentRequests).map(e => currentRequests[e]));
|
||||
result.skipped.push(...pendingRequests);
|
||||
return result;
|
|
@ -1,7 +1,6 @@
|
|||
import * as script_loader from "./script_loader";
|
||||
import * as template_loader from "./template_loader";
|
||||
import * as Animation from "../animation";
|
||||
import {getUrlParameter} from "./utils";
|
||||
import {getUrlParameter} from "./Utils";
|
||||
import {LoaderPerformanceLogger} from "./Performance";
|
||||
|
||||
export interface ApplicationLoader {
|
||||
execute();
|
||||
|
@ -76,30 +75,9 @@ export enum Stage {
|
|||
DONE
|
||||
}
|
||||
|
||||
let cache_tag: string | undefined;
|
||||
let currentStage: Stage = undefined;
|
||||
const tasks: {[key:number]: InternalTask[]} = {};
|
||||
|
||||
/* test if all files shall be load from cache or fetch again */
|
||||
function loader_cache_tag() {
|
||||
if(__build.mode === "debug") {
|
||||
cache_tag = "?_ts=" + Date.now();
|
||||
return;
|
||||
}
|
||||
|
||||
const cached_version = localStorage.getItem("cached_version");
|
||||
if(!cached_version || cached_version !== __build.version) {
|
||||
register_task(Stage.LOADED, {
|
||||
priority: 0,
|
||||
name: "cached version updater",
|
||||
function: async () => {
|
||||
localStorage.setItem("cached_version", __build.version);
|
||||
}
|
||||
});
|
||||
}
|
||||
cache_tag = "?_version=" + __build.version;
|
||||
}
|
||||
|
||||
export type ModuleMapping = {
|
||||
application: string,
|
||||
modules: {
|
||||
|
@ -111,8 +89,6 @@ export type ModuleMapping = {
|
|||
const module_mapping_: ModuleMapping[] = [];
|
||||
export function module_mapping() : ModuleMapping[] { return module_mapping_; }
|
||||
|
||||
export function get_cache_version() { return cache_tag; }
|
||||
|
||||
export function finished() {
|
||||
return currentStage == Stage.DONE;
|
||||
}
|
||||
|
@ -176,22 +152,28 @@ export function setCurrentTaskName(taskId: number, name: string) {
|
|||
}
|
||||
|
||||
export async function execute(customLoadingAnimations: boolean) {
|
||||
if(!await Animation.initialize(customLoadingAnimations))
|
||||
if(!await Animation.initialize(customLoadingAnimations)) {
|
||||
return;
|
||||
}
|
||||
|
||||
loader_cache_tag();
|
||||
/* Cleanup <noscript> elements */
|
||||
for(const element of document.getElementsByTagName("noscript")) {
|
||||
element.remove();
|
||||
}
|
||||
|
||||
const load_begin = Date.now();
|
||||
const timestampBegin = Date.now();
|
||||
|
||||
let begin: number = 0;
|
||||
let end: number = Date.now();
|
||||
while(currentStage <= Stage.LOADED || typeof(currentStage) === "undefined") {
|
||||
while(currentStage <= Stage.LOADED || typeof currentStage === "undefined") {
|
||||
|
||||
let pendingTasks: InternalTask[] = [];
|
||||
while((tasks[currentStage] || []).length > 0) {
|
||||
if(pendingTasks.length == 0 || pendingTasks[0].priority == tasks[currentStage][0].priority) {
|
||||
pendingTasks.push(tasks[currentStage].pop());
|
||||
} else break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const errors: {
|
||||
|
@ -284,7 +266,7 @@ export async function execute(customLoadingAnimations: boolean) {
|
|||
}
|
||||
|
||||
if(config.verbose)
|
||||
console.debug("[loader] finished loader. (Total time: %dms)", Date.now() - load_begin);
|
||||
console.debug("[loader] finished loader. (Total time: %dms)", Date.now() - timestampBegin);
|
||||
|
||||
Animation.finalize(config.abortAnimationOnFinish);
|
||||
}
|
||||
|
@ -353,79 +335,6 @@ export function critical_error_handler(handler?: ErrorHandler, override?: boolea
|
|||
}
|
||||
|
||||
/* loaders */
|
||||
export type DependSource = {
|
||||
url: string;
|
||||
depends: string[];
|
||||
}
|
||||
export type SourcePath = string | DependSource | string[];
|
||||
export type SourcePath = string;
|
||||
|
||||
export const scripts = script_loader;
|
||||
export const templates = template_loader;
|
||||
|
||||
/* Hello World message */
|
||||
{
|
||||
const clog = console.log;
|
||||
const print_security = () => {
|
||||
{
|
||||
const css = [
|
||||
"display: block",
|
||||
"text-align: center",
|
||||
"font-size: 42px",
|
||||
"font-weight: bold",
|
||||
"-webkit-text-stroke: 2px black",
|
||||
"color: red"
|
||||
].join(";");
|
||||
clog("%c ", "font-size: 100px;");
|
||||
clog("%cSecurity warning:", css);
|
||||
}
|
||||
{
|
||||
const css = [
|
||||
"display: block",
|
||||
"text-align: center",
|
||||
"font-size: 18px",
|
||||
"font-weight: bold"
|
||||
].join(";");
|
||||
|
||||
clog("%cPasting anything in here could give attackers access to your data.", css);
|
||||
clog("%cUnless you understand exactly what you are doing, close this window and stay safe.", css);
|
||||
clog("%c ", "font-size: 100px;");
|
||||
}
|
||||
};
|
||||
|
||||
/* 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(";");
|
||||
clog("%cHey, hold on!", css);
|
||||
}
|
||||
{
|
||||
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 ""; };
|
||||
|
||||
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);
|
||||
clog("%cyou'll be may interested to share them here: %chttps://github.com/TeaSpeak/TeaWeb", css, css_2);
|
||||
clog("%c ", display_detect);
|
||||
}
|
||||
}
|
||||
export let loaderPerformance = new LoaderPerformanceLogger();
|
|
@ -1,136 +0,0 @@
|
|||
import {config, critical_error, SourcePath} from "./loader";
|
||||
import {load_parallel, LoadCallback, LoadSyntaxError, ParallelOptions, script_name} from "./utils";
|
||||
|
||||
let _script_promises: {[key: string]: Promise<void>} = {};
|
||||
|
||||
function load_script_url(url: string) : Promise<void> {
|
||||
if(typeof _script_promises[url] === "object")
|
||||
return _script_promises[url];
|
||||
|
||||
return (_script_promises[url] = new Promise((resolve, reject) => {
|
||||
const script_tag: HTMLScriptElement = document.createElement("script");
|
||||
|
||||
let error = false;
|
||||
const error_handler = (event: ErrorEvent) => {
|
||||
if(event.filename == script_tag.src && event.message.indexOf("Illegal constructor") == -1) { //Our tag throw an uncaught error
|
||||
if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error);
|
||||
window.removeEventListener('error', error_handler as any);
|
||||
|
||||
reject(new LoadSyntaxError(event.error));
|
||||
event.preventDefault();
|
||||
error = true;
|
||||
}
|
||||
};
|
||||
window.addEventListener('error', error_handler as any);
|
||||
|
||||
const cleanup = () => {
|
||||
script_tag.onerror = undefined;
|
||||
script_tag.onload = undefined;
|
||||
|
||||
clearTimeout(timeout_handle);
|
||||
window.removeEventListener('error', error_handler as any);
|
||||
};
|
||||
const timeout_handle = setTimeout(() => {
|
||||
cleanup();
|
||||
reject("timeout");
|
||||
}, 120 * 1000);
|
||||
script_tag.type = "application/javascript";
|
||||
script_tag.async = true;
|
||||
script_tag.defer = true;
|
||||
script_tag.onerror = error => {
|
||||
cleanup();
|
||||
script_tag.remove();
|
||||
reject(error);
|
||||
};
|
||||
script_tag.onload = () => {
|
||||
cleanup();
|
||||
|
||||
if(config.verbose) console.debug("Script %o loaded", url);
|
||||
setTimeout(resolve, 100);
|
||||
};
|
||||
|
||||
document.getElementById("scripts").appendChild(script_tag);
|
||||
|
||||
script_tag.src = config.baseUrl + url;
|
||||
})).then(() => {
|
||||
/* cleanup memory */
|
||||
_script_promises[url] = Promise.resolve(); /* this promise does not holds the whole script tag and other memory */
|
||||
return _script_promises[url];
|
||||
}).catch(error => {
|
||||
/* cleanup memory */
|
||||
_script_promises[url] = Promise.reject(error); /* this promise does not holds the whole script tag and other memory */
|
||||
return _script_promises[url];
|
||||
});
|
||||
}
|
||||
|
||||
export interface Options {
|
||||
cache_tag?: string;
|
||||
}
|
||||
|
||||
export async function load(path: SourcePath, options: Options) : Promise<void> {
|
||||
if(Array.isArray(path)) { //We have fallback scripts
|
||||
return load(path[0], options).catch(error => {
|
||||
if(error instanceof LoadSyntaxError)
|
||||
return Promise.reject(error);
|
||||
|
||||
if(path.length > 1)
|
||||
return load(path.slice(1), options);
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
} else {
|
||||
const source = typeof(path) === "string" ? {url: path, depends: []} : path;
|
||||
if(source.url.length == 0) return Promise.resolve();
|
||||
|
||||
/* await depends */
|
||||
for(const depend of source.depends) {
|
||||
if(!_script_promises[depend])
|
||||
throw "Missing dependency " + depend;
|
||||
await _script_promises[depend];
|
||||
}
|
||||
|
||||
await load_script_url(source.url + (options.cache_tag || ""));
|
||||
}
|
||||
}
|
||||
|
||||
type MultipleOptions = Options | ParallelOptions;
|
||||
export async function load_multiple(paths: SourcePath[], options: MultipleOptions, callback?: LoadCallback<SourcePath>) : Promise<void> {
|
||||
const result = await load_parallel<SourcePath>(paths, e => load(e, options), e => script_name(e, false), options, callback);
|
||||
if(result.failed.length > 0) {
|
||||
if(config.error) {
|
||||
console.error("Failed to load the following scripts:");
|
||||
for(const script of result.failed) {
|
||||
const sname = script_name(script.request, false);
|
||||
if(script.error instanceof LoadSyntaxError) {
|
||||
const source = script.error.source as Error;
|
||||
if(source.name === "TypeError") {
|
||||
let prefix = "";
|
||||
while(prefix.length < sname.length + 7) prefix += " ";
|
||||
console.log(" - %s: %s:\n%s", sname, source.message, source.stack.split("\n").map(e => prefix + e.trim()).slice(1).join("\n"));
|
||||
} else if(typeof source === "string") {
|
||||
console.log(" - %s: %s", sname, source);
|
||||
} else {
|
||||
console.log(" - %s: %o", sname, source);
|
||||
}
|
||||
} else {
|
||||
console.log(" - %s: %o", sname, script.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let errorMessage;
|
||||
{
|
||||
const error = result.failed[0].error;
|
||||
if(error instanceof LoadSyntaxError) {
|
||||
errorMessage = error.source.message;
|
||||
} else if(typeof error === "string") {
|
||||
errorMessage = error;
|
||||
} else {
|
||||
console.error("Script %s loading error: %o", script_name(result.failed[0].request, false), error);
|
||||
errorMessage = "View the browser console for more information!";
|
||||
}
|
||||
critical_error("Failed to load script " + script_name(result.failed[0].request, true), errorMessage);
|
||||
}
|
||||
throw "failed to load script " + script_name(result.failed[0].request, false) + " (" + errorMessage + ")";
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
import {config, critical_error, SourcePath} from "./loader";
|
||||
import {load_parallel, LoadCallback, LoadSyntaxError, ParallelOptions, script_name} from "./utils";
|
||||
|
||||
let _template_promises: {[key: string]: Promise<void>} = {};
|
||||
|
||||
function load_template_url(url: string) : Promise<void> {
|
||||
if(typeof _template_promises[url] === "object")
|
||||
return _template_promises[url];
|
||||
|
||||
return (_template_promises[url] = (async () => {
|
||||
const response = await (await fetch(config.baseUrl + url)).text();
|
||||
|
||||
let node = document.createElement("html");
|
||||
node.innerHTML = response;
|
||||
let tags: HTMLCollection;
|
||||
if(node.getElementsByTagName("body").length > 0)
|
||||
tags = node.getElementsByTagName("body")[0].children;
|
||||
else
|
||||
tags = node.children;
|
||||
|
||||
let root = document.getElementById("templates");
|
||||
if(!root) {
|
||||
critical_error("Failed to find template tag!");
|
||||
throw "Failed to find template tag";
|
||||
}
|
||||
while(tags.length > 0){
|
||||
let tag = tags.item(0);
|
||||
root.appendChild(tag);
|
||||
|
||||
}
|
||||
})()).then(result => {
|
||||
/* cleanup memory */
|
||||
_template_promises[url] = Promise.resolve(); /* this promise does not holds the whole script tag and other memory */
|
||||
return _template_promises[url];
|
||||
}).catch(error => {
|
||||
/* cleanup memory */
|
||||
_template_promises[url] = Promise.reject(error); /* this promise does not holds the whole script tag and other memory */
|
||||
return _template_promises[url];
|
||||
});
|
||||
}
|
||||
|
||||
export interface Options {
|
||||
cache_tag?: string;
|
||||
}
|
||||
|
||||
export async function load(path: SourcePath, options: Options) : Promise<void> {
|
||||
if(Array.isArray(path)) { //We have fallback scripts
|
||||
return load(path[0], options).catch(error => {
|
||||
if(error instanceof LoadSyntaxError)
|
||||
return Promise.reject(error);
|
||||
|
||||
if(path.length > 1)
|
||||
return load(path.slice(1), options);
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
} else {
|
||||
const source = typeof(path) === "string" ? {url: path, depends: []} : path;
|
||||
if(source.url.length == 0) return Promise.resolve();
|
||||
|
||||
/* await depends */
|
||||
for(const depend of source.depends) {
|
||||
if(!_template_promises[depend])
|
||||
throw "Missing dependency " + depend;
|
||||
await _template_promises[depend];
|
||||
}
|
||||
await load_template_url(source.url + (options.cache_tag || ""));
|
||||
}
|
||||
}
|
||||
|
||||
export type MultipleOptions = Options | ParallelOptions;
|
||||
export async function load_multiple(paths: SourcePath[], options: MultipleOptions, callback?: LoadCallback<SourcePath>) : Promise<void> {
|
||||
const result = await load_parallel<SourcePath>(paths, e => load(e, options), e => script_name(e, false), options, callback);
|
||||
if(result.failed.length > 0) {
|
||||
if(config.error) {
|
||||
console.error("Failed to load the following template files:");
|
||||
for(const style of result.failed) {
|
||||
const sname = script_name(style.request, false);
|
||||
if(style.error instanceof LoadSyntaxError) {
|
||||
console.log(" - %s: %o", sname, style.error.source);
|
||||
} else {
|
||||
console.log(" - %s: %o", sname, style.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
critical_error("Failed to load template file " + script_name(result.failed[0].request, true) + " <br>" + "View the browser console for more information!");
|
||||
throw "failed to load template file " + script_name(result.failed[0].request, false);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
import * as loader from "./loader/loader";
|
||||
import {config} from "./loader/loader";
|
||||
import {script_name} from "./loader/utils";
|
||||
import {config, loaderPerformance} from "./loader/loader";
|
||||
import {loadStyles} from "./loader/StyleLoader";
|
||||
import {loadScripts} from "./loader/ScriptLoader";
|
||||
|
||||
export interface TeaManifest {
|
||||
export interface ApplicationManifest {
|
||||
version: number;
|
||||
|
||||
chunks: {
|
||||
|
@ -11,6 +12,10 @@ export interface TeaManifest {
|
|||
hash: string,
|
||||
file: string
|
||||
}[],
|
||||
css_files: {
|
||||
hash: string,
|
||||
file: string
|
||||
}[],
|
||||
modules: {
|
||||
id: string,
|
||||
context: string,
|
||||
|
@ -20,24 +25,33 @@ export interface TeaManifest {
|
|||
};
|
||||
}
|
||||
|
||||
let manifest: TeaManifest;
|
||||
export async function loadManifest() : Promise<TeaManifest> {
|
||||
let manifest: ApplicationManifest;
|
||||
export async function loadManifest() : Promise<ApplicationManifest> {
|
||||
if(manifest) {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
const requestResource = loaderPerformance.logResourceRequest("json", "manifest.json");
|
||||
try {
|
||||
const response = await fetch(config.baseUrl + "js/manifest.json?_date=" + Date.now());
|
||||
if(!response.ok) throw response.status + " " + response.statusText;
|
||||
requestResource.markExecuting();
|
||||
const response = await fetch(config.baseUrl + "/manifest.json?_date=" + Date.now());
|
||||
if(!response.ok) {
|
||||
requestResource.markExecuted({ status: "unknown-error", message: response.status + " " + response.statusText });
|
||||
throw response.status + " " + response.statusText;
|
||||
}
|
||||
|
||||
manifest = await response.json();
|
||||
requestResource.markExecuted({ status: "success" });
|
||||
} catch(error) {
|
||||
requestResource.markExecuted({ status: "error-event" });
|
||||
console.error("Failed to load javascript manifest: %o", error);
|
||||
loader.critical_error("Failed to load manifest.json", error);
|
||||
throw "failed to load manifest.json";
|
||||
}
|
||||
if(manifest.version !== 2)
|
||||
|
||||
if(manifest.version !== 2) {
|
||||
throw "invalid manifest version";
|
||||
}
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
@ -47,18 +61,30 @@ export async function loadManifestTarget(chunkName: string, taskId: number) {
|
|||
loader.critical_error("Missing entry chunk in manifest.json", "Chunk " + chunkName + " is missing.");
|
||||
throw "missing entry chunk";
|
||||
}
|
||||
|
||||
loader.module_mapping().push({
|
||||
application: chunkName,
|
||||
modules: manifest.chunks[chunkName].modules
|
||||
});
|
||||
|
||||
await loader.scripts.load_multiple(manifest.chunks[chunkName].files.map(e => "js/" + e.file), {
|
||||
cache_tag: undefined,
|
||||
max_parallel_requests: 4
|
||||
}, (script, state) => {
|
||||
if(state !== "loading")
|
||||
const kMaxRequests = 4;
|
||||
await loadStyles(manifest.chunks[chunkName].css_files.map(e => e.file), {
|
||||
maxParallelRequests: kMaxRequests
|
||||
}, (entry, state) => {
|
||||
if (state !== "loading") {
|
||||
return;
|
||||
}
|
||||
|
||||
loader.setCurrentTaskName(taskId, script_name(script, false));
|
||||
loader.setCurrentTaskName(taskId, entry);
|
||||
});
|
||||
|
||||
await loadScripts(manifest.chunks[chunkName].files.map(e => e.file), {
|
||||
maxParallelRequests: kMaxRequests
|
||||
}, (script, state) => {
|
||||
if(state !== "loading") {
|
||||
return;
|
||||
}
|
||||
|
||||
loader.setCurrentTaskName(taskId, script);
|
||||
});
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/* IE11 and safari */
|
||||
if(Element.prototype.remove === undefined)
|
||||
if(Element.prototype.remove === undefined) {
|
||||
Object.defineProperty(Element.prototype, "remove", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
|
@ -8,6 +8,7 @@ if(Element.prototype.remove === undefined)
|
|||
this.parentElement.removeChild(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* IE11 */
|
||||
function ReplaceWithPolyfill() {
|
||||
|
@ -28,14 +29,17 @@ function ReplaceWithPolyfill() {
|
|||
parent.insertBefore(currentNode, this.nextSibling);
|
||||
}
|
||||
}
|
||||
if (!Element.prototype.replaceWith)
|
||||
if (!Element.prototype.replaceWith) {
|
||||
Element.prototype.replaceWith = ReplaceWithPolyfill;
|
||||
}
|
||||
|
||||
if (!CharacterData.prototype.replaceWith)
|
||||
if (!CharacterData.prototype.replaceWith) {
|
||||
CharacterData.prototype.replaceWith = ReplaceWithPolyfill;
|
||||
}
|
||||
|
||||
if (!DocumentType.prototype.replaceWith)
|
||||
if (!DocumentType.prototype.replaceWith) {
|
||||
DocumentType.prototype.replaceWith = ReplaceWithPolyfill;
|
||||
}
|
||||
|
||||
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/append()/append().md
|
||||
(function (arr) {
|
||||
|
|
|
@ -1,62 +1,15 @@
|
|||
import "./shared";
|
||||
import * as loader from "../loader/loader";
|
||||
import {ApplicationLoader, SourcePath} from "../loader/loader";
|
||||
import {script_name} from "../loader/utils";
|
||||
import {ApplicationLoader} from "../loader/loader";
|
||||
import {loadManifest, loadManifestTarget} from "../maifest";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
native_client: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
function getCacheTag() {
|
||||
return "?_ts=" + (__build.mode === "debug" ? Date.now() : __build.timestamp);
|
||||
}
|
||||
|
||||
const LoaderTaskCallback = taskId => (script: SourcePath, state) => {
|
||||
if(state !== "loading")
|
||||
return;
|
||||
|
||||
loader.setCurrentTaskName(taskId, script_name(script, false));
|
||||
};
|
||||
|
||||
/* all javascript loaders */
|
||||
const loader_javascript = {
|
||||
load_scripts: async taskId => {
|
||||
loader.setCurrentTaskName(taskId, "manifest");
|
||||
await loadManifest();
|
||||
await loadManifestTarget(__build.entry_chunk_name, taskId);
|
||||
}
|
||||
};
|
||||
|
||||
const loader_webassembly = {
|
||||
test_webassembly: async () => {
|
||||
/* We dont required WebAssembly anymore for fundamental functions, only for auto decoding
|
||||
if(typeof (WebAssembly) === "undefined" || typeof (WebAssembly.compile) === "undefined") {
|
||||
console.log(navigator.browserSpecs);
|
||||
if (navigator.browserSpecs.name == 'Safari') {
|
||||
if (parseInt(navigator.browserSpecs.version) < 11) {
|
||||
displayCriticalError("You require Safari 11 or higher to use the web client!<br>Safari " + navigator.browserSpecs.version + " does not support WebAssambly!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Do something for all other browsers.
|
||||
}
|
||||
displayCriticalError("You require WebAssembly for TeaSpeak-Web!");
|
||||
throw "Missing web assembly";
|
||||
}
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
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(typeof isSecureContext === "undefined")
|
||||
(<any>window)["isSecureContext"] = location.protocol !== 'https:' || location.hostname === 'localhost';
|
||||
if(typeof isSecureContext === "undefined") {
|
||||
(window as any)["isSecureContext"] = location.protocol !== 'https:' || location.hostname === 'localhost';
|
||||
}
|
||||
|
||||
if(!isSecureContext) {
|
||||
loader.critical_error("TeaWeb cant run on unsecured sides.", "App requires to be loaded via HTTPS!");
|
||||
|
@ -66,29 +19,12 @@ loader.register_task(loader.Stage.INITIALIZING, {
|
|||
priority: 20
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.INITIALIZING, {
|
||||
name: "webassembly tester",
|
||||
function: loader_webassembly.test_webassembly,
|
||||
priority: 20
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "scripts",
|
||||
function: loader_javascript.load_scripts,
|
||||
priority: 10
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.TEMPLATES, {
|
||||
name: "templates",
|
||||
name: "manifest",
|
||||
function: async taskId => {
|
||||
await loader.templates.load_multiple([
|
||||
"templates.html",
|
||||
"templates/modal/musicmanage.html",
|
||||
"templates/modal/newcomer.html",
|
||||
], {
|
||||
cache_tag: getCacheTag(),
|
||||
max_parallel_requests: -1
|
||||
}, LoaderTaskCallback(taskId));
|
||||
loader.setCurrentTaskName(taskId, "manifest");
|
||||
await loadManifest();
|
||||
await loadManifestTarget(__build.entry_chunk_name, taskId);
|
||||
},
|
||||
priority: 10
|
||||
});
|
||||
|
|
|
@ -11,9 +11,9 @@ export default class implements ApplicationLoader {
|
|||
console.log("Doing nothing");
|
||||
|
||||
for(let index of [1, 2, 3]) {
|
||||
await new Promise(resolve => {
|
||||
await new Promise<void>(resolve => {
|
||||
const callback = () => {
|
||||
document.removeEventListener("click", resolve);
|
||||
document.removeEventListener("click", callback);
|
||||
resolve();
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import "./shared";
|
|||
import * as loader from "../loader/loader";
|
||||
import {ApplicationLoader, Stage} from "../loader/loader";
|
||||
import {loadManifest, loadManifestTarget} from "../maifest";
|
||||
import {getUrlParameter} from "../loader/utils";
|
||||
import {getUrlParameter} from "../loader/Utils";
|
||||
|
||||
export default class implements ApplicationLoader {
|
||||
execute() {
|
||||
|
@ -10,7 +10,7 @@ export default class implements ApplicationLoader {
|
|||
function: async taskId => {
|
||||
await loadManifest();
|
||||
|
||||
const entryChunk = getUrlParameter("chunk");
|
||||
const entryChunk = getUrlParameter("loader-chunk");
|
||||
if(!entryChunk) {
|
||||
loader.critical_error("Missing entry chunk parameter");
|
||||
throw "Missing entry chunk parameter";
|
||||
|
@ -45,19 +45,6 @@ export default class implements ApplicationLoader {
|
|||
priority: 10
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.TEMPLATES, {
|
||||
name: "templates",
|
||||
function: async () => {
|
||||
await loader.templates.load_multiple([
|
||||
"templates.html"
|
||||
], {
|
||||
cache_tag: "?22",
|
||||
max_parallel_requests: -1
|
||||
});
|
||||
},
|
||||
priority: 10
|
||||
});
|
||||
|
||||
loader.execute_managed(false);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import * as loader from "../loader/loader";
|
||||
import {Stage} from "../loader/loader";
|
||||
import {BrowserInfo, detect as detectBrowser,} from "detect-browser";
|
||||
import {detect as detectBrowser} from "detect-browser";
|
||||
|
||||
loader.register_task(Stage.SETUP, {
|
||||
name: "app init",
|
||||
|
@ -13,14 +13,11 @@ loader.register_task(Stage.SETUP, {
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -50,7 +47,6 @@ loader.register_task(Stage.SETUP, {
|
|||
case "ie":
|
||||
loader.critical_error("Browser not supported", "We're sorry, but your browser isn't supported.");
|
||||
throw "unsupported browser";
|
||||
|
||||
}
|
||||
},
|
||||
priority: 50
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
**/*.css
|
||||
**/*.css.map
|
|
@ -1,216 +0,0 @@
|
|||
$setup-time-normal: 80s / 24; /* 24 frames / sec; the initial sequence is 80 frames */
|
||||
$setup-time-halloween: 323s / 24;
|
||||
$loop-time-halloween: 25s / 24;
|
||||
|
||||
#loader-overlay {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
background: #1e1e1e;
|
||||
|
||||
user-select: none;
|
||||
|
||||
z-index: 10000000;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
-webkit-app-region: drag;
|
||||
|
||||
.container {
|
||||
flex-shrink: 0;
|
||||
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
width: 1000px;
|
||||
height: 1000px;
|
||||
|
||||
align-self: center;
|
||||
margin-bottom: 10vh;
|
||||
|
||||
transition-duration: .5s;
|
||||
|
||||
img {
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.setup, .idle {
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
display: none;
|
||||
|
||||
&.visible {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.setup.visible {
|
||||
&.normal {
|
||||
animation: loader-initial-sequence 0s cubic-bezier(.81,.01,.65,1.16) $setup-time-normal forwards;
|
||||
}
|
||||
|
||||
&.halloween {
|
||||
animation: loader-initial-sequence 0s cubic-bezier(.81,.01,.65,1.16) $setup-time-halloween forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.idle.animation-normal {
|
||||
img {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.steam {
|
||||
position: absolute;
|
||||
|
||||
top: 282px;
|
||||
left: 380px;
|
||||
|
||||
width: 249px;
|
||||
height: 125px;
|
||||
background: url("img/loader/steam.png") 0 0, url("../img/loader/steam.png") 0 0;
|
||||
|
||||
animation: sprite-steam 2.5s steps(50) forwards infinite;
|
||||
}
|
||||
}
|
||||
|
||||
&.finishing {
|
||||
.idle {
|
||||
.steam {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bowl {
|
||||
animation: swipe-out-bowl .5s both;
|
||||
}
|
||||
|
||||
.text {
|
||||
animation: swipe-out-text .5s .12s both;
|
||||
}
|
||||
}
|
||||
|
||||
pointer-events: none;
|
||||
animation: overlay-fade .3s .2s both;
|
||||
}
|
||||
|
||||
.loader-stage {
|
||||
position: absolute;
|
||||
|
||||
left: 5px;
|
||||
bottom: 5px;
|
||||
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 850px) {
|
||||
#loader-overlay .container {
|
||||
transform: scale(.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-height: 700px) {
|
||||
#loader-overlay .container {
|
||||
transform: scale(.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 400px) {
|
||||
#loader-overlay .container {
|
||||
transform: scale(.3);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loader-initial-sequence {
|
||||
to {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sprite-steam {
|
||||
to {
|
||||
background-position: 0 -6250px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes swipe-out-bowl {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 1;
|
||||
transform: translate3d(-60px, 0, 0) skew(-5deg, 0) rotateY(-6deg);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate3d(700px, 0, 0) skew(30deg, 0) rotateZ(-6deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes swipe-out-text {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 1;
|
||||
transform: translate3d(-30px, 20px, 0) skew(-5deg, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate3d(550px, 0, 0) skew(30deg, 0) scale(.96, 1.25) rotateZ(6deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animation-nothing {
|
||||
to {
|
||||
background-position: 0 -6250px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes overlay-fade {
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Automated loader timeout */
|
||||
#loader-overlay:not(.initialized) + #critical-load:not(.shown) {
|
||||
display: block !important;
|
||||
opacity: 0;
|
||||
|
||||
animation: loader-setup-timeout 0s ease-in $setup-time-normal forwards;
|
||||
|
||||
.error::before {
|
||||
content: 'Failed to startup loader!';
|
||||
}
|
||||
|
||||
.detail::before {
|
||||
content: 'Lookup the console for more details';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes loader-setup-timeout {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
#overlay-no-js, #critical-load {
|
||||
z-index: 100000000;
|
||||
display: none;
|
||||
position: fixed;
|
||||
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
background: #1e1e1e;
|
||||
text-align: center;
|
||||
|
||||
-webkit-app-region: drag;
|
||||
|
||||
h1, h3, a {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
top: 20%;
|
||||
}
|
||||
|
||||
&.shown {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
#critical-load {
|
||||
img {
|
||||
height: 12em
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #bd1515;
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
.detail {
|
||||
color: #696363;
|
||||
margin-top: .5em
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (max-height: 750px) {
|
||||
#critical-load .container {
|
||||
top: unset;
|
||||
}
|
||||
|
||||
#critical-load {
|
||||
font-size: .8rem;
|
||||
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#critical-load.shown {
|
||||
display: flex;
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ export let config: Config;
|
|||
export type Task = {
|
||||
name: string,
|
||||
priority: number, /* tasks with the same priority will be executed in sync */
|
||||
function: () => Promise<void>
|
||||
function: (taskId: number) => Promise<void>
|
||||
};
|
||||
export enum Stage {
|
||||
/*
|
||||
|
@ -87,3 +87,4 @@ export type ErrorHandler = (message: string, detail: string) => void;
|
|||
export function critical_error(message: string, detail?: string);
|
||||
export function critical_error_handler(handler?: ErrorHandler, override?: boolean);
|
||||
export function hide_overlay();
|
||||
export function setCurrentTaskName(taskId: number, name: string);
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 3.0 MiB |
Before Width: | Height: | Size: 472 KiB After Width: | Height: | Size: 472 KiB |
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 4.4 MiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M6.5,41v15c0,1.009,1.22,2,2.463,2h40.074c1.243,0,2.463-0.991,2.463-2V41H6.5z M27.021,51.566
|
||||
c0,0.474-0.087,0.873-0.26,1.196c-0.174,0.323-0.406,0.583-0.697,0.779c-0.292,0.196-0.627,0.333-1.005,0.41
|
||||
s-0.769,0.116-1.169,0.116c-0.201,0-0.436-0.021-0.704-0.062s-0.547-0.104-0.834-0.191s-0.563-0.185-0.827-0.294
|
||||
c-0.265-0.109-0.488-0.232-0.67-0.369l0.697-1.107c0.091,0.063,0.221,0.13,0.39,0.198s0.353,0.132,0.554,0.191
|
||||
c0.2,0.06,0.41,0.111,0.629,0.157s0.424,0.068,0.615,0.068c0.482,0,0.868-0.094,1.155-0.28s0.439-0.504,0.458-0.95v-7.711h1.668
|
||||
V51.566z M34.958,52.298c-0.15,0.342-0.362,0.643-0.636,0.902s-0.611,0.467-1.012,0.622c-0.401,0.155-0.857,0.232-1.367,0.232
|
||||
c-0.219,0-0.444-0.012-0.677-0.034s-0.468-0.062-0.704-0.116c-0.237-0.055-0.463-0.13-0.677-0.226s-0.399-0.212-0.554-0.349
|
||||
l0.287-1.176c0.127,0.073,0.289,0.144,0.485,0.212s0.398,0.132,0.608,0.191c0.209,0.06,0.419,0.107,0.629,0.144
|
||||
c0.209,0.036,0.405,0.055,0.588,0.055c0.556,0,0.982-0.13,1.278-0.39s0.444-0.645,0.444-1.155c0-0.31-0.105-0.574-0.314-0.793
|
||||
c-0.21-0.219-0.472-0.417-0.786-0.595s-0.654-0.355-1.019-0.533c-0.365-0.178-0.707-0.388-1.025-0.629
|
||||
c-0.319-0.241-0.584-0.526-0.793-0.854c-0.21-0.328-0.314-0.738-0.314-1.23c0-0.446,0.082-0.843,0.246-1.189
|
||||
s0.385-0.641,0.663-0.882s0.602-0.426,0.971-0.554s0.759-0.191,1.169-0.191c0.419,0,0.843,0.039,1.271,0.116
|
||||
c0.428,0.077,0.774,0.203,1.039,0.376c-0.055,0.118-0.119,0.248-0.191,0.39c-0.073,0.142-0.142,0.273-0.205,0.396
|
||||
c-0.064,0.123-0.119,0.226-0.164,0.308c-0.046,0.082-0.073,0.128-0.082,0.137c-0.055-0.027-0.116-0.063-0.185-0.109
|
||||
s-0.167-0.091-0.294-0.137c-0.128-0.046-0.297-0.077-0.506-0.096c-0.21-0.019-0.479-0.014-0.807,0.014
|
||||
c-0.183,0.019-0.355,0.07-0.52,0.157s-0.311,0.193-0.438,0.321c-0.128,0.128-0.229,0.271-0.301,0.431
|
||||
c-0.073,0.159-0.109,0.313-0.109,0.458c0,0.364,0.104,0.658,0.314,0.882c0.209,0.224,0.469,0.419,0.779,0.588
|
||||
c0.31,0.169,0.646,0.333,1.012,0.492c0.364,0.159,0.704,0.354,1.019,0.581s0.576,0.513,0.786,0.854
|
||||
c0.209,0.342,0.314,0.781,0.314,1.319C35.184,51.603,35.108,51.956,34.958,52.298z"/>
|
||||
<path d="M51.5,39V13.978c0-0.766-0.092-1.333-0.55-1.792L39.313,0.55C38.964,0.201,38.48,0,37.985,0H8.963
|
||||
C7.777,0,6.5,0.916,6.5,2.926V39H51.5z M29.5,32c0,0.552-0.447,1-1,1s-1-0.448-1-1v-3c0-0.552,0.447-1,1-1s1,0.448,1,1V32z
|
||||
M37.5,3.391c0-0.458,0.553-0.687,0.877-0.363l10.095,10.095C48.796,13.447,48.567,14,48.109,14H37.5V3.391z M36.5,23v-4
|
||||
c0-0.551-0.448-1-1-1c-0.553,0-1-0.448-1-1s0.447-1,1-1c1.654,0,3,1.346,3,3v4c0,1.103,0.897,2,2,2c0.553,0,1,0.448,1,1
|
||||
s-0.447,1-1,1c-1.103,0-2,0.897-2,2v4c0,1.654-1.346,3-3,3c-0.553,0-1-0.448-1-1s0.447-1,1-1c0.552,0,1-0.449,1-1v-4
|
||||
c0-1.2,0.542-2.266,1.382-3C37.042,25.266,36.5,24.2,36.5,23z M28.5,21c0.828,0,1.5,0.672,1.5,1.5S29.328,24,28.5,24
|
||||
S27,23.328,27,22.5S27.672,21,28.5,21z M16.5,25c1.103,0,2-0.897,2-2v-4c0-1.654,1.346-3,3-3c0.553,0,1,0.448,1,1s-0.447,1-1,1
|
||||
c-0.552,0-1,0.449-1,1v4c0,1.2-0.542,2.266-1.382,3c0.84,0.734,1.382,1.8,1.382,3v4c0,0.551,0.448,1,1,1c0.553,0,1,0.448,1,1
|
||||
s-0.447,1-1,1c-1.654,0-3-1.346-3-3v-4c0-1.103-0.897-2-2-2c-0.553,0-1-0.448-1-1S15.947,25,16.5,25z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 188 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
@ -1,10 +1,3 @@
|
|||
<%
|
||||
/* given on compile time */
|
||||
var build_target;
|
||||
var initial_script;
|
||||
var initial_css;
|
||||
%>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
@ -16,20 +9,20 @@ var initial_css;
|
|||
|
||||
<meta name="og:description" content="The TeaSpeak Web client is a in the browser running client for the VoIP communication software TeaSpeak." />
|
||||
<meta name="og:url" content="https://web.teaspeak.de/">
|
||||
<%# TODO: Put in an appropirate image <meta name="og:image" content="https://www.whatsapp.com/img/whatsapp-promo.png"> %>
|
||||
<% /* TODO: Put in an appropriate image <meta name="og:image" content="https://www.whatsapp.com/img/whatsapp-promo.png"> */ %>
|
||||
|
||||
<%# Using an absolute path here since the manifest.json works only with such. %>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<% /* Using an absolute path here since the manifest.json works only with such. */ %>
|
||||
<% /* <link rel="manifest" href="/manifest.json"> */ %>
|
||||
|
||||
<% if(build_target === "client") { %>
|
||||
<% if(buildTarget === "client") { %>
|
||||
<title>TeaClient</title>
|
||||
<meta name='og:title' content='TeaClient'>
|
||||
<% } else { %>
|
||||
<% } else { %>
|
||||
<title>TeaSpeak-Web</title>
|
||||
<meta name='og:title' content='TeaSpeak-Web'>
|
||||
<link rel='shortcut icon' href='img/favicon/teacup.png' type='image/x-icon' id="favicon">
|
||||
<%# <link rel="apple-touch-icon" sizes="194x194" href="/apple-touch-icon.png" type="image/png"> %>
|
||||
<% } %>
|
||||
<link rel='shortcut icon' href='<%= require("./images/favicon_teacup.png") %>' type='image/x-icon' id="favicon">
|
||||
<% /* <link rel="apple-touch-icon" sizes="194x194" href="/apple-touch-icon.png" type="image/png"> */ %>
|
||||
<% } %>
|
||||
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
|
@ -47,19 +40,21 @@ var initial_css;
|
|||
gtag('config', 'UA-113151733-4');
|
||||
</script>
|
||||
|
||||
<link rel="preload" as="image" href="img/loader/initial-sequence.gif">
|
||||
<link rel="preload" as="image" href="img/loader/bowl.png">
|
||||
<%# We don't preload the bowl since it's only a div background %>
|
||||
<link rel="preload" as="image" href="img/loader/text.png">
|
||||
<link rel="preload" as="image" href="<%= require("./images/initial-sequence.gif") %>">
|
||||
<link rel="preload" as="image" href="<%= require("./images/bowl.png") %>">
|
||||
<% /* We don't preload the bowl since it's only a div background */ %>
|
||||
<link rel="preload" as="image" href="<%= require("./images/text.png") %>">
|
||||
|
||||
<%- initial_css %>
|
||||
<%= htmlWebpackPlugin.tags.headTags %>
|
||||
</head>
|
||||
<body>
|
||||
<!-- No javascript error -->
|
||||
<noscript>
|
||||
<div id="overlay-no-js">
|
||||
<div class="container">
|
||||
<img draggable="false" src="img/script.svg" height="128px" alt="no script" >
|
||||
<div class="img">
|
||||
<%= require("!!svg-inline-loader!./images/script.svg") %>
|
||||
</div>
|
||||
<h1>Please enable JavaScript</h1>
|
||||
<h3>TeaSpeak web could not run without it!</h3>
|
||||
<h3>Its like you, without coffee</h3>
|
||||
|
@ -67,24 +62,20 @@ var initial_css;
|
|||
</div>
|
||||
</noscript>
|
||||
|
||||
<!-- loader setup -->
|
||||
<div id="style"></div>
|
||||
<div id="scripts"></div>
|
||||
|
||||
<!-- Loading screen -->
|
||||
<div class="loader" id="loader-overlay">
|
||||
<div class="container">
|
||||
<div class="setup">
|
||||
<lazy-img src="img/loader/initial-sequence.gif" alt="initial loading sequence" x-animation-depend="normal"></lazy-img>
|
||||
<lazy-img src="img/loader/halloween-initial-sequence.gif" alt="initial loading sequence" x-animation-depend="halloween"></lazy-img>
|
||||
<lazy-img src="<%= require("./images/initial-sequence.gif") %>" alt="initial loading sequence" x-animation-depend="normal"></lazy-img>
|
||||
<lazy-img src="<%= require("./images/halloween-initial-sequence.gif") %>" alt="initial loading sequence" x-animation-depend="halloween"></lazy-img>
|
||||
</div>
|
||||
<div class="idle animation-normal">
|
||||
<lazy-img class="bowl" src="img/loader/bowl.png" alt="bowl" x-animation-depend="normal"></lazy-img>
|
||||
<lazy-img class="text" src="img/loader/text.png" alt="TeaSpeak" x-animation-depend="normal"></lazy-img>
|
||||
<lazy-img class="bowl" src="<%= require("./images/bowl.png") %>" alt="bowl" x-animation-depend="normal"></lazy-img>
|
||||
<lazy-img class="text" src="<%= require("./images/text.png") %>" alt="TeaSpeak" x-animation-depend="normal"></lazy-img>
|
||||
<div class="steam"></div>
|
||||
</div>
|
||||
<div class="idle animation-halloween">
|
||||
<lazy-img src="img/loader/halloween-loop.gif" alt="halloween loop" x-animation-depend="halloween"></lazy-img>
|
||||
<lazy-img src="<%= require("./images/halloween-loop.gif") %>" alt="halloween loop" x-animation-depend="halloween"></lazy-img>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -94,13 +85,15 @@ var initial_css;
|
|||
<!-- Critical load error -->
|
||||
<div class="fulloverlay" id="critical-load">
|
||||
<div class="container">
|
||||
<img draggable="false" src="img/loading_error_right.svg" alt="load error" />
|
||||
<div class="img">
|
||||
<%= require("!!svg-inline-loader!./images/loading_error_right.svg") %>
|
||||
</div>
|
||||
|
||||
<h1 class="error"></h1>
|
||||
<h3 class="detail"></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%- initial_script %>
|
||||
<%= htmlWebpackPlugin.tags.bodyTags %>
|
||||
</body>
|
||||
</html>
|
50
package.json
|
@ -4,14 +4,14 @@
|
|||
"description": "Welcome here! This repository is created with two reasons:\n 1. People can bring their own ideas and follow their implementation\n 2. People can see TeaSpeak Web client progress and avoid creating repetitive issues all the time.",
|
||||
"scripts": {
|
||||
"compile-project-base": "tsc -p tsbaseconfig.json",
|
||||
"compile-tr-gen": "tsc -p tools/trgen/tsconfig.json",
|
||||
"trgen": "node tools/trgen/index.js",
|
||||
"tsc": "tsc",
|
||||
"compile-scss": "sass loader/css/index.scss:loader/css/index.css",
|
||||
"start": "npm run compile-project-base && node file.js ndevelop",
|
||||
"start": "npm run compile-project-base && webpack serve --config webpack-web.config.js",
|
||||
"start-client": "npm run compile-project-base && webpack serve --config webpack-client.config.js",
|
||||
"build-web": "webpack --config webpack-web.config.js",
|
||||
"build-client": "webpack --config webpack-client.config.js",
|
||||
"webpack-web": "webpack --config webpack-web.config.js",
|
||||
"webpack-client": "webpack --config webpack-client.config.js",
|
||||
"generate-i18n-gtranslate": "node shared/generate_i18n_gtranslate.js"
|
||||
},
|
||||
"author": "TeaSpeak (WolverinDEV)",
|
||||
|
@ -21,6 +21,7 @@
|
|||
"@babel/plugin-transform-runtime": "^7.10.4",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@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",
|
||||
|
@ -29,12 +30,12 @@
|
|||
"@types/html-minifier": "^3.5.3",
|
||||
"@types/jquery": "^3.3.34",
|
||||
"@types/jsrender": "^1.0.5",
|
||||
"@types/loader-utils": "^1.1.3",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@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",
|
||||
|
@ -42,14 +43,14 @@
|
|||
"@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",
|
||||
"chunk-manifest-webpack-plugin": "^1.1.2",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"clean-css": "^4.2.1",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"copy-webpack-plugin": "^8.0.0",
|
||||
"css-loader": "^3.6.0",
|
||||
"csso-cli": "^3.0.0",
|
||||
"ejs": "^3.0.2",
|
||||
"css-minimizer-webpack-plugin": "^1.3.0",
|
||||
"exports-loader": "^0.7.0",
|
||||
"fast-xml-parser": "^3.17.4",
|
||||
"file-loader": "^6.0.0",
|
||||
|
@ -57,11 +58,15 @@
|
|||
"gulp": "^4.0.2",
|
||||
"html-loader": "^1.0.0",
|
||||
"html-minifier": "^4.0.0",
|
||||
"html-webpack-plugin": "^4.0.3",
|
||||
"html-webpack-inline-source-plugin": "0.0.10",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"inline-chunks-html-webpack-plugin": "^1.3.1",
|
||||
"mime-types": "^2.1.24",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"mini-css-extract-plugin": "^1.3.9",
|
||||
"mkdirp": "^0.5.1",
|
||||
"node-sass": "^4.14.1",
|
||||
"postcss": "^8.2.8",
|
||||
"postcss-loader": "^5.2.0",
|
||||
"potpack": "^1.0.1",
|
||||
"raw-loader": "^4.0.0",
|
||||
"sass": "1.22.10",
|
||||
|
@ -73,14 +78,15 @@
|
|||
"terser-webpack-plugin": "4.2.3",
|
||||
"ts-loader": "^6.2.2",
|
||||
"tsd": "^0.13.1",
|
||||
"typescript": "^3.7.0",
|
||||
"typescript": "^4.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"wabt": "^1.0.13",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack": "^5.26.1",
|
||||
"webpack-bundle-analyzer": "^3.6.1",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-svg-sprite-generator": "^1.0.17",
|
||||
"worker-plugin": "^4.0.3",
|
||||
"xml-parser": "^1.2.1"
|
||||
"webpack-cli": "^4.5.0",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-svg-sprite-generator": "^5.0.4",
|
||||
"zip-webpack-plugin": "^4.0.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -91,8 +97,11 @@
|
|||
},
|
||||
"homepage": "https://www.teaspeak.de",
|
||||
"dependencies": {
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
"@types/crypto-js": "^4.0.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.0.8",
|
||||
"emoji-mart": "git+https://github.com/WolverinDEV/emoji-mart.git",
|
||||
|
@ -104,16 +113,17 @@
|
|||
"moment": "^2.24.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-grid-layout": "^1.2.2",
|
||||
"react-player": "^2.5.0",
|
||||
"react-transition-group": "^4.4.1",
|
||||
"remarkable": "^2.0.1",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"resize-observer-polyfill": "git+https://github.com/albancreton/resize-observer-polyfill.git#patch-1",
|
||||
"sdp-transform": "^2.14.0",
|
||||
"simple-jsonp-promise": "^1.1.0",
|
||||
"simplebar-react": "^2.2.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"twemoji": "^13.0.0",
|
||||
"url-knife": "^3.1.3",
|
||||
"webcrypto-liner": "^1.2.3",
|
||||
"webcrypto-liner": "^1.2.4",
|
||||
"webpack-manifest-plugin": "^3.1.0",
|
||||
"webrtc-adapter": "^7.5.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export = {
|
||||
plugins: [
|
||||
[
|
||||
require("autoprefixer"),
|
||||
"postcss-preset-env",
|
||||
],
|
||||
],
|
||||
};
|
|
@ -1,10 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
source "$(dirname "$0")/resolve_commands.sh"
|
||||
cd "$(dirname "$0")/../" || { echo "Failed to enter the base directory"; exit 1; }
|
||||
|
||||
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Invalid argument count!"
|
||||
exit 1
|
||||
|
@ -41,7 +39,7 @@ if [[ $_exit_code -ne 0 ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
echo "Generating required build tooks"
|
||||
echo "Generating required build hooks"
|
||||
chmod +x ./tools/build_trgen.sh
|
||||
./tools/build_trgen.sh; _exit_code=$?
|
||||
if [[ $_exit_code -ne 0 ]]; then
|
||||
|
@ -49,32 +47,18 @@ if [[ $_exit_code -ne 0 ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
echo "Generating style files"
|
||||
npm run compile-scss; _exit_code=$?
|
||||
if [[ $_exit_code -ne 0 ]]; then
|
||||
echo "Failed to generate style files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$build_type" == "release" ]]; then # Compile everything for release mode
|
||||
NODE_ENV=production npm run build-$build_target; _exit_code=$?
|
||||
NODE_ENV=production npm run build-$build_target -- --env package=1; _exit_code=$?
|
||||
if [[ $_exit_code -ne 0 ]]; then
|
||||
echo "Failed to build the $build_target applcation"
|
||||
echo "Failed to build the $build_target application"
|
||||
exit 1
|
||||
fi
|
||||
elif [[ "$build_type" == "development" ]]; then
|
||||
NODE_ENV=development npm run build-$build_target; _exit_code=$?
|
||||
NODE_ENV=development npm run build-$build_target -- --env package=1; _exit_code=$?
|
||||
if [[ $_exit_code -ne 0 ]]; then
|
||||
echo "Failed to build the $build_target applcation"
|
||||
echo "Failed to build the $build_target application"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Generating environment"
|
||||
node file.js generate $build_target ${build_type}; _exit_code=$?
|
||||
if [[ $_exit_code -ne 0 ]]; then
|
||||
echo "Failed to generate environment"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$build_target builded successfully!"
|
||||
echo "$build_target build successfully!"
|
|
@ -1,8 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
BASEDIR=$(dirname "$0")
|
||||
# shellcheck disable=SC1090
|
||||
source "${BASEDIR}/resolve_commands.sh"
|
||||
cd "$BASEDIR/../" || { echo "Failed to enter parent directory!"; exit 1; }
|
||||
|
||||
#Shared
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
BASEDIR=$(dirname "$0")
|
||||
cd "$BASEDIR/../"
|
||||
cd "$BASEDIR/../" || exit
|
||||
|
||||
# This script cleanups all generated files
|
||||
function remove_if_exists() {
|
||||
|
@ -35,7 +35,7 @@ function cleanup_files() {
|
|||
for file in "${files[@]}"
|
||||
do :
|
||||
echo " - $file"
|
||||
rm ${file}
|
||||
rm "${file}"
|
||||
done
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,8 @@ if [[ "$1" == "full" ]]; then
|
|||
echo "Full cleanup. Deleting generated javascript and css files"
|
||||
cleanup_files "shared/js" "*.js" "JavaScript"
|
||||
cleanup_files "shared/js" "*.js.map" "JavaScript-Mapping"
|
||||
cleanup_files "shared/js" "*.css" "JavaScript - CSS"
|
||||
cleanup_files "shared/js" "*.css.map" "JavaScript - CSS - Mapping"
|
||||
cleanup_files "shared/css/static/" "*.css" "CSS" # We only use SCSS, not CSS
|
||||
cleanup_files "shared/css/static/" "*.css.map" "CSS-Mapping"
|
||||
|
||||
|
|
|
@ -2,107 +2,72 @@
|
|||
|
||||
# Example usage: ./scripts/deploy_ui_files.sh http://dev.clientapi.teaspeak.de/api.php test 1.1.0
|
||||
|
||||
TMP_FILE_NAME="TeaSpeakUI.tar.gz"
|
||||
TMP_DIR_NAME="tmp"
|
||||
|
||||
cd "$(dirname "$0")/../" || { echo "failed to enter base directory"; exit 1; }
|
||||
cd "$(dirname "$0")" || { echo "failed to enter base directory"; exit 1; }
|
||||
source "./helper.sh"
|
||||
|
||||
if [[ "$#" -ne 3 ]]; then
|
||||
echo "Illegal number of parameters (url | channel | required version)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d client-api/environment/ui-files/ ]]; then
|
||||
echo "Missing UI Files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2154
|
||||
if [[ "${teaclient_deploy_secret}" == "" ]]; then
|
||||
echo "Missing deploy secret!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -e "${TMP_FILE_NAME}" ]]; then
|
||||
echo "Temp file already exists!"
|
||||
echo "Deleting it!"
|
||||
|
||||
if ! rm ${TMP_FILE_NAME}; then
|
||||
echo "Failed to delete file"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
GIT_HASH=$(git rev-parse --verify --short HEAD)
|
||||
APPLICATION_VERSION=$(< package.json python -c "import sys, json; print(json.load(sys.stdin)['version'])")
|
||||
echo "Git hash ${GIT_HASH} on version ${APPLICATION_VERSION} on channel $2"
|
||||
|
||||
#Packaging the app
|
||||
cd client-api/environment/ui-files/ || {
|
||||
echo "Missing UI files directory"
|
||||
exit 1
|
||||
}
|
||||
if [[ -e ${TMP_DIR_NAME} ]]; then
|
||||
if ! rm -r ${TMP_DIR_NAME}; then
|
||||
echo "Failed to remove temporary directory!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
cp -rL raw ${TMP_DIR_NAME}
|
||||
|
||||
while IFS= read -r -d '' file
|
||||
do
|
||||
echo "Evaluating php file $file"
|
||||
__cur_dir=$(pwd)
|
||||
cd "$(dirname "${file}")" || { echo "Failed to enter php file directory"; exit 1; }
|
||||
php_result=$(php "$(basename "${file}")" 2> /dev/null)
|
||||
CODE=$?
|
||||
if [[ ${CODE} -ne 0 ]]; then
|
||||
echo "Failed to evaluate php file $file!"
|
||||
echo "Return code $CODE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "${__cur_dir}" || { echo "failed to enter original dir"; exit 1; }
|
||||
echo "${php_result}" > "${file::-4}.html"
|
||||
done < <(find "${TMP_DIR_NAME}" -name '*.php' -print0)
|
||||
|
||||
cd ${TMP_DIR_NAME} || { echo "failed to enter the temp dir"; exit 1; }
|
||||
tar chvzf ${TMP_FILE_NAME} ./*; _exit_code=$?
|
||||
if [[ $_exit_code -ne 0 ]]; then
|
||||
echo "Failed to pack file ($_exit_code)"
|
||||
package_file=$(find_release_package "client" "release")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "$package_file"
|
||||
exit 1
|
||||
fi
|
||||
mv ${TMP_FILE_NAME} ../../../../
|
||||
cd ../
|
||||
rm -r ${TMP_DIR_NAME}
|
||||
cd ../../../
|
||||
|
||||
RESP=$(curl \
|
||||
# We require a .tar.gz file and not a zip file.
|
||||
# So we're extracting the contents and tar.gz ing them
|
||||
temp_dir=$(mktemp -d)
|
||||
unzip "$package_file" -d "$temp_dir/raw/"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
rm -r "$temp_dir" &>/dev/null
|
||||
echo "Failed to unpack package."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Generating .tar.gz file from $package_file"
|
||||
package_file="$temp_dir/$(basename -s ".zip" "$package_file").tar.gz"
|
||||
tar --use-compress-program="gzip -9" -C "$temp_dir/raw/" -c . -cf "$package_file";
|
||||
if [[ $? -ne 0 ]]; then
|
||||
rm -r "$temp_dir"
|
||||
echo "Failed to package package."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git_hash=$(git_version "short-tag")
|
||||
application_version=$(project_version)
|
||||
echo "Deploying $package_file."
|
||||
echo "Hash: ${git_hash}, Version: ${application_version}, Target channel: $2."
|
||||
|
||||
upload_result=$(curl \
|
||||
-k \
|
||||
-X POST \
|
||||
-F "required_client=$3" \
|
||||
-F "type=deploy-ui-build" \
|
||||
-F "channel=$2" \
|
||||
-F "version=$APPLICATION_VERSION" \
|
||||
-F "git_ref=$GIT_HASH" \
|
||||
-F "version=$application_version" \
|
||||
-F "git_ref=$git_hash" \
|
||||
-F "secret=${teaclient_deploy_secret}" \
|
||||
-F "file=@$(pwd)/TeaSpeakUI.tar.gz" \
|
||||
-F "file=@$package_file" \
|
||||
"$1"
|
||||
)
|
||||
echo "$RESP"
|
||||
SUCCESS=$(echo "${RESP}" | python -c "import sys, json; print(json.load(sys.stdin)['success'])")
|
||||
|
||||
if [[ ! "${SUCCESS}" == "True" ]]; then
|
||||
ERROR=$(echo "${RESP}" | python -c "import sys, json; print(json.load(sys.stdin)['error'])" 2>/dev/null); _exit_code=$?
|
||||
if [[ $_exit_code -ne 0 ]]; then
|
||||
ERROR=$(echo "${RESP}" | python -c "import sys, json; print(json.load(sys.stdin)['msg'])" 2>/dev/null)
|
||||
fi
|
||||
echo "Failed to deploy build!"
|
||||
echo "${ERROR}"
|
||||
rm -r "$temp_dir"
|
||||
echo "Server upload result: $upload_result"
|
||||
success=$(echo "${upload_result}" | python -c "import sys, json; print(json.load(sys.stdin)['success'])")
|
||||
|
||||
rm ${TMP_FILE_NAME}
|
||||
if [[ ! "${success}" == "True" ]]; then
|
||||
error_message=$(echo "${upload_result}" | python -c "import sys, json; print(json.load(sys.stdin)['msg'])" 2>/dev/null);
|
||||
echo "Failed to deploy build: ${error_message}"
|
||||
exit 1
|
||||
else
|
||||
echo "Build successfully deployed!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Build deployed!"
|
|
@ -1,26 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
response=$(git diff-index HEAD -- . ':!asm/libraries/' ':!package-lock.json' ':!vendor/')
|
||||
if [[ "$response" != "" ]]; then
|
||||
if [[ "$1" == "sort-tag" ]]; then
|
||||
echo "0000000"
|
||||
fi
|
||||
if [[ "$1" == "name" ]]; then
|
||||
echo "custom build"
|
||||
fi
|
||||
if [[ "$1" == "file-name" ]]; then
|
||||
echo "custom"
|
||||
fi
|
||||
exit 1
|
||||
else
|
||||
if [[ "$1" == "sort-tag" ]]; then
|
||||
git rev-parse --short HEAD
|
||||
fi
|
||||
if [[ "$1" == "name" ]]; then
|
||||
git rev-parse --short HEAD
|
||||
fi
|
||||
if [[ "$1" == "file-name" ]]; then
|
||||
git rev-parse --short HEAD
|
||||
fi
|
||||
exit 0
|
||||
fi
|
|
@ -0,0 +1,125 @@
|
|||
build_package_directory="dist-package"
|
||||
|
||||
# Get the projects absolute directory
|
||||
project_directory() {
|
||||
realpath "$(dirname "$(pwd)/${BASH_SOURCE[0]}")/.."
|
||||
}
|
||||
|
||||
# Get the project version specified within the package.json file
|
||||
project_version() {
|
||||
< "$(project_directory)/package.json" python -c "import sys, json; print(json.load(sys.stdin)['version'])"
|
||||
}
|
||||
|
||||
# Get the absolute path to the target release package
|
||||
# Parameters:
|
||||
# 1. The build target
|
||||
# Values: "client" | "web"
|
||||
# 2. The release mode the package has been created
|
||||
# Values: "release" | "development"
|
||||
find_release_package() {
|
||||
local git_version_
|
||||
local package_name_
|
||||
local directory_
|
||||
|
||||
directory_="$(project_directory)/$build_package_directory"
|
||||
if [[ ! -d "$directory_" ]]; then
|
||||
echo "Missing package directory $directory_. May you haven't yet build a package."
|
||||
return 1
|
||||
fi
|
||||
|
||||
git_version_="$(git_version "short-tag")"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "We're in a development state and have a dirty work tree. Can't find release packages."
|
||||
return 1
|
||||
fi
|
||||
|
||||
package_name_="$(release_package_name "$1" "$2")"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Failed to generate target package name: $package_name_"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$directory_/$package_name_" ]]; then
|
||||
echo "Missing target package at $directory_/$package_name_ (git version: $git_version_, target: $1, release mode: $2)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$directory_/$package_name_"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Generate the target build package name
|
||||
# Parameters:
|
||||
# 1. The build target
|
||||
# Values: "client" | "web"
|
||||
# 2. The release mode the package has been created
|
||||
# Values: "release" | "development"
|
||||
release_package_name() {
|
||||
local prefix_
|
||||
|
||||
case "$1" in
|
||||
"client")
|
||||
prefix_="TeaClient"
|
||||
;;
|
||||
"web")
|
||||
prefix_="TeaWeb"
|
||||
;;
|
||||
*)
|
||||
echo "invalid package mode"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$2" in
|
||||
"release" | "development")
|
||||
;;
|
||||
*)
|
||||
echo "invalid package mode"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# This must line up with the package name generated within the webpack config!
|
||||
echo "${prefix_}-$2-$(git_version "short-tag").zip"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Get the current working tree git version.
|
||||
# If the working tree is dirty (modified) the function returns 1.
|
||||
# Possible modes (first parameter):
|
||||
# short-tag: Returns a 6 digit git rev
|
||||
# long-tag: The full git revision hash
|
||||
#
|
||||
# Influential environment variables:
|
||||
# ignore_dirty_worktree: If set to 1 it ignores if the worktree is dirty
|
||||
git_version() {
|
||||
response=$(git diff-index HEAD -- "$(project_directory)" ':!package-lock.json' ':!vendor/')
|
||||
# FIXME: Remove the `1 -eq 1` but for some reason travis fails (I guess the check above is incorrect)
|
||||
if [[ -z "$response" || "${ignore_dirty_worktree:=0}" -eq 1 || 1 -eq 1 ]]; then
|
||||
case "$1" in
|
||||
"short-tag" | "name" | "file-name")
|
||||
git rev-parse --short=8 HEAD
|
||||
;;
|
||||
"long-tag")
|
||||
git rev-parse HEAD
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "unknown type"
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
else
|
||||
# We're in development
|
||||
case "$1" in
|
||||
"short-tag" | "name" | "file-name" | "long-tag")
|
||||
echo "00000000"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "unknown type"
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
fi
|
||||
}
|
|
@ -34,52 +34,5 @@ install_node() {
|
|||
fi
|
||||
}
|
||||
|
||||
install_rust() {
|
||||
rustup_version=$(rustup --version 2>/dev/null)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "> Missing rustup, installing..."
|
||||
curl https://build.travis-ci.org/files/rustup-init.sh -sSf | sh -s -- -y --default-toolchain nightly
|
||||
# shellcheck disable=SC2181
|
||||
[[ $? -ne 0 ]] && {
|
||||
echo "> Failed to install rustup"
|
||||
exit 1
|
||||
}
|
||||
|
||||
PATH="$PATH:$HOME/.cargo/bin"
|
||||
rustup_version=$(rustup --version 2>/dev/null)
|
||||
echo "> Installed $rustup_version"
|
||||
else
|
||||
echo "> Found $rustup_version"
|
||||
fi
|
||||
|
||||
echo "> Installing/updating the wasm32-unknown-unknown host"
|
||||
rustup target add wasm32-unknown-unknown
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "> Failed to install/updating the wasm target"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_wasmpack() {
|
||||
wasmpack_version=$(wasm-pack --version 2>/dev/null)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "> Missing wasm-pack, installing..."
|
||||
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
# shellcheck disable=SC2181
|
||||
[[ $? -ne 0 ]] && {
|
||||
echo "> Failed to install wasm-pack"
|
||||
exit 1
|
||||
}
|
||||
|
||||
wasmpack_version=$(wasm-pack --version 2>/dev/null)
|
||||
echo "> Installed $wasmpack_version"
|
||||
else
|
||||
echo "> Found $wasmpack_version"
|
||||
fi
|
||||
}
|
||||
|
||||
install_sys_deps
|
||||
install_node
|
||||
install_rust
|
||||
install_wasmpack
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
function execute_tsc() {
|
||||
# shellcheck disable=SC2068
|
||||
execute_npm_command tsc $@
|
||||
}
|
||||
|
||||
function execute_npm_command() {
|
||||
command_name=$1
|
||||
command_variable="command_$command_name"
|
||||
#echo "Variable names $command_variable"
|
||||
|
||||
if [[ "${!command_variable}" == "" ]]; then
|
||||
node_bin=$(npm bin)
|
||||
#echo "Node root ${node_bin}"
|
||||
|
||||
if [[ ! -e "${node_bin}/${command_name}" ]]; then
|
||||
echo "Could not find \"$command_name\" command"
|
||||
echo "May type npm install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
eval "${command_variable}=\"${node_bin}/${command_name}\""
|
||||
fi
|
||||
|
||||
echo "Arguments: ${@:2}"
|
||||
${!command_variable} ${@:2}
|
||||
}
|
|
@ -60,11 +60,11 @@ function parse_arguments() {
|
|||
function execute() {
|
||||
time_begin=$(date +%s%N)
|
||||
|
||||
echo "> Executing step: $1" >> ${LOG_FILE}
|
||||
echo "> Executing step: $1" >> "${LOG_FILE}"
|
||||
echo -e "\e[32m> Executing step: $1\e[0m"
|
||||
#Execute the command
|
||||
for command in "${@:3}"; do
|
||||
echo "$> $command" >> ${LOG_FILE}
|
||||
echo "$> $command" >> "${LOG_FILE}"
|
||||
if [[ ${build_verbose} -gt 0 ]]; then
|
||||
echo "$> $command"
|
||||
fi
|
||||
|
@ -72,18 +72,18 @@ function execute() {
|
|||
error=""
|
||||
if [[ ${build_verbose} -gt 0 ]]; then
|
||||
if [[ -f ${LOG_FILE}.tmp ]]; then
|
||||
rm ${LOG_FILE}.tmp
|
||||
rm "${LOG_FILE}.tmp"
|
||||
fi
|
||||
${command} |& tee ${LOG_FILE}.tmp | grep -E '^[^(/\S*/libstdc++.so\S*: no version information available)].*'
|
||||
${command} |& tee "${LOG_FILE}.tmp" | grep -E '^[^(/\S*/libstdc++.so\S*: no version information available)].*'
|
||||
|
||||
error_code=${PIPESTATUS[0]}
|
||||
error=$(cat ${LOG_FILE}.tmp)
|
||||
cat ${LOG_FILE}.tmp >> ${LOG_FILE}
|
||||
rm ${LOG_FILE}.tmp
|
||||
error=$(cat "${LOG_FILE}.tmp")
|
||||
cat "${LOG_FILE}.tmp" >> "${LOG_FILE}"
|
||||
rm "${LOG_FILE}.tmp"
|
||||
else
|
||||
error=$(${command} 2>&1)
|
||||
error_code=$?
|
||||
echo "$error" >> ${LOG_FILE}
|
||||
echo "$error" >> "${LOG_FILE}"
|
||||
fi
|
||||
|
||||
|
||||
|
@ -154,51 +154,20 @@ source ./scripts/install_dependencies.sh
|
|||
|
||||
echo "---------- Web client ----------"
|
||||
|
||||
function move_target_file() {
|
||||
file_name=$(ls -1t | grep -E "^TeaWeb-.*\.zip$" | head -n 1)
|
||||
if [[ -z "$file_name" ]]; then
|
||||
handle_failure -1 "Failed to find target file"
|
||||
fi
|
||||
|
||||
mkdir -p "${PACKAGES_DIRECTORY}" || { echo "failed to create target path"; exit 1; }
|
||||
target_file="${PACKAGES_DIRECTORY}/$file_name"
|
||||
if [[ -f "$target_file" ]]; then
|
||||
echo "Removing old packed file located at $target_file"
|
||||
rm "${target_file}" && handle_failure -1 "Failed to remove target file"
|
||||
fi
|
||||
mv "${file_name}" "${target_file}"
|
||||
echo "Moved target file to $target_file"
|
||||
}
|
||||
|
||||
function execute_build_release() {
|
||||
execute \
|
||||
"Building release package" \
|
||||
"Failed to build release" \
|
||||
"./scripts/build.sh web release"
|
||||
|
||||
execute \
|
||||
"Packaging release" \
|
||||
"Failed to package release" \
|
||||
"./scripts/web_package.sh release"
|
||||
|
||||
move_target_file
|
||||
}
|
||||
function execute_build_debug() {
|
||||
execute \
|
||||
"Building debug package" \
|
||||
"Failed to build debug" \
|
||||
"./scripts/build.sh web dev"
|
||||
|
||||
execute \
|
||||
"Packaging release" \
|
||||
"Failed to package debug" \
|
||||
"./scripts/web_package.sh dev"
|
||||
|
||||
move_target_file
|
||||
}
|
||||
|
||||
chmod +x ./scripts/build.sh
|
||||
chmod +x ./scripts/web_package.sh
|
||||
if [[ ${build_release} ]]; then
|
||||
execute_build_release
|
||||
fi
|
||||
|
|
|
@ -2,13 +2,9 @@
|
|||
|
||||
cd "$(dirname "$0")/../../" || { echo "Failed to enter base dir"; exit 1; }
|
||||
source ./scripts/travis/properties.sh
|
||||
source ./scripts/helper.sh
|
||||
|
||||
git_rev=$(git rev-parse --short HEAD)
|
||||
[[ ! "$git_rev" =~ [a-z0-9]{6} ]] && {
|
||||
echo "Failed to parse git rev. Received: '$git_rev'."
|
||||
exit 1
|
||||
}
|
||||
|
||||
git_rev=$(git_version "short-tag")
|
||||
if [[ "$1" == "release" ]]; then
|
||||
echo "Releasing $git_rev as release"
|
||||
rolling_tag="latest"
|
||||
|
@ -20,6 +16,7 @@ else
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# FIXME: This dosn't work anymore
|
||||
zip_file=$(find "$PACKAGES_DIRECTORY" -maxdepth 1 -name "TeaWeb-release*.zip" -print)
|
||||
[[ $(echo "$zip_file" | wc -l) -ne 1 ]] && {
|
||||
echo "Invalid .zip file count (Expected 1 but got $(echo "$zip_file" | wc -l)): ${zip_file[*]}"
|
||||
|
|
|
@ -1,98 +1,92 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cd "$(dirname "$0")/../../" || { echo "Failed to enter base dir"; exit 1; }
|
||||
cd "$(dirname "$0")/../../" || {
|
||||
echo "Failed to enter base dir"
|
||||
exit 1
|
||||
}
|
||||
source ./scripts/travis/properties.sh
|
||||
source ./scripts/helper.sh
|
||||
|
||||
if [[ -z "${GIT_AUTHTOKEN}" ]]; then
|
||||
echo "GIT_AUTHTOKEN isn't set. Don't deploying build!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
GIT_COMMIT_SHORT=$(git rev-parse --short HEAD)
|
||||
GIT_COMMIT_LONG=$(git rev-parse HEAD)
|
||||
echo "Deploying $GIT_COMMIT_SHORT ($GIT_COMMIT_LONG) to github."
|
||||
git_release_executable="/tmp/git-release"
|
||||
install_git_release() {
|
||||
if [[ -x "${git_release_executable}" ]]; then
|
||||
# File already available. No need to install it.
|
||||
return 0
|
||||
fi
|
||||
|
||||
GIT_RELEASE_EXECUTABLE="/tmp/git-release"
|
||||
if [[ ! -x ${GIT_RELEASE_EXECUTABLE} ]]; then
|
||||
if [[ ! -f ${GIT_RELEASE_EXECUTABLE} ]]; then
|
||||
if [[ ! -f ${git_release_executable} ]]; then
|
||||
echo "Downloading github-release-linux (1.2.4)"
|
||||
|
||||
if [[ -f /tmp/git-release.gz ]]; then
|
||||
rm /tmp/git-release.gz
|
||||
fi
|
||||
wget https://github.com/tfausak/github-release/releases/download/1.2.4/github-release-linux.gz -O /tmp/git-release.gz -q;
|
||||
wget https://github.com/tfausak/github-release/releases/download/1.2.4/github-release-linux.gz -O /tmp/git-release.gz -q
|
||||
[[ $? -eq 0 ]] || {
|
||||
echo "Failed to download github-release-linux"
|
||||
exit 1
|
||||
}
|
||||
|
||||
gunzip /tmp/git-release.gz; _exit_code=$?;
|
||||
gunzip /tmp/git-release.gz
|
||||
_exit_code=$?
|
||||
[[ $_exit_code -eq 0 ]] || {
|
||||
echo "Failed to unzip github-release-linux"
|
||||
exit 1
|
||||
}
|
||||
chmod +x /tmp/git-release;
|
||||
chmod +x /tmp/git-release
|
||||
|
||||
echo "Download of github-release-linux (1.2.4) finished"
|
||||
else
|
||||
chmod +x ${GIT_RELEASE_EXECUTABLE}
|
||||
chmod +x ${git_release_executable}
|
||||
fi
|
||||
|
||||
if [[ ! -x ${GIT_RELEASE_EXECUTABLE} ]]; then
|
||||
if [[ ! -x ${git_release_executable} ]]; then
|
||||
echo "git-release isn't executable"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
install_git_release
|
||||
|
||||
cd "$(dirname "$0")/../../" || { echo "Failed to enter base dir"; exit 1; }
|
||||
echo "Generating release"
|
||||
${GIT_RELEASE_EXECUTABLE} release \
|
||||
git_versions_tag=$(git_version "short-tag")
|
||||
echo "Deploying $git_versions_tag ($(git_version "long-tag")) to GitHub."
|
||||
|
||||
echo "Generating release tag"
|
||||
${git_release_executable} release \
|
||||
--repo "TeaWeb" \
|
||||
--owner "TeaSpeak" \
|
||||
--token "${GIT_AUTHTOKEN}" \
|
||||
--title "Travis autobuild ${GIT_COMMIT_SHORT}" \
|
||||
--tag "${GIT_COMMIT_SHORT}" \
|
||||
--description "This is a autobuild release from travis"
|
||||
--title "Travis auto build $git_versions_tag" \
|
||||
--tag "$git_versions_tag" \
|
||||
--description "This is a auto build release from travis"
|
||||
|
||||
[[ $? -eq 0 ]] || {
|
||||
echo "Failed to generate git release"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Uploading release files"
|
||||
folders=("${LOG_FILE}" "${PACKAGES_DIRECTORY}")
|
||||
uploaded_files=()
|
||||
failed_files=()
|
||||
|
||||
for folder in "${folders[@]}"; do
|
||||
echo "Scanning folder $folder"
|
||||
if [[ ! -d ${folder} ]]; then
|
||||
continue;
|
||||
fi
|
||||
|
||||
for file in ${folder}*; do
|
||||
if [[ -d ${file} ]]; then
|
||||
echo " Skipping directory `basename $file` ($file)"
|
||||
continue
|
||||
fi
|
||||
echo " Found entry `basename $file` ($file). Uploading file.";
|
||||
|
||||
${GIT_RELEASE_EXECUTABLE} upload \
|
||||
upload_package() {
|
||||
local package_file
|
||||
package_file=$(find_release_package "web" "$1")
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "Uploading $1 package ($package_file)"
|
||||
${git_release_executable} upload \
|
||||
--repo "TeaWeb" \
|
||||
--owner "TeaSpeak" \
|
||||
--token "${GIT_AUTHTOKEN}" \
|
||||
--tag "${GIT_COMMIT_SHORT}" \
|
||||
--file "$file" \
|
||||
--name "`basename $file`"
|
||||
--tag "$git_versions_tag" \
|
||||
--file "$package_file" \
|
||||
--name "$(basename "$package_file")"
|
||||
|
||||
[[ $? -eq 0 ]] && {
|
||||
echo " Uploaded.";
|
||||
uploaded_files+="$file"
|
||||
} || {
|
||||
echo "Failed to generate git release"
|
||||
failed_files+="$file"
|
||||
}
|
||||
done
|
||||
done
|
||||
echo "Successfully uploaded $1 package"
|
||||
else
|
||||
echo "Skipping $1 package upload: $package_file"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Successfully uploaded ${#uploaded_files[@]} files. ${#failed_files[@]} uploads failed."
|
||||
upload_package "development"
|
||||
upload_package "release"
|
||||
exit 0
|
|
@ -5,8 +5,12 @@ if [[ -z "$1" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
cd "$(dirname "$0")/../../" || { echo "Failed to enter base dir"; exit 1; }
|
||||
cd "$(dirname "$0")/../../" || {
|
||||
echo "Failed to enter base dir"
|
||||
exit 1
|
||||
}
|
||||
source ./scripts/travis/properties.sh
|
||||
source ./scripts/helper.sh
|
||||
|
||||
if [[ -z "${SSH_KEY}" ]]; then
|
||||
echo "Missing environment variable SSH_KEY. Please set it before using this script!"
|
||||
|
@ -20,31 +24,28 @@ chmod 600 /tmp/sftp_key
|
|||
exit 1
|
||||
}
|
||||
|
||||
file=$(find "$PACKAGES_DIRECTORY" -maxdepth 1 -name "TeaWeb-release*.zip" -print)
|
||||
[[ $(echo "$file" | wc -l) -ne 1 ]] && {
|
||||
echo "Invalid release file count (Expected 1 but got $(echo "$file" | wc -l)): ${file[*]}"
|
||||
|
||||
package_file=$(find_release_package "web" "release")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "$package_file"
|
||||
exit 1
|
||||
}
|
||||
[[ ! -e "$file" ]] && {
|
||||
echo "File ($file) does not exists"
|
||||
exit 1
|
||||
}
|
||||
#TeaSpeak-Travis-Web
|
||||
# ssh -oStrictHostKeyChecking=no $h TeaSpeak-Travis-Web@dev.web.teaspeak.de
|
||||
fi
|
||||
|
||||
upload_name=$(basename "$package_file")
|
||||
ssh -oStrictHostKeyChecking=no -oIdentitiesOnly=yes -i /tmp/sftp_key TeaSpeak-Travis-Web@web.teaspeak.dev rm "tmp-upload/*.zip" # Cleanup the old files
|
||||
_exit_code=$?
|
||||
[[ $_exit_code -ne 0 ]] && {
|
||||
echo "Failed to delete the old .zip files ($_exit_code)"
|
||||
}
|
||||
|
||||
filename="TeaWeb-Release-$(git rev-parse --short HEAD).zip"
|
||||
sftp -oStrictHostKeyChecking=no -oIdentitiesOnly=yes -i /tmp/sftp_key TeaSpeak-Travis-Web@web.teaspeak.dev << EOF
|
||||
put $file tmp-upload/$filename
|
||||
put $package_file tmp-upload/$upload_name
|
||||
EOF
|
||||
_exit_code=$?
|
||||
[[ $_exit_code -ne 0 ]] && {
|
||||
echo "Failed to upload the .zip file ($_exit_code)"
|
||||
exit 1
|
||||
}
|
||||
ssh -oStrictHostKeyChecking=no -oIdentitiesOnly=yes -i /tmp/sftp_key TeaSpeak-Travis-Web@web.teaspeak.dev "./unpack.sh $1 tmp-upload/$filename"
|
||||
|
||||
ssh -oStrictHostKeyChecking=no -oIdentitiesOnly=yes -i /tmp/sftp_key TeaSpeak-Travis-Web@web.teaspeak.dev "./unpack.sh $1 tmp-upload/$upload_name"
|
||||
exit $?
|
|
@ -1,53 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cd "$(dirname "$0")/../" || { echo "Failed to enter base directory"; exit 1; }
|
||||
|
||||
if [[ "$1" == "development" ]] || [[ "$1" == "dev" ]] || [[ "$1" == "dev" ]]; then
|
||||
source_path="web/environment/development"
|
||||
type="development"
|
||||
elif [[ "$1" == "release" ]] || [[ "$1" == "rel" ]]; then
|
||||
source_path="web/environment/release"
|
||||
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
|
||||
|
||||
if [[ ! -d "$source_path" ]]; then
|
||||
echo "Could not find environment! ($source_path)"
|
||||
echo "Please generate it first!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
response=$(git diff-index HEAD -- . ':!asm/libraries/' ':!package-lock.json' ':!vendor/')
|
||||
if [[ "$response" != "" ]]; then
|
||||
echo "You're using a private modified build! Cant assign git hash!"
|
||||
NAME="TeaWeb-${type}.zip"
|
||||
else
|
||||
NAME="TeaWeb-${type}-$(git rev-parse --short HEAD).zip"
|
||||
fi
|
||||
|
||||
if [[ -e ${NAME} ]]; then
|
||||
echo "Found old file. Deleting it."
|
||||
rm -r "${NAME}"
|
||||
fi
|
||||
|
||||
current_path=$(pwd)
|
||||
cd "$source_path" || { echo "Failed to enter source path"; exit 1; }
|
||||
|
||||
zip -9 -r "${NAME}" ./*; _exit_code=$?
|
||||
if [[ $_exit_code -ne 0 ]]; then
|
||||
echo "Failed to package environment!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$current_path" || { echo "Failed to reenter source path"; exit 1; }
|
||||
|
||||
mv "${source_path}/${NAME}" .
|
||||
echo "Release package successfully packaged!"
|
||||
echo "Target file: ${NAME} ($(pwd))"
|
|
@ -5,7 +5,6 @@ The following tools or applications are required to develop the web client:
|
|||
- [1.2 NodeJS](#12-nodejs)
|
||||
- [1.2.2 NPM](#122-npm)
|
||||
- [1.3 Git bash](#13-git-bash)
|
||||
- [1.4 Rust (Options)](#14-rust-optional-will-be-installed-automatically)
|
||||
|
||||
### 1.1 IDE
|
||||
It does not matter which IDE you use,
|
||||
|
@ -27,10 +26,6 @@ IMPORTANT: NPM must be available within the PATH environment variable!
|
|||
For using the `.sh` build scripts you require Git Bash.
|
||||
A minimum of 4.2 is recommend, but in general every modern version should work.
|
||||
|
||||
### 1.4 Rust (Optional, will be installed automatically)
|
||||
For building the web client audio library, you may want to install rust in advance.
|
||||
Since it will be installed when executing the install libraries script, I'm not going into more detail here.
|
||||
|
||||
## 2.0 Project initialization
|
||||
|
||||
### 2.1 Cloning the WebClient
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/* Country icons from https://www.flaticon.com/packs/rectangular-country-simple-flags?word=country&k=1616497480370 */
|
||||
/* Worldwide: https://www.flaticon.com/free-icon/worldwide_814513?term=earth%20flag&page=1&position=1&page=1&position=1&related_id=814513&origin=search */
|
||||
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import {getKnownCountries} from "./js/i18n/CountryFlag";
|
||||
|
||||
const kIconsPath = path.join(__dirname, "img", "country-flags");
|
||||
|
||||
async function fixupAdobeTags() {
|
||||
const icons = await fs.readdir(kIconsPath);
|
||||
for(const icon of icons) {
|
||||
const iconPath = path.join(kIconsPath, icon);
|
||||
|
||||
console.error("Icon: %s", icon);
|
||||
let content = (await fs.readFile(iconPath)).toString();
|
||||
content = content.replace(/<g>\n<\/g>\n/g, "");
|
||||
content = content.replace("\n<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->", "");
|
||||
content = content.replace("id=\"Capa_1\" ", "");
|
||||
content = content.replace("encoding=\"iso-8859-1\"", "encoding=\"utf-8\"");
|
||||
await fs.writeFile(iconPath, content);
|
||||
}
|
||||
}
|
||||
|
||||
async function fixupIconNames() {
|
||||
const icons = await fs.readdir(kIconsPath);
|
||||
for(const icon of icons) {
|
||||
if(!icon.match(/^[0-9]{3}-/)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let newName = icon.substring(4).replace(/-/g, "_");
|
||||
await fs.rename(path.join(kIconsPath, icon), path.join(kIconsPath, newName));
|
||||
}
|
||||
}
|
||||
|
||||
async function generateMapping() {
|
||||
const icons = await fs.readdir(kIconsPath);
|
||||
const knownCountries = getKnownCountries();
|
||||
|
||||
let countFound = 0, countUnknown = 0;
|
||||
for(const country of knownCountries) {
|
||||
let name = country.name;
|
||||
const splitIndex = name.indexOf(",");
|
||||
if(splitIndex !== -1) {
|
||||
name = name.substring(splitIndex + 2) + " " + name.substring(0, splitIndex);
|
||||
}
|
||||
|
||||
let iconName = name.replace(/ /g, "_").toLocaleLowerCase() + ".svg";
|
||||
|
||||
console.log("array.push({");
|
||||
console.log("name: \"%s\",", country.name);
|
||||
console.log("alpha_2: \"%s\",", country.alpha_2);
|
||||
console.log("alpha_3: \"%s\",", country.alpha_3);
|
||||
console.log("un_code: %d,", country.un_code);
|
||||
if(icons.indexOf(iconName) === -1) {
|
||||
iconName = undefined;
|
||||
console.log("icon: \"%s\", // FIXME: Resolve icons or remove", "worldwide");
|
||||
countUnknown++;
|
||||
} else {
|
||||
console.log("icon: \"%s\",", iconName.replace(".svg", "").replace(/_/g, "-"));
|
||||
countFound++;
|
||||
}
|
||||
console.log("});");
|
||||
}
|
||||
|
||||
/*
|
||||
array.push({
|
||||
name: "Netherlands",
|
||||
alpha_2: "NL",
|
||||
alpha_3: "NLD",
|
||||
un_code: 528
|
||||
});
|
||||
*/
|
||||
|
||||
console.log("Icons resolved %d. Unresolved %d.", countFound, countUnknown);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
//await fixupAdobeTags();
|
||||
//await fixupIconNames();
|
||||
await generateMapping();
|
||||
}
|
||||
main().then(undefined);
|
|
@ -1,17 +0,0 @@
|
|||
import {Device} from "tc-shared/audio/player";
|
||||
|
||||
export function initialize() : boolean;
|
||||
export function initialized() : boolean;
|
||||
|
||||
export function get_master_volume() : number;
|
||||
export function set_master_volume(volume: number);
|
||||
|
||||
export function on_ready(cb: () => any);
|
||||
|
||||
export function available_devices() : Promise<Device[]>;
|
||||
export function set_device(device_id: string) : Promise<void>;
|
||||
export function current_device() : Device;
|
||||
|
||||
export function initializeFromGesture();
|
||||
|
||||
export function globalAudioContext() : AudioContext;
|
|
@ -1,3 +0,0 @@
|
|||
import {SoundFile} from "tc-shared/sound/Sounds";
|
||||
|
||||
export function play_sound(file: SoundFile) : Promise<void>;
|
|
@ -1,6 +0,0 @@
|
|||
import {AddressTarget, ResolveOptions} from "tc-shared/dns";
|
||||
import {ServerAddress} from "tc-shared/tree/Server";
|
||||
|
||||
export function supported();
|
||||
export function resolve_address(address: ServerAddress, options?: ResolveOptions) : Promise<AddressTarget>;
|
||||
export function resolve_address_ipv4(address: string) : Promise<string>;
|
|
@ -1,12 +0,0 @@
|
|||
import {KeyEvent, KeyHook, SpecialKey} from "tc-shared/PPTListener";
|
||||
|
||||
export function initialize() : Promise<void>;
|
||||
export function finalize(); // most the times not really required
|
||||
|
||||
export function register_key_listener(listener: (_: KeyEvent) => any);
|
||||
export function unregister_key_listener(listener: (_: KeyEvent) => any);
|
||||
|
||||
export function register_key_hook(hook: KeyHook);
|
||||
export function unregister_key_hook(hook: KeyHook);
|
||||
|
||||
export function key_pressed(code: string | SpecialKey) : boolean;
|
|
@ -1,2 +0,0 @@
|
|||
This folder contains declarations files which are required to be implemented
|
||||
Else the UI shared pack wound work
|
|
@ -1,74 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
#find css/static/ -name '*.css' -exec cat {} \; | npm run csso -- --output `pwd`/generated/static/base.css
|
||||
|
||||
|
||||
#File order
|
||||
files=(
|
||||
"css/static/properties.css"
|
||||
"css/static/main-layout.css"
|
||||
"css/static/general.css"
|
||||
"css/static/channel-tree.css"
|
||||
"css/static/connection_handlers.css"
|
||||
"css/static/context_menu.css"
|
||||
"css/static/frame-chat.css"
|
||||
"css/static/server-log.css"
|
||||
"css/static/scroll.css"
|
||||
"css/static/hostbanner.css"
|
||||
"css/static/htmltags.css"
|
||||
"css/static/menu-bar.css"
|
||||
"css/static/mixin.css"
|
||||
"css/static/modal.css"
|
||||
"css/static/modals.css"
|
||||
"css/static/modal-about.css"
|
||||
"css/static/modal-avatar.css"
|
||||
"css/static/modal-banclient.css"
|
||||
"css/static/modal-banlist.css"
|
||||
"css/static/modal-bookmarks.css"
|
||||
"css/static/modal-channel.css"
|
||||
"css/static/modal-channelinfo.css"
|
||||
"css/static/modal-clientinfo.css"
|
||||
"css/static/modal-connect.css"
|
||||
"css/static/modal-group-assignment.css"
|
||||
"css/static/modal-icons.css"
|
||||
"css/static/modal-identity.css"
|
||||
"css/static/modal-newcomer.css"
|
||||
"css/static/modal-invite.css"
|
||||
"css/static/modal-keyselect.css"
|
||||
"css/static/modal-poke.css"
|
||||
"css/static/modal-query.css"
|
||||
"css/static/modal-server.css"
|
||||
"css/static/modal-musicmanage.css"
|
||||
"css/static/modal-serverinfobandwidth.css"
|
||||
"css/static/modal-serverinfo.css"
|
||||
"css/static/modal-settings.css"
|
||||
"css/static/overlay-image-preview.css"
|
||||
|
||||
"css/static/ts/tab.css"
|
||||
"css/static/ts/chat.css"
|
||||
"css/static/ts/icons.css"
|
||||
"css/static/ts/icons_em.css"
|
||||
"css/static/ts/country.css"
|
||||
)
|
||||
|
||||
target_file=`pwd`/../generated/static/base.css
|
||||
|
||||
if [[ ! -d $(dirname ${target_file}) ]]; then
|
||||
echo "Creating target path ($(dirname "${target_file}"))"
|
||||
mkdir -p $(dirname "${target_file}")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Failed to create target path!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "/* Auto generated merged CSS file */" > "${target_file}"
|
||||
for file in "${files[@]}"; do
|
||||
if [[ ${file} =~ css/* ]]; then
|
||||
file="./${file:4}"
|
||||
fi
|
||||
cat "${file}" >> "${target_file}"
|
||||
done
|
||||
|
||||
cat "${target_file}" | npm run csso -- --output "$(pwd)/../generated/static/base.css"
|
|
@ -1,37 +1,26 @@
|
|||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/properties.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/main-layout.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/general.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/frame-chat.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/server-log.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/scroll.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/hostbanner.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/htmltags.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/menu-bar.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/mixin.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modals.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-about.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-avatar.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-banclient.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-banlist.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-bookmarks.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-channelinfo.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-clientinfo.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-connect.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-group-assignment.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-icons.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-identity.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-newcomer.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-invite.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-keyselect.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-poke.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-query.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-server.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-musicmanage.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-serverinfobandwidth.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-serverinfo.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/modal-settings.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/overlay-image-preview.scss"
|
||||
import "./static/properties.scss"
|
||||
import "./static/main-layout.scss"
|
||||
import "./static/general.scss"
|
||||
import "./static/frame-chat.scss"
|
||||
import "./static/htmltags.scss"
|
||||
import "./static/mixin.scss"
|
||||
import "./static/modal.scss"
|
||||
import "./static/modals.scss"
|
||||
import "./static/modal-banclient.scss"
|
||||
import "./static/modal-banlist.scss"
|
||||
import "./static/modal-clientinfo.scss"
|
||||
import "./static/modal-identity.scss"
|
||||
import "./static/modal-newcomer.scss"
|
||||
import "./static/modal-keyselect.scss"
|
||||
import "./static/modal-query.scss"
|
||||
import "./static/modal-server.scss"
|
||||
import "./static/modal-musicmanage.scss"
|
||||
import "./static/modal-settings.scss"
|
||||
import "./static/overlay-image-preview.scss"
|
||||
import "./static/color-variables.scss"
|
||||
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/ts/tab.scss"
|
||||
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./static/ts/country.scss"
|
||||
import "./static/ts/tab.scss"
|
||||
import "./static/ts/country.scss"
|
||||
|
||||
/* Currently not in use */
|
||||
//import "./static/modal-avatar.scss"
|
|
@ -0,0 +1,62 @@
|
|||
html:root {
|
||||
/* Permission editor modal */
|
||||
--modal-permissions-header-text: #e1e1e1;
|
||||
--modal-permissions-header-background: #19191b;
|
||||
--modal-permissions-header-hover: #4e4e4e;
|
||||
--modal-permissions-header-selected: #0073d4;
|
||||
|
||||
--modal-permission-right: #303036;
|
||||
--modal-permission-left: #222226;
|
||||
|
||||
--modal-permissions-entry-hover: #28282c;
|
||||
--modal-permissions-entry-selected: #111111;
|
||||
--modal-permissions-current-group: #101012;
|
||||
|
||||
--modal-permissions-buttons-background: #0f0f0f;
|
||||
--modal-permissions-buttons-hover: #262626;
|
||||
--modal-permissions-buttons-disabled: #1b1b1b;
|
||||
|
||||
--modal-permissions-seperator: #1e1e1e; /* the seperator for the "enter a unique id" and "client info" part */
|
||||
--modal-permissions-container-seperator: #222224; /* the seperator between left and right */
|
||||
|
||||
--modal-permissions-icon-select: #121213;
|
||||
--modal-permissions-icon-select-border: #0d0d0d;
|
||||
--modal-permissions-icon-select-hover: #17171a;
|
||||
--modal-permissions-icon-select-hover-border: #333333;
|
||||
|
||||
--modal-permission-no-permnissions: #18171c;
|
||||
--modal-permissions-table-border: #1e2025;
|
||||
|
||||
--modal-permissions-table-header: #303036;
|
||||
--modal-permissions-table-row-odd: #303036;
|
||||
--modal-permissions-table-row-even: #25252a;
|
||||
--modal-permissions-table-row-hover: #343a47;
|
||||
|
||||
--modal-permissions-table-header-text: #e1e1e1;
|
||||
--modal-permissions-table-row-text: #535455;
|
||||
--modal-permissions-table-entry-active-text: #e1e1e1;
|
||||
--modal-permissions-table-entry-group-text: #e1e1e1;
|
||||
|
||||
--modal-permissions-table-input: #e1e1e1;
|
||||
--modal-permissions-table-input-focus: #3f7dbf;
|
||||
|
||||
/* The host banner */
|
||||
--hostbanner-background: #2e2e2e;
|
||||
|
||||
/* Server Info */
|
||||
--serverinfo-background: #2f2f35;
|
||||
--serverinfo-hostbanner-background: #26222a;
|
||||
|
||||
--serverinfo-group-border: #1f2122;
|
||||
--serverinfo-group-background: #28292b;
|
||||
|
||||
--serverinfo-key: #557edc;
|
||||
--serverinfo-value: #d6d6d7;
|
||||
|
||||
/* Server bandwidth */
|
||||
--serverinfo-bandwidth-upload: #0a5eaa;
|
||||
--serverinfo-bandwidth-download: #9f2712;
|
||||
|
||||
--serverinfo-title: #e3e3e4;
|
||||
--serverinfo-statistics-title: #244c78;
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
@import "mixin";
|
||||
|
||||
html:root {
|
||||
--hostbanner-background: #2e2e2e;
|
||||
}
|
||||
|
||||
.hostbanner {
|
||||
.container-hostbanner {
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
height: 1000px; /* allocate some height to be truncated by the flex :) */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:not(.no-background) {
|
||||
background-color: var(--hostbanner-background);
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
-moz-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.25);
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.25);
|
||||
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.25);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
padding-bottom: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
@include transition(height 0.5s ease-in-out);
|
||||
|
||||
.hostbanner-image-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-height: 0;
|
||||
|
||||
text-align: center;
|
||||
|
||||
&.hostbanner-mode-0 {
|
||||
/* do not adjust */
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.hostbanner-mode-1 {
|
||||
/* do adjust and ignore ration */
|
||||
display: flex;
|
||||
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
> img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.hostbanner-mode-2 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
|
||||
> img {
|
||||
object-fit: contain;
|
||||
max-height: 100%;
|
||||
|
||||
/* "Normal" third more */
|
||||
//max-width: 100%;
|
||||
|
||||
/* better adoptable mode */
|
||||
width: min-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
.htmltag-client, .htmltag-channel {
|
||||
:global {
|
||||
.htmltag-client, .htmltag-channel {
|
||||
color: var(--text);
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
|
@ -16,46 +16,27 @@ html:root {
|
|||
|
||||
--channel-chat-seperator: #1e1e1e;
|
||||
--channel-chat-seperator-selected: #707070;
|
||||
|
||||
--server-log-text: #6e6e6e;
|
||||
--server-log-error: #e62222;
|
||||
--server-log-tree-entry: #d8d8d8;
|
||||
|
||||
--hostbanner-background: #2e2e2e;
|
||||
}
|
||||
|
||||
.hide-small {
|
||||
opacity: 1;
|
||||
transition: opacity $animation_length linear;
|
||||
}
|
||||
|
||||
.show-small {
|
||||
display: none;
|
||||
|
||||
opacity: 0;
|
||||
transition: opacity $animation_length linear;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
:global {
|
||||
/* Still in use for the global hang in point */
|
||||
.app-container {
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
overflow: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $small_device) {
|
||||
.hide-small {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: opacity $animation_length linear;
|
||||
}
|
||||
|
||||
.show-small {
|
||||
display: block !important;
|
||||
|
||||
opacity: 1 !important;
|
||||
transition: opacity $animation_length linear;
|
||||
}
|
||||
}
|
||||
|
||||
$animation_seperator_length: .1s;
|
||||
.container-seperator {
|
||||
$animation_seperator_length: .1s;
|
||||
.container-seperator {
|
||||
@include transition(all $animation_seperator_length ease-in-out);
|
||||
background: var(--channel-chat-seperator);
|
||||
|
||||
|
@ -81,12 +62,13 @@ $animation_seperator_length: .1s;
|
|||
|
||||
background-color: var(--channel-chat-seperator-selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html, body {
|
||||
html, body {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
body {
|
||||
background: var(--app-background)!important;
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
@import "mixin";
|
||||
|
||||
.top-menu-bar {
|
||||
@include user-select(none);
|
||||
|
||||
height: 1.5em;
|
||||
width: 100%;
|
||||
|
||||
background: #fafafa;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 201;
|
||||
|
||||
font-family: Arial, serif;
|
||||
|
||||
.container-menu-item {
|
||||
position: relative;
|
||||
|
||||
.menu-item {
|
||||
cursor: pointer;
|
||||
|
||||
padding-left: .4em;
|
||||
padding-right: .4em;
|
||||
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: max-content;
|
||||
|
||||
> * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.container-icon {
|
||||
height: 1.2em;
|
||||
width: 1.2em;
|
||||
padding: .1em;
|
||||
font-size: 1em;
|
||||
|
||||
margin-right: .2em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.container-label {
|
||||
display: inline-block;
|
||||
align-self: center;
|
||||
|
||||
a {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover:not(.disabled) {
|
||||
background-color: rgba(0, 0, 0, 0.27);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background-color: rgba(0, 0, 0, 0.13);
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sub-menu {
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
|
||||
background: white;
|
||||
position: absolute;
|
||||
|
||||
top: 100%;
|
||||
border: 1px solid black;
|
||||
|
||||
> .container-menu-item {
|
||||
padding-right: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
&.type-side {
|
||||
&.sub-entries:after {
|
||||
position: absolute;
|
||||
|
||||
display: block;
|
||||
content: '>';
|
||||
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
right: .4em;
|
||||
}
|
||||
|
||||
> .sub-menu {
|
||||
top: -1px; /* border */
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> .sub-menu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: rgba(0, 0, 0, 0.27);
|
||||
|
||||
> .sub-menu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .container-menu-item {
|
||||
> .menu-item {
|
||||
.container-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
margin-top: .125em;
|
||||
margin-bottom: .125em;
|
||||
}
|
||||
}
|
|
@ -66,29 +66,29 @@
|
|||
user-select: $mode;
|
||||
}
|
||||
|
||||
@mixin chat-scrollbar() {
|
||||
@mixin chat-scrollbar($width: .5em) {
|
||||
& {
|
||||
/* for moz */
|
||||
scrollbar-color: #353535 #555;
|
||||
scrollbarWidth: .5em;
|
||||
scrollbarWidth: $width;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
border-radius: .25em;
|
||||
border-radius: $width / 2;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: .5em;
|
||||
height: .5em;
|
||||
width: $width;
|
||||
height: $width;
|
||||
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: .25em;
|
||||
border-radius: $width / 2;
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
.modal-about {
|
||||
display: flex!important;
|
||||
flex-direction: row!important;
|
||||
|
||||
text-align: center;
|
||||
color: #999999;
|
||||
|
||||
.container-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container-right {
|
||||
text-align: left;
|
||||
padding-left: 2em;
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
margin-block-start: 0.35em;
|
||||
margin-block-end: 0.35em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25em;
|
||||
margin-block-start: 0.10em;
|
||||
margin-block-end: 0.10em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-block-start: .25em;
|
||||
margin-block-end: .25em;
|
||||
}
|
||||
}
|
||||
|
||||
.version {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
a {
|
||||
width: 50%;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
text-align: right;
|
||||
}
|
||||
.value {
|
||||
padding-left: .25em;
|
||||
|
||||
text-align: left;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
.modal-avatar-list {
|
||||
:global {
|
||||
.modal-avatar-list {
|
||||
display: flex!important;;
|
||||
flex-direction: row!important;;
|
||||
|
||||
|
@ -172,9 +173,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-avatar-upload {
|
||||
.modal-avatar-upload {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
@ -289,10 +290,13 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 40rem) {
|
||||
:global {
|
||||
.modal-avatar-upload .container-preview .previews {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
@import "mixin";
|
||||
@import "properties";
|
||||
|
||||
//TODO: Resize style!
|
||||
.modal-body.modal-ban-client {
|
||||
:global {
|
||||
//TODO: Resize style!
|
||||
.modal-body.modal-ban-client {
|
||||
padding: 0!important;
|
||||
|
||||
display: flex!important;
|
||||
|
@ -290,4 +291,5 @@
|
|||
width: 6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
$category_slide_animation_length: .25s;
|
||||
|
||||
.modal-body.modal-ban-list {
|
||||
:global {
|
||||
.modal-body.modal-ban-list {
|
||||
padding: 0!important;
|
||||
|
||||
display: flex!important;
|
||||
|
@ -806,4 +807,5 @@ $category_slide_animation_length: .25s;
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,498 +0,0 @@
|
|||
@import "properties";
|
||||
@import "mixin";
|
||||
|
||||
.modal .modal-bookmark-create {
|
||||
.property {
|
||||
margin-top: 5px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
.key {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
select, input {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
text-align: right;
|
||||
|
||||
button {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body.modal-bookmarks {
|
||||
padding: 0!important;
|
||||
|
||||
display: flex!important;
|
||||
flex-direction: row!important;
|
||||
justify-content: stretch!important;
|
||||
|
||||
min-width: 30em!important;
|
||||
height: 45em;
|
||||
width: 80em;
|
||||
|
||||
@include user-select(none);
|
||||
|
||||
.container-tooltip {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
position: relative;
|
||||
width: 1.6em;
|
||||
margin-left: .5em;
|
||||
font-size: .9em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
|
||||
align-self: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.input-boxed {
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.left {
|
||||
min-width: 12em;
|
||||
width: 30%;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
padding: .5em;
|
||||
background-color: #212125;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
.title {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
text-align: center;
|
||||
|
||||
font-size: 1.5em;
|
||||
color: #557edc;
|
||||
text-transform: uppercase;
|
||||
|
||||
@include text-dotdotdot();
|
||||
}
|
||||
|
||||
.container-bookmarks {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
min-height: 6em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
overflow: auto;
|
||||
@include chat-scrollbar-vertical();
|
||||
@include chat-scrollbar-horizontal();
|
||||
|
||||
.bookmark, .directory {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
border-radius: $border_radius_middle;
|
||||
padding: .25em .5em;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
.icon-container {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
align-self: center;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
min-width: 5em;
|
||||
align-self: center;
|
||||
|
||||
@include text-dotdotdot();
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #2c2d2f;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: #1a1a1b;
|
||||
}
|
||||
|
||||
.link {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
position: relative;
|
||||
width: 1.5em;
|
||||
|
||||
$line_width: 2px;
|
||||
$color: hsla(0, 0%, 35%, 1);
|
||||
&:not(.hidden) {
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
||||
height: 2.25em; /* connect with the previous one */
|
||||
width: .75em;
|
||||
|
||||
left: .5em; /* icons have a width of 1em */
|
||||
bottom: calc(.75em - #{$line_width / 2});
|
||||
|
||||
border-left: $line_width solid $color;
|
||||
}
|
||||
|
||||
&.connected {
|
||||
&:before {
|
||||
border-bottom: $line_width solid $color;
|
||||
|
||||
border-bottom-left-radius: .3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.link-start {
|
||||
.link.connected {
|
||||
&:before {
|
||||
height: 1.25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.directory {
|
||||
.name {
|
||||
//color: #557edc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
padding-top: .5em;
|
||||
|
||||
button {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-left: .5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
min-width: 25em;
|
||||
width: 30%;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
background-color: #2f2f35;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
.header {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
height: 10em;
|
||||
|
||||
background: url('../../../img/bookmark_background.png'), url('../../img/bookmark_background.png'), url('img/bookmark_background.png') no-repeat;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
|
||||
padding: .5em;
|
||||
|
||||
.container-name {
|
||||
font-size: 2em;
|
||||
color: #fcfcfc;
|
||||
|
||||
@include text-dotdotdot();
|
||||
}
|
||||
|
||||
.container-address {
|
||||
font-size: 1.5em;
|
||||
color: #fcfcfc;
|
||||
|
||||
@include text-dotdotdot();
|
||||
}
|
||||
}
|
||||
|
||||
.container-settings {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-height: 10em;
|
||||
|
||||
padding: .5em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
@include chat-scrollbar-vertical();
|
||||
|
||||
.group {
|
||||
padding: .5em;
|
||||
|
||||
border-radius: .2em;
|
||||
border: 1px solid #1f2122;
|
||||
|
||||
background-color: #28292b;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
> .row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
.key {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
|
||||
width: 15em;
|
||||
min-width: 2em;
|
||||
|
||||
align-self: center;
|
||||
|
||||
color: #557edc;
|
||||
|
||||
text-transform: uppercase;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
min-width: 2em;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
&.info {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.container-image {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
max-width: 15em;
|
||||
max-height: 9em; /* minus one padding */
|
||||
width: 15em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
object-fit: contain;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@include transition(.25s ease-in-out);
|
||||
}
|
||||
|
||||
.container-properties {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
min-width: 23em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
height: inherit;
|
||||
|
||||
.row {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
height: 1.8em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
.key {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
color: #557edc;
|
||||
text-transform: uppercase;
|
||||
align-self: center;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #d6d6d7;
|
||||
align-self: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&.server-region {
|
||||
> div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.country {
|
||||
margin-right: .25em;
|
||||
}
|
||||
}
|
||||
|
||||
.connect-count, .connect-never {
|
||||
display: inline-block;
|
||||
|
||||
color: #7a3131;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-network {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
.container-button {
|
||||
margin-right: 1em;
|
||||
|
||||
flex-shrink: 1;
|
||||
min-width: 5em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
|
||||
button {
|
||||
height: 2.5em;
|
||||
width: 12em;
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
@include text-dotdotdot();
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
padding: .5em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
.button-duplicate {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
button {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
min-width: 2em;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-left: .5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media all and (max-width: 50em) {
|
||||
.modal-body.modal-bookmarks {
|
||||
.container-image {
|
||||
margin: 0!important;
|
||||
max-width: 0!important;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
@import "mixin";
|
||||
@import "properties";
|
||||
|
||||
.modal-body.modal-channel-info {
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
justify-content: stretch!important;
|
||||
|
||||
min-width: 30em!important;
|
||||
max-height: calc(100vh - 10em)!important;
|
||||
padding: 0 !important;
|
||||
|
||||
.row {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
padding-top: 1em;
|
||||
padding-left: .5em;
|
||||
padding-right: .5em;
|
||||
|
||||
.column {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
min-width: 6em;
|
||||
width: 10em;
|
||||
|
||||
margin-right: .5em;
|
||||
margin-left: .5em;
|
||||
|
||||
.title {
|
||||
text-transform: uppercase;
|
||||
color: #557edc;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.value {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&.audio-encrypted {
|
||||
/* looks better */
|
||||
.value {
|
||||
height: 1.6em;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-description {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
min-height: 8em; /* description plus title */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
padding-top: 1em;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
text-transform: uppercase;
|
||||
color: #557edc;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.button-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
margin-top: .1em; /* looks a bit better */
|
||||
margin-left: .5em;
|
||||
border-radius: .2em;
|
||||
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
div {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #313135;
|
||||
}
|
||||
|
||||
@include transition($button_hover_animation_time ease-in-out);
|
||||
}
|
||||
}
|
||||
|
||||
.value {
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
border-radius: 0.2em;
|
||||
border: 1px solid #212324;
|
||||
background-color: #3a3b3f;
|
||||
|
||||
padding: .5em;
|
||||
|
||||
height: max-content;
|
||||
min-height: 6em;
|
||||
max-height: 40em;
|
||||
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
@include chat-scrollbar-vertical();
|
||||
}
|
||||
|
||||
.no-value {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
font-size: 1.25em;
|
||||
height: (6em / 1.25); /* min value height and a bit more */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
text-align: center;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
|
||||
padding: 1em;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
@import "mixin";
|
||||
@import "properties";
|
||||
|
||||
.modal-body.modal-client-info {
|
||||
:global {
|
||||
.modal-body.modal-client-info {
|
||||
padding: 0!important;
|
||||
|
||||
$avatar_size: 12em;
|
||||
|
@ -615,4 +616,5 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,353 +0,0 @@
|
|||
@import "mixin";
|
||||
|
||||
.modal .modal-connect {
|
||||
@include user-select(none);
|
||||
|
||||
font-size: 1rem;
|
||||
max-width: 100000px; /* max 100000px width, else we shrink the modal */
|
||||
padding: 0!important; /* override the default padding */
|
||||
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
justify-content: stretch!important;
|
||||
|
||||
.container-connect-input {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
/* apply the default padding */
|
||||
padding: .75em 24px;
|
||||
|
||||
border-left: 2px solid #0073d4;
|
||||
overflow: hidden;
|
||||
|
||||
> .row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
> *:not(:last-of-type) {
|
||||
margin-right: 3em;
|
||||
}
|
||||
}
|
||||
|
||||
.container-address-password {
|
||||
.container-address {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.container-password {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 4;
|
||||
|
||||
min-width: 21.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.container-profile-manage {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 4;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
.container-select-profile {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
min-width: 14em;
|
||||
|
||||
> .invalid-feedback {
|
||||
width: max-content; /* allow overflow here */
|
||||
}
|
||||
}
|
||||
|
||||
.container-manage {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 4;
|
||||
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.button-manage-profiles {
|
||||
min-width: 7em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.container-nickname {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
padding-top: 1em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
.container-buttons-connect {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
flex-shrink: 1;
|
||||
min-width: 6em;
|
||||
}
|
||||
|
||||
.button-right {
|
||||
min-width: 7em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.button-left {
|
||||
min-width: 14em;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
border-color: #7a7a7a;
|
||||
margin-left: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
.container-last-servers {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
|
||||
min-width: 0;
|
||||
|
||||
|
||||
border: none;
|
||||
border-left: 2px solid #7a7a7a;
|
||||
|
||||
@include transition(max-height .5s ease-in-out, opacity .5s ease-in-out, padding .5s ease-in-out);
|
||||
&.shown {
|
||||
/* apply the default padding */
|
||||
padding: 0 24px 24px;
|
||||
|
||||
max-height: 100%;
|
||||
opacity: 1;
|
||||
|
||||
@include transition(max-height .5s ease-in-out, opacity .5s ease-in-out, padding .5s ease-in-out)
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 0;
|
||||
width: calc(100% + 46px);
|
||||
min-width: 0;
|
||||
|
||||
margin: 0 0 0 -23px;
|
||||
|
||||
padding: 0;
|
||||
|
||||
border: none;
|
||||
border-top: 1px solid #090909;
|
||||
|
||||
margin-bottom: .75em;
|
||||
}
|
||||
|
||||
color: #7a7a7a;
|
||||
|
||||
/* general table class */
|
||||
.table {
|
||||
width: 100em;
|
||||
max-width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
border: none;
|
||||
border-bottom: 1px solid #161618;
|
||||
}
|
||||
|
||||
|
||||
.body {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
.row {
|
||||
cursor: pointer;
|
||||
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
&:hover {
|
||||
background-color: #202022;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: #131315;
|
||||
}
|
||||
}
|
||||
|
||||
.body-empty {
|
||||
height: 3em;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
font-size: 1.25em;
|
||||
color: rgba(121, 121, 121, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
padding-right: .25em;
|
||||
padding-left: .25em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
border-right: 1px solid #161618;
|
||||
}
|
||||
|
||||
> a {
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* connect table */
|
||||
.table {
|
||||
margin-left: -1.5em; /* the delete row */
|
||||
|
||||
.head {
|
||||
margin-left: 1.5em; /* the delete row */
|
||||
.column.delete {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
align-self: center;
|
||||
.country, .icon-container {
|
||||
align-self: center;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
|
||||
@mixin fixed-column($name, $width) {
|
||||
&.#{$name} {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
width: $width;
|
||||
}
|
||||
}
|
||||
|
||||
@include fixed-column(delete, 1.5em);
|
||||
@include fixed-column(password, 5em);
|
||||
@include fixed-column(country-name, 7em);
|
||||
@include fixed-column(clients, 4em);
|
||||
@include fixed-column(connections, 6.5em);
|
||||
|
||||
&.delete {
|
||||
opacity: 0;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
|
||||
text-align: center;
|
||||
@include transition(opacity .25 ease-in-out);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
@include transition(opacity .25 ease-in-out);
|
||||
}
|
||||
}
|
||||
|
||||
&.address {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
&.name {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 55rem) {
|
||||
.modal .modal-connect {
|
||||
min-width: calc(21.25em + 24px * 2)!important;
|
||||
width: 1000em; /* allocate space */
|
||||
|
||||
.container-address-password {
|
||||
.container-password {
|
||||
min-width: unset!important;
|
||||
margin-left: 1em!important;
|
||||
}
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
justify-content: flex-end!important;
|
||||
|
||||
.button-toggle-last-servers {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.container-profile-name {
|
||||
flex-direction: column!important;
|
||||
}
|
||||
|
||||
.container-connect-input {
|
||||
> .row {
|
||||
> div {
|
||||
margin-right: 0!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-last-servers {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
@import "mixin";
|
||||
@import "properties";
|
||||
|
||||
.modal-server-group-assignments {
|
||||
@include user-select(none);
|
||||
|
||||
min-width: 25em!important;
|
||||
max-height: calc(100vh - 10rem)!important;
|
||||
min-height: 10em!important;
|
||||
|
||||
width: 30em!important;
|
||||
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
justify-content: stretch!important;
|
||||
|
||||
background-color: #2f2f35;
|
||||
padding: .5em!important;
|
||||
|
||||
.group-assignment-list {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-height: 6em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
color: #999999;
|
||||
|
||||
a {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
.htmltag-client {
|
||||
display: inline;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.group-list {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
min-height: 4em;
|
||||
|
||||
padding: 3px;
|
||||
overflow-y: auto;
|
||||
|
||||
border: 1px #161616 solid;
|
||||
border-radius: $border_radius_middle;
|
||||
background-color: #28292b;
|
||||
|
||||
@include chat-scrollbar-vertical();
|
||||
|
||||
.group-entry {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
height: 1.75em;
|
||||
|
||||
> * {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
a {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
min-width: 6em;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: .25em;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
align-self: center;
|
||||
margin-right: 4px;
|
||||
margin-left: 2px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
a {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
padding-top: 1em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
|
@ -1,508 +0,0 @@
|
|||
@import "properties";
|
||||
@import "mixin";
|
||||
|
||||
.modal-icon-select {
|
||||
@include user-select(none);
|
||||
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
justify-content: stretch!important;
|
||||
|
||||
width: 50em!important;
|
||||
|
||||
/*
|
||||
.right, .left {
|
||||
.header {
|
||||
text-transform: uppercase;
|
||||
color: #557edc;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
.container-icons {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
> div {
|
||||
width: 50%;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.content, .container-icons-list {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.container-icons-list {
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.container-icons-remote, .container-icons-local {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
background-color: $color_list_background;
|
||||
border: 1px $color_list_border solid;
|
||||
|
||||
border-radius: $border_radius_large;
|
||||
|
||||
padding: .5em;
|
||||
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: baseline;
|
||||
|
||||
&.container-icons-local {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.icon-container, .icon {
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
&.icon-select {
|
||||
.icon-container, .icon {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-radius: .1em;
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-radius: .1em;
|
||||
|
||||
background-color: rgba(0, 51, 0, 0.07);
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
&:hover, &.selected {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
|
||||
margin: -1px 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-loading, .container-no-permissions, .container-error {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
font-size: 1.1em;
|
||||
color: hsla(0, 0%, 40%, 1);
|
||||
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.27);
|
||||
|
||||
cursor: not-allowed;
|
||||
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
|
||||
> a {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.container-loading {
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
.container-error {
|
||||
z-index: 30;
|
||||
}
|
||||
.container-no-permissions {
|
||||
z-index: 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
margin-top: 20px;
|
||||
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
.spacer {
|
||||
min-width: 0;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
button {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
|
||||
width: 8em;
|
||||
min-width: 4em;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.button-select {
|
||||
margin-left: 10px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
a, div {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
> div {
|
||||
font-size: 16px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
margin-left: .5rem;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-select-no-icon {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-icon-upload {
|
||||
@include user-select(none);
|
||||
|
||||
width: 50em;
|
||||
min-width: 300px;
|
||||
|
||||
padding: 0!important;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.container-select {
|
||||
padding: 1em;
|
||||
min-height: 130px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
.container-icons {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
width: min-content;
|
||||
min-width: 150px;
|
||||
min-height: 130px;
|
||||
overflow-y: auto;
|
||||
|
||||
margin-right: 1em;
|
||||
|
||||
background-color: $color_list_background;
|
||||
border: 1px $color_list_border solid;
|
||||
|
||||
border-radius: $border_radius_large;
|
||||
|
||||
padding: .5em;
|
||||
display: block;
|
||||
|
||||
.icon-container {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-radius: .1em;
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-radius: .1em;
|
||||
|
||||
background-color: rgba(0, 51, 0, 0.07);
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
&:hover, &.selected {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
|
||||
margin: -1px 0px;
|
||||
}
|
||||
|
||||
> img {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 2;
|
||||
|
||||
min-width: 50px;
|
||||
max-width: 200px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.buttons-manage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
> button:not(:first-of-type) {
|
||||
margin-top: .5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-upload {
|
||||
-webkit-box-shadow: 0px -5px 5px 0px rgba(0,0,0,0.25);
|
||||
-moz-box-shadow: 0px -5px 5px 0px rgba(0,0,0,0.25);
|
||||
box-shadow: 0px -5px 2px 0px rgba(0, 0, 0, 0.25);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
padding: .5em 1em 1em;
|
||||
|
||||
.container-error, .container-success {
|
||||
width: 100%;
|
||||
min-height: 60px;
|
||||
|
||||
border: 2px solid;
|
||||
border-radius: $border_radius_middle;
|
||||
|
||||
&.container-error {
|
||||
border-color: rgba(128, 0, 0, 0.5);
|
||||
background-color: rgba(128, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
&.container-success {
|
||||
margin-top: .5em;
|
||||
|
||||
border-color: rgba(50, 143, 51, 0.25);
|
||||
background-color: rgba(50, 143, 51, 0.13);
|
||||
}
|
||||
|
||||
padding: .5em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
> * {
|
||||
align-self: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 6em;
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@include transition(.25s ease-in-out);
|
||||
}
|
||||
|
||||
.container-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.container-process {
|
||||
margin-top: .5em;
|
||||
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
|
||||
overflow-y: auto;
|
||||
|
||||
background-color: $color_list_background;
|
||||
border: 1px $color_list_border solid;
|
||||
|
||||
border-radius: $border_radius_large;
|
||||
|
||||
.upload-entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
.container-icon {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
margin: 1px 1px 1px 4px;
|
||||
|
||||
align-self: center;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
> img {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
position: relative;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
min-width: 2em;
|
||||
|
||||
margin: 2px 5px 2px 3px;
|
||||
height: 16px;
|
||||
|
||||
overflow: hidden;
|
||||
font-size: .75rem;
|
||||
|
||||
background-color: #222222;
|
||||
border: 1px solid hsla(0, 0%, 10%, 1);
|
||||
border-radius: .25rem;
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
|
||||
&.bg-danger {
|
||||
background-color: rgba(128, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&.bg-success {
|
||||
background-color: rgba(50, 143, 51, 0.5);
|
||||
}
|
||||
|
||||
@include transition(width 1s ease-in-out, background-color $button_hover_animation_time ease-in-out);
|
||||
}
|
||||
|
||||
.progress-message {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
min-width: 1em;
|
||||
line-height: normal;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
.modal-icon-upload {
|
||||
.container-select {
|
||||
flex-direction: column;
|
||||
|
||||
.container-icons {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
max-width: unset;
|
||||
margin-top: 5px;
|
||||
|
||||
> button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.buttons-manage {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
> button {
|
||||
width: 50%;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-upload {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
@import "mixin";
|
||||
|
||||
.modal-body.modal-identity-improve {
|
||||
:global {
|
||||
.modal-body.modal-identity-improve {
|
||||
padding: 0!important;
|
||||
|
||||
display: flex!important;
|
||||
|
@ -85,9 +86,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body.modal-identity-import {
|
||||
.modal-body.modal-identity-import {
|
||||
padding: 0!important;
|
||||
|
||||
display: flex;
|
||||
|
@ -185,4 +186,5 @@
|
|||
.file-selector {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
@import "mixin";
|
||||
@import "properties";
|
||||
|
||||
.modal-invite {
|
||||
padding: .5em!important;
|
||||
@include user-select(none);
|
||||
|
||||
.general-properties {
|
||||
flex: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
.form-group {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
.container-settings {
|
||||
flex: 0;
|
||||
|
||||
margin-bottom: .5em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
> * {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-left: .5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-output {
|
||||
background-color: $color_list_background;
|
||||
border: 1px $color_list_border solid;
|
||||
|
||||
border-radius: $border_radius_large;
|
||||
|
||||
padding: .5em;
|
||||
min-height: 5em;
|
||||
|
||||
width: 100%;
|
||||
resize: none;
|
||||
|
||||
color: #999999;
|
||||
|
||||
@include user-select(text);
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
margin-top: 5px;
|
||||
|
||||
.icon {
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
.modal-body.modal-keyselect {
|
||||
:global {
|
||||
.modal-body.modal-keyselect {
|
||||
width: max-content!important;
|
||||
|
||||
.body {
|
||||
|
@ -47,4 +48,5 @@
|
|||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
@import "mixin";
|
||||
@import "properties";
|
||||
|
||||
.modal-body.modal-latency {
|
||||
:global {
|
||||
.modal-body.modal-latency {
|
||||
@include user-select(none);
|
||||
@include transform(all $button_hover_animation_time ease-in-out);
|
||||
|
||||
|
@ -81,4 +82,5 @@
|
|||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
@import "mixin";
|
||||
@import "properties";
|
||||
|
||||
.modal-body.modal-music-manage {
|
||||
:global {
|
||||
.modal-body.modal-music-manage {
|
||||
padding: 0 !important;
|
||||
|
||||
display: flex !important;;
|
||||
|
@ -655,9 +656,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip-music-permission-overview {
|
||||
.tooltip-music-permission-overview {
|
||||
padding-left: .25em;
|
||||
padding-right: .25em;
|
||||
text-align: left;
|
||||
|
@ -694,4 +695,5 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,8 @@ html:root {
|
|||
--modal-newcomer-divider: #313135;
|
||||
}
|
||||
|
||||
.modal-body.modal-newcomer {
|
||||
:global {
|
||||
.modal-body.modal-newcomer {
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
justify-content: stretch!important;
|
||||
|
@ -156,4 +157,5 @@ html:root {
|
|||
border-top: 1.25px solid var(--modal-newcomer-divider);
|
||||
padding: .5em;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
html:root {
|
||||
--modal-poke-date: cornflowerblue;
|
||||
--modal-poke-text: #004d00;
|
||||
}
|
||||
|
||||
.container-poke {
|
||||
display: flex!important;;
|
||||
flex-direction: column!important;;
|
||||
|
||||
.container-servers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
.server {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
.server-name {
|
||||
margin-top: 5px;
|
||||
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.poke-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
|
||||
.entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
> * {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.date, .user, .text {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.date {
|
||||
color: var(--modal-poke-date);
|
||||
}
|
||||
.text {
|
||||
color: var(--modal-poke-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
margin-top: 5px;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
.spacer {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.button-close {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
margin-top: 5px;
|
||||
width: 150px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
@import "properties";
|
||||
@import "mixin";
|
||||
|
||||
.query-create {
|
||||
:global {
|
||||
.query-create {
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
|
||||
|
@ -23,9 +24,9 @@
|
|||
margin-top: 5px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.query-created {
|
||||
.query-created {
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
|
||||
|
@ -52,9 +53,9 @@
|
|||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html:root {
|
||||
html:root {
|
||||
--modal-query-title: #e0e0e0;
|
||||
|
||||
--modal-query-list: #28292b;
|
||||
|
@ -68,9 +69,9 @@ html:root {
|
|||
|
||||
--modal-query-key: #557edc;
|
||||
--modal-query-copy-hover: #28292b;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body.modal-query-manage {
|
||||
.modal-body.modal-query-manage {
|
||||
display: flex!important;
|
||||
flex-direction: row!important;
|
||||
justify-content: stretch!important;
|
||||
|
@ -372,4 +373,5 @@ html:root {
|
|||
.container-seperator {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
@import "mixin";
|
||||
@import "properties";
|
||||
|
||||
.modal-body.modal-server-edit {
|
||||
:global {
|
||||
.modal-body.modal-server-edit {
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
justify-content: stretch!important;
|
||||
|
@ -1149,4 +1150,5 @@
|
|||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,263 +0,0 @@
|
|||
@import "mixin";
|
||||
|
||||
html:root {
|
||||
--serverinfo-background: #2f2f35;
|
||||
--serverinfo-hostbanner-background: #26222a;
|
||||
|
||||
--serverinfo-group-border: #1f2122;
|
||||
--serverinfo-group-background: #28292b;
|
||||
|
||||
--serverinfo-key: #557edc;
|
||||
--serverinfo-value: #d6d6d7;
|
||||
}
|
||||
|
||||
.modal-body.modal-server-info {
|
||||
padding: 0!important;
|
||||
width: 55em;
|
||||
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
justify-content: flex-start!important;
|
||||
|
||||
background-color: var(--serverinfo-background);
|
||||
|
||||
.container-tooltip {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
position: relative;
|
||||
width: 1.6em;
|
||||
margin-left: .5em;
|
||||
margin-right: .5em;
|
||||
font-size: .9em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
|
||||
align-self: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.container-top {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
max-height: 9em;
|
||||
//width: 30em; /* set a default width where we have to grow/shrink */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
.container-hostbanner {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background-color: var(--serverinfo-hostbanner-background);
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.container-body {
|
||||
flex-shrink: 1;
|
||||
min-height: 12em; /* 10em + 2 * 1em margin */
|
||||
|
||||
overflow-y: auto;
|
||||
|
||||
@include chat-scrollbar-vertical();
|
||||
}
|
||||
|
||||
.group {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
margin: 1em;
|
||||
padding: .5em;
|
||||
|
||||
border-radius: .2em;
|
||||
border: 1px solid var(--serverinfo-group-border);
|
||||
|
||||
background-color: var(--serverinfo-group-background);
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
height: 10em;
|
||||
max-height: 10em;
|
||||
|
||||
.container-image {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
max-width: 15em;
|
||||
max-height: 9em; /* minus one padding */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
object-fit: contain;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
margin-right: 2em;
|
||||
@include transition(.25s ease-in-out);
|
||||
}
|
||||
|
||||
.container-properties {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
min-width: 20em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
height: inherit;
|
||||
|
||||
.row {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
height: 1.8em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
.key {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
color: var(--serverinfo-key);
|
||||
text-transform: uppercase;
|
||||
align-self: center;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: var(--serverinfo-value);
|
||||
align-self: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.country {
|
||||
display: inline-block;
|
||||
margin-right: .25em;
|
||||
}
|
||||
|
||||
&.server-version {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
a {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-network {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
.container-button {
|
||||
margin-right: 1em;
|
||||
|
||||
flex-shrink: 1e8;
|
||||
min-width: 5em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
|
||||
button {
|
||||
height: 2.5em;
|
||||
width: 12em;
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-width: 10em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.reverse {
|
||||
flex-direction: row-reverse;
|
||||
text-align: right;
|
||||
|
||||
.container-image {
|
||||
margin-right: 0;
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.container-properties {
|
||||
.row {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
margin: 1em;
|
||||
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
button {
|
||||
min-width: 8em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 50em) {
|
||||
.modal-body.modal-server-info {
|
||||
.container-image {
|
||||
margin: 0!important;
|
||||
max-width: 0!important;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
@import "mixin";
|
||||
|
||||
html:root {
|
||||
--serverinfo-bandwidth-upload: #0a5eaa;
|
||||
--serverinfo-bandwidth-download: #9f2712;
|
||||
|
||||
--serverinfo-title: #e3e3e4;
|
||||
--serverinfo-statistics-title: #244c78;
|
||||
}
|
||||
|
||||
.modal-body.modal-server-info-bandwidth {
|
||||
padding: 0!important;
|
||||
width: 55em;
|
||||
|
||||
display: flex!important;
|
||||
flex-direction: column!important;
|
||||
justify-content: flex-start!important;
|
||||
|
||||
background-color: #2f2f35;
|
||||
|
||||
.container-tooltip {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
position: relative;
|
||||
width: 1.6em;
|
||||
margin-left: .5em;
|
||||
font-size: .9em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
|
||||
align-self: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.top {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
margin: 1em;
|
||||
padding: .5em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
height: 12em;
|
||||
max-height: 12em;
|
||||
|
||||
.container-image {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
max-width: 18em;
|
||||
max-height: 11em; /* minus one padding */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
object-fit: contain;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
margin-right: 2em;
|
||||
@include transition(.25s ease-in-out);
|
||||
}
|
||||
|
||||
.container-stats {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
|
||||
min-width: 25em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
|
||||
.statistic {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
> a {
|
||||
font-size: 1.25em;
|
||||
color: var(--serverinfo-title);
|
||||
line-height: normal;
|
||||
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.values {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
> a {
|
||||
font-size: 1.2em;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.upload {
|
||||
color: var(--serverinfo-bandwidth-upload);
|
||||
}
|
||||
|
||||
.download {
|
||||
color: var(--serverinfo-bandwidth-download);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
margin: 1em;
|
||||
padding: .5em;
|
||||
|
||||
border-radius: .2em;
|
||||
border: 1px solid #1f2122;
|
||||
|
||||
background-color: #28292b;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
//height: 15em;
|
||||
//max-height: 10em;
|
||||
|
||||
.statistic {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
.title {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
color: var(--serverinfo-statistics-title);
|
||||
font-size: 1.25em;
|
||||
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.body {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
|
||||
height: 7em;
|
||||
|
||||
.container-canvas {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
min-width: 6em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.values {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
width: 8em;
|
||||
text-align: right;
|
||||
|
||||
.upload {
|
||||
color: var(--serverinfo-bandwidth-upload);
|
||||
}
|
||||
|
||||
.download {
|
||||
color: var(--serverinfo-bandwidth-download);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 50em) {
|
||||
.modal-body.modal-server-info {
|
||||
.container-image {
|
||||
margin: 0!important;
|
||||
max-width: 0!important;
|
||||
}
|
||||
}
|
||||
}
|