Merge branch 'develop' of github.com:thecodingmachine/workadventure
This commit is contained in:
commit
13e854c4c4
15
.github/workflows/end_to_end_tests.yml
vendored
15
.github/workflows/end_to_end_tests.yml
vendored
@ -20,6 +20,15 @@ jobs:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2.0.0"
|
||||
|
||||
- name: "Setup NodeJS"
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '14.x'
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: npm install
|
||||
working-directory: "tests"
|
||||
|
||||
- name: "Setup .env file"
|
||||
run: cp .env.template .env
|
||||
|
||||
@ -27,10 +36,10 @@ jobs:
|
||||
run: sudo chown 1000:1000 -R .
|
||||
|
||||
- name: "Start environment"
|
||||
run: docker-compose up -d
|
||||
run: LIVE_RELOAD=0 docker-compose up -d
|
||||
|
||||
- name: "Wait for environment to build (and downloading testcafe image)"
|
||||
run: (docker-compose -f docker-compose.testcafe.yml pull &) && docker-compose logs -f --tail=0 front | grep -q "Compiled successfully"
|
||||
run: (docker-compose -f docker-compose.testcafe.yml build &) && docker-compose logs -f --tail=0 front | grep -q "Compiled successfully"
|
||||
|
||||
# - name: "temp debug: display logs"
|
||||
# run: docker-compose logs
|
||||
@ -42,7 +51,7 @@ jobs:
|
||||
# run: docker-compose logs -f pusher | grep -q "WorkAdventure starting on port"
|
||||
|
||||
- name: "Run tests"
|
||||
run: docker-compose -f docker-compose.testcafe.yml up --exit-code-from testcafe
|
||||
run: PROJECT_DIR=$(pwd) docker-compose -f docker-compose.testcafe.yml up --exit-code-from testcafe
|
||||
|
||||
- name: Upload failed tests
|
||||
if: ${{ failure() }}
|
||||
|
@ -59,9 +59,43 @@ $ docker-compose exec back yarn run pretty
|
||||
|
||||
WorkAdventure is based on a video game engine (Phaser), and video games are not the easiest programs to unit test.
|
||||
|
||||
Nevertheless, if your code can be unit tested, please provide a unit test (we use Jasmine).
|
||||
Nevertheless, if your code can be unit tested, please provide a unit test (we use Jasmine), or an end-to-end test (we use Testcafe).
|
||||
|
||||
If you are providing a new feature, you should setup a test map in the `maps/tests` directory. The test map should contain
|
||||
some description text describing how to test the feature. Finally, you should modify the `maps/tests/index.html` file
|
||||
to add a reference to your newly created test map.
|
||||
some description text describing how to test the feature.
|
||||
|
||||
* if the features is meant to be manually tested, you should modify the `maps/tests/index.html` file to add a reference
|
||||
to your newly created test map
|
||||
* if the features can be automatically tested, please provide a testcafe test
|
||||
|
||||
#### Running testcafe tests
|
||||
|
||||
End-to-end tests are available in the "/tests" directory.
|
||||
|
||||
To run these tests locally:
|
||||
|
||||
```console
|
||||
$ LIVE_RELOAD=0 docker-compose up -d
|
||||
$ cd tests
|
||||
$ npm install
|
||||
$ npm run test
|
||||
```
|
||||
|
||||
Note: If your tests fail on a Javascript error in "sockjs", this is due to the
|
||||
Webpack live reload. The Webpack live reload feature is conflicting with testcafe. This is why we recommend starting
|
||||
WorkAdventure with the `LIVE_RELOAD=0` environment variable.
|
||||
|
||||
End-to-end tests can take a while to run. To run only one test, use:
|
||||
|
||||
```console
|
||||
$ npm run test -- tests/[name of the test file].ts
|
||||
```
|
||||
|
||||
You can also run the tests inside a container (but you will not have visual feedbacks on your test, so we recommend using
|
||||
the local tests).
|
||||
|
||||
```console
|
||||
$ LIVE_RELOAD=0 docker-compose up -d
|
||||
# Wait 2-3 minutes for the environment to start, then:
|
||||
$ PROJECT_DIR=$(pwd) docker-compose -f docker-compose.testcafe.yml up
|
||||
```
|
||||
|
@ -12,43 +12,52 @@ export class DebugController {
|
||||
|
||||
getDump() {
|
||||
this.App.get("/dump", (res: HttpResponse, req: HttpRequest) => {
|
||||
const query = parse(req.getQuery());
|
||||
(async () => {
|
||||
const query = parse(req.getQuery());
|
||||
|
||||
if (query.token !== ADMIN_API_TOKEN) {
|
||||
return res.writeStatus("401 Unauthorized").end("Invalid token sent!");
|
||||
}
|
||||
if (query.token !== ADMIN_API_TOKEN) {
|
||||
return res.writeStatus("401 Unauthorized").end("Invalid token sent!");
|
||||
}
|
||||
|
||||
return res
|
||||
.writeStatus("200 OK")
|
||||
.writeHeader("Content-Type", "application/json")
|
||||
.end(
|
||||
stringify(socketManager.getWorlds(), (key: unknown, value: unknown) => {
|
||||
if (key === "listeners") {
|
||||
return "Listeners";
|
||||
}
|
||||
if (key === "socket") {
|
||||
return "Socket";
|
||||
}
|
||||
if (key === "batchedMessages") {
|
||||
return "BatchedMessages";
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
const obj: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
for (const [mapKey, mapValue] of value.entries()) {
|
||||
obj[mapKey] = mapValue;
|
||||
return res
|
||||
.writeStatus("200 OK")
|
||||
.writeHeader("Content-Type", "application/json")
|
||||
.end(
|
||||
stringify(
|
||||
await Promise.all(socketManager.getWorlds().values()),
|
||||
(key: unknown, value: unknown) => {
|
||||
if (key === "listeners") {
|
||||
return "Listeners";
|
||||
}
|
||||
if (key === "socket") {
|
||||
return "Socket";
|
||||
}
|
||||
if (key === "batchedMessages") {
|
||||
return "BatchedMessages";
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
const obj: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
for (const [mapKey, mapValue] of value.entries()) {
|
||||
obj[mapKey] = mapValue;
|
||||
}
|
||||
return obj;
|
||||
} else if (value instanceof Set) {
|
||||
const obj: Array<unknown> = [];
|
||||
for (const [setKey, setValue] of value.entries()) {
|
||||
obj.push(setValue);
|
||||
}
|
||||
return obj;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
} else if (value instanceof Set) {
|
||||
const obj: Array<unknown> = [];
|
||||
for (const [setKey, setValue] of value.entries()) {
|
||||
obj.push(setValue);
|
||||
}
|
||||
return obj;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
})
|
||||
);
|
||||
)
|
||||
);
|
||||
})().catch((e) => {
|
||||
console.error(e);
|
||||
res.writeStatus("500");
|
||||
res.end("An error occurred");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,19 @@
|
||||
version: "3"
|
||||
version: "3.5"
|
||||
services:
|
||||
testcafe:
|
||||
image: testcafe/testcafe:1.17.1
|
||||
working_dir: /tests
|
||||
build: tests/
|
||||
working_dir: /project/tests
|
||||
command:
|
||||
- --dev
|
||||
# Run as root to have the right to access /var/run/docker.sock
|
||||
user: root
|
||||
environment:
|
||||
BROWSER: "chromium --use-fake-device-for-media-stream"
|
||||
PROJECT_DIR: ${PROJECT_DIR}
|
||||
ADMIN_API_TOKEN: ${ADMIN_API_TOKEN}
|
||||
volumes:
|
||||
- ./tests:/tests
|
||||
- ./:/project
|
||||
- ./maps:/maps
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# security_opt:
|
||||
# - seccomp:unconfined
|
||||
|
@ -1,20 +1,21 @@
|
||||
version: "3"
|
||||
version: "3.5"
|
||||
services:
|
||||
reverse-proxy:
|
||||
image: traefik:v2.0
|
||||
image: traefik:v2.5
|
||||
command:
|
||||
- --api.insecure=true
|
||||
- --providers.docker
|
||||
- --entryPoints.web.address=:80
|
||||
- --entryPoints.websecure.address=:443
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
# The Web UI (enabled by --api.insecure=true)
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
- back
|
||||
- front
|
||||
#depends_on:
|
||||
# - back
|
||||
# - front
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
networks:
|
||||
@ -51,10 +52,12 @@ services:
|
||||
MAX_USERNAME_LENGTH: "$MAX_USERNAME_LENGTH"
|
||||
DISABLE_ANONYMOUS: "$DISABLE_ANONYMOUS"
|
||||
OPID_LOGIN_SCREEN_PROVIDER: "$OPID_LOGIN_SCREEN_PROVIDER"
|
||||
LIVE_RELOAD: "$LIVE_RELOAD:-true"
|
||||
command: yarn run start
|
||||
volumes:
|
||||
- ./front:/usr/src/app
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.front.rule=Host(`play.workadventure.localhost`)"
|
||||
- "traefik.http.routers.front.entryPoints=web"
|
||||
- "traefik.http.services.front.loadbalancer.server.port=8080"
|
||||
@ -87,6 +90,7 @@ services:
|
||||
volumes:
|
||||
- ./pusher:/usr/src/app
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.pusher.rule=Host(`pusher.workadventure.localhost`)"
|
||||
- "traefik.http.routers.pusher.entryPoints=web"
|
||||
- "traefik.http.services.pusher.loadbalancer.server.port=8080"
|
||||
@ -111,6 +115,7 @@ services:
|
||||
volumes:
|
||||
- ./maps:/var/www/html
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.maps.rule=Host(`maps.workadventure.localhost`)"
|
||||
- "traefik.http.routers.maps.entryPoints=web,traefik"
|
||||
- "traefik.http.services.maps.loadbalancer.server.port=80"
|
||||
@ -142,6 +147,7 @@ services:
|
||||
volumes:
|
||||
- ./back:/usr/src/app
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.back.rule=Host(`api.workadventure.localhost`)"
|
||||
- "traefik.http.routers.back.entryPoints=web"
|
||||
- "traefik.http.services.back.loadbalancer.server.port=8080"
|
||||
@ -160,6 +166,7 @@ services:
|
||||
volumes:
|
||||
- ./uploader:/usr/src/app
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.uploader.rule=Host(`uploader.workadventure.localhost`)"
|
||||
- "traefik.http.routers.uploader.entryPoints=web"
|
||||
- "traefik.http.services.uploader.loadbalancer.server.port=8080"
|
||||
@ -187,6 +194,7 @@ services:
|
||||
redisinsight:
|
||||
image: redislabs/redisinsight:latest
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.redisinsight.rule=Host(`redis.workadventure.localhost`)"
|
||||
- "traefik.http.routers.redisinsight.entryPoints=web"
|
||||
- "traefik.http.services.redisinsight.loadbalancer.server.port=8001"
|
||||
@ -198,6 +206,7 @@ services:
|
||||
icon:
|
||||
image: matthiasluedtke/iconserver:v3.13.0
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.icon.rule=Host(`icon.workadventure.localhost`)"
|
||||
- "traefik.http.routers.icon.entryPoints=web"
|
||||
- "traefik.http.services.icon.loadbalancer.server.port=8080"
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
module.exports = {
|
||||
"root": true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
@ -18,10 +18,18 @@
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
"project": "./tsconfig.json",
|
||||
"extraFileExtensions": [".svelte"]
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
"@typescript-eslint",
|
||||
"svelte3"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.svelte"],
|
||||
"processor": "svelte3/svelte3"
|
||||
}
|
||||
],
|
||||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
@ -34,5 +42,9 @@
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||
"@typescript-eslint/restrict-template-expressions": "off"
|
||||
},
|
||||
"settings": {
|
||||
"svelte3/typescript": true,
|
||||
"svelte3/ignore-styles": () => true
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4
|
||||
"tabWidth": 4,
|
||||
"plugins": ["prettier-plugin-svelte"]
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
"@typescript-eslint/parser": "^4.23.0",
|
||||
"css-loader": "^5.2.4",
|
||||
"eslint": "^7.26.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"eslint-plugin-svelte3": "^3.2.1",
|
||||
"fork-ts-checker-webpack-plugin": "^6.2.9",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"jasmine": "^3.5.0",
|
||||
@ -25,6 +25,7 @@
|
||||
"node-polyfill-webpack-plugin": "^1.1.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.3.1",
|
||||
"prettier-plugin-svelte": "^2.5.0",
|
||||
"sass": "^1.32.12",
|
||||
"sass-loader": "^11.1.0",
|
||||
"svelte": "^3.38.2",
|
||||
@ -56,6 +57,7 @@
|
||||
"queue-typescript": "^1.0.1",
|
||||
"quill": "1.3.6",
|
||||
"quill-delta-to-html": "^0.12.0",
|
||||
"retry-axios": "^2.6.0",
|
||||
"rxjs": "^6.6.3",
|
||||
"sanitize-html": "^2.5.0",
|
||||
"simple-peer": "^9.11.0",
|
||||
@ -70,17 +72,21 @@
|
||||
"build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack",
|
||||
"build-typings": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production BUILD_TYPINGS=1 webpack",
|
||||
"test": "cross-env TS_NODE_PROJECT=\"tsconfig-for-jasmine.json\" ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
|
||||
"lint": "node_modules/.bin/eslint src/ . --ext .ts",
|
||||
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts",
|
||||
"lint": "node_modules/.bin/eslint src/ tests/ --ext .ts,.svelte",
|
||||
"fix": "node_modules/.bin/eslint --fix src/ tests/ --ext .ts,.svelte",
|
||||
"precommit": "lint-staged",
|
||||
"svelte-check-watch": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore,a11y-media-has-caption:ignore\" --watch",
|
||||
"svelte-check": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore,a11y-media-has-caption:ignore\"",
|
||||
"pretty": "yarn prettier --write 'src/**/*.{ts,tsx}'",
|
||||
"pretty-check": "yarn prettier --check 'src/**/*.{ts,tsx}'"
|
||||
"pretty": "yarn prettier --write 'src/**/*.{ts,svelte}'",
|
||||
"pretty-check": "yarn prettier --check 'src/**/*.{ts,svelte}'"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.ts": [
|
||||
"prettier --write"
|
||||
"*.svelte": [
|
||||
"yarn run svelte-check"
|
||||
],
|
||||
"*.{ts,svelte}": [
|
||||
"yarn run fix",
|
||||
"yarn run pretty"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,147 +1,146 @@
|
||||
<script lang="typescript">
|
||||
import MenuIcon from "./Menu/MenuIcon.svelte";
|
||||
import {menuIconVisiblilityStore, menuVisiblilityStore} from "../Stores/MenuStore";
|
||||
import {emoteMenuStore} from "../Stores/EmoteStore";
|
||||
import {enableCameraSceneVisibilityStore} from "../Stores/MediaStore";
|
||||
import { menuIconVisiblilityStore, menuVisiblilityStore } from "../Stores/MenuStore";
|
||||
import { emoteMenuStore } from "../Stores/EmoteStore";
|
||||
import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
|
||||
import CameraControls from "./CameraControls.svelte";
|
||||
import MyCamera from "./MyCamera.svelte";
|
||||
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
|
||||
import {selectCompanionSceneVisibleStore} from "../Stores/SelectCompanionStore";
|
||||
import {selectCharacterSceneVisibleStore} from "../Stores/SelectCharacterStore";
|
||||
import { selectCompanionSceneVisibleStore } from "../Stores/SelectCompanionStore";
|
||||
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
||||
import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte";
|
||||
import {customCharacterSceneVisibleStore} from "../Stores/CustomCharacterStore";
|
||||
import {errorStore} from "../Stores/ErrorStore";
|
||||
import { customCharacterSceneVisibleStore } from "../Stores/CustomCharacterStore";
|
||||
import { errorStore } from "../Stores/ErrorStore";
|
||||
import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte";
|
||||
import LoginScene from "./Login/LoginScene.svelte";
|
||||
import Chat from "./Chat/Chat.svelte";
|
||||
import {loginSceneVisibleStore} from "../Stores/LoginSceneStore";
|
||||
import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
|
||||
import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte";
|
||||
import VisitCard from "./VisitCard/VisitCard.svelte";
|
||||
import {requestVisitCardsStore} from "../Stores/GameStore";
|
||||
import { requestVisitCardsStore } from "../Stores/GameStore";
|
||||
|
||||
import type {Game} from "../Phaser/Game/Game";
|
||||
import {chatVisibilityStore} from "../Stores/ChatStore";
|
||||
import {helpCameraSettingsVisibleStore} from "../Stores/HelpCameraSettingsStore";
|
||||
import type { Game } from "../Phaser/Game/Game";
|
||||
import { chatVisibilityStore } from "../Stores/ChatStore";
|
||||
import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore";
|
||||
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte";
|
||||
import AudioPlaying from "./UI/AudioPlaying.svelte";
|
||||
import {soundPlayingStore} from "../Stores/SoundPlayingStore";
|
||||
import { soundPlayingStore } from "../Stores/SoundPlayingStore";
|
||||
import ErrorDialog from "./UI/ErrorDialog.svelte";
|
||||
import Menu from "./Menu/Menu.svelte";
|
||||
import EmoteMenu from "./EmoteMenu/EmoteMenu.svelte";
|
||||
import VideoOverlay from "./Video/VideoOverlay.svelte";
|
||||
import {gameOverlayVisibilityStore} from "../Stores/GameOverlayStoreVisibility";
|
||||
import { gameOverlayVisibilityStore } from "../Stores/GameOverlayStoreVisibility";
|
||||
import AdminMessage from "./TypeMessage/BanMessage.svelte";
|
||||
import TextMessage from "./TypeMessage/TextMessage.svelte";
|
||||
import {banMessageVisibleStore} from "../Stores/TypeMessageStore/BanMessageStore";
|
||||
import {textMessageVisibleStore} from "../Stores/TypeMessageStore/TextMessageStore";
|
||||
import {warningContainerStore} from "../Stores/MenuStore";
|
||||
import { banMessageVisibleStore } from "../Stores/TypeMessageStore/BanMessageStore";
|
||||
import { textMessageVisibleStore } from "../Stores/TypeMessageStore/TextMessageStore";
|
||||
import { warningContainerStore } from "../Stores/MenuStore";
|
||||
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
|
||||
import {layoutManagerVisibilityStore} from "../Stores/LayoutManagerStore";
|
||||
import { layoutManagerVisibilityStore } from "../Stores/LayoutManagerStore";
|
||||
import LayoutManager from "./LayoutManager/LayoutManager.svelte";
|
||||
import {audioManagerVisibilityStore} from "../Stores/AudioManagerStore";
|
||||
import AudioManager from "./AudioManager/AudioManager.svelte"
|
||||
import { audioManagerVisibilityStore } from "../Stores/AudioManagerStore";
|
||||
import AudioManager from "./AudioManager/AudioManager.svelte";
|
||||
import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore";
|
||||
import ReportMenu from "./ReportMenu/ReportMenu.svelte";
|
||||
|
||||
export let game: Game;
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if $loginSceneVisibleStore}
|
||||
<div class="scrollable">
|
||||
<LoginScene game={game}></LoginScene>
|
||||
<LoginScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $selectCharacterSceneVisibleStore}
|
||||
<div>
|
||||
<SelectCharacterScene game={ game }></SelectCharacterScene>
|
||||
<SelectCharacterScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $customCharacterSceneVisibleStore}
|
||||
<div>
|
||||
<CustomCharacterScene game={ game }></CustomCharacterScene>
|
||||
<CustomCharacterScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $selectCompanionSceneVisibleStore}
|
||||
<div>
|
||||
<SelectCompanionScene game={ game }></SelectCompanionScene>
|
||||
<SelectCompanionScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $enableCameraSceneVisibilityStore}
|
||||
<div class="scrollable">
|
||||
<EnableCameraScene game={game}></EnableCameraScene>
|
||||
<EnableCameraScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $banMessageVisibleStore}
|
||||
<div>
|
||||
<AdminMessage></AdminMessage>
|
||||
<AdminMessage />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $textMessageVisibleStore}
|
||||
<div>
|
||||
<TextMessage></TextMessage>
|
||||
<TextMessage />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $soundPlayingStore}
|
||||
<div>
|
||||
<AudioPlaying url={$soundPlayingStore} />
|
||||
</div>
|
||||
<div>
|
||||
<AudioPlaying url={$soundPlayingStore} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $audioManagerVisibilityStore}
|
||||
<div>
|
||||
<AudioManager></AudioManager>
|
||||
</div>
|
||||
<div>
|
||||
<AudioManager />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $layoutManagerVisibilityStore}
|
||||
<div>
|
||||
<LayoutManager></LayoutManager>
|
||||
<LayoutManager />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $showReportScreenStore !== userReportEmpty}
|
||||
<div>
|
||||
<ReportMenu></ReportMenu>
|
||||
<ReportMenu />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $menuIconVisiblilityStore}
|
||||
<div>
|
||||
<MenuIcon></MenuIcon>
|
||||
<MenuIcon />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $menuVisiblilityStore}
|
||||
<div>
|
||||
<Menu></Menu>
|
||||
<Menu />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $emoteMenuStore}
|
||||
<div>
|
||||
<EmoteMenu></EmoteMenu>
|
||||
<EmoteMenu />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $gameOverlayVisibilityStore}
|
||||
<div>
|
||||
<VideoOverlay></VideoOverlay>
|
||||
<MyCamera></MyCamera>
|
||||
<CameraControls></CameraControls>
|
||||
<VideoOverlay />
|
||||
<MyCamera />
|
||||
<CameraControls />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $helpCameraSettingsVisibleStore}
|
||||
<div>
|
||||
<HelpCameraSettingsPopup></HelpCameraSettingsPopup>
|
||||
<HelpCameraSettingsPopup />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $requestVisitCardsStore}
|
||||
<VisitCard visitCardUrl={$requestVisitCardsStore}></VisitCard>
|
||||
<VisitCard visitCardUrl={$requestVisitCardsStore} />
|
||||
{/if}
|
||||
{#if $errorStore.length > 0}
|
||||
<div>
|
||||
<ErrorDialog></ErrorDialog>
|
||||
</div>
|
||||
<div>
|
||||
<ErrorDialog />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $chatVisibilityStore}
|
||||
<Chat></Chat>
|
||||
<Chat />
|
||||
{/if}
|
||||
{#if $warningContainerStore}
|
||||
<WarningContainer></WarningContainer>
|
||||
<WarningContainer />
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -1,10 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import type { audioManagerVolume } from "../../Stores/AudioManagerStore";
|
||||
import {
|
||||
audioManagerFileStore,
|
||||
audioManagerVolumeStore,
|
||||
} from "../../Stores/AudioManagerStore";
|
||||
import { audioManagerFileStore, audioManagerVolumeStore } from "../../Stores/AudioManagerStore";
|
||||
import { get } from "svelte/store";
|
||||
import type { Unsubscriber } from "svelte/store";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
@ -41,8 +38,8 @@
|
||||
HTMLAudioPlayer.muted = audioManager.muted;
|
||||
HTMLAudioPlayer.loop = audioManager.loop;
|
||||
updateVolumeUI();
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscriberFileStore) {
|
||||
@ -51,24 +48,24 @@
|
||||
if (unsubscriberVolumeStore) {
|
||||
unsubscriberVolumeStore();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function updateVolumeUI() {
|
||||
if (get(audioManagerVolumeStore).muted) {
|
||||
audioPlayerVolumeIcon.classList.add('muted');
|
||||
audioPlayerVolumeIcon.classList.add("muted");
|
||||
audioPlayerVol.value = "0";
|
||||
} else {
|
||||
let volume = HTMLAudioPlayer.volume;
|
||||
audioPlayerVol.value = "" + volume;
|
||||
audioPlayerVolumeIcon.classList.remove('muted');
|
||||
audioPlayerVolumeIcon.classList.remove("muted");
|
||||
if (volume < 0.3) {
|
||||
audioPlayerVolumeIcon.classList.add('low');
|
||||
audioPlayerVolumeIcon.classList.add("low");
|
||||
} else if (volume < 0.7) {
|
||||
audioPlayerVolumeIcon.classList.remove('low');
|
||||
audioPlayerVolumeIcon.classList.add('mid');
|
||||
audioPlayerVolumeIcon.classList.remove("low");
|
||||
audioPlayerVolumeIcon.classList.add("mid");
|
||||
} else {
|
||||
audioPlayerVolumeIcon.classList.remove('low');
|
||||
audioPlayerVolumeIcon.classList.remove('mid');
|
||||
audioPlayerVolumeIcon.classList.remove("low");
|
||||
audioPlayerVolumeIcon.classList.remove("mid");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -97,45 +94,67 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="main-audio-manager nes-container is-rounded">
|
||||
<div class="audio-manager-player-volume">
|
||||
<span id="audioplayer_volume_icon_playing" alt="player volume" bind:this={audioPlayerVolumeIcon}
|
||||
on:click={onMute}>
|
||||
<svg width="2em" height="2em" viewBox="0 0 16 16" class="bi bi-volume-up" fill="white"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
d="M6.717 3.55A.5.5 0 0 1 7 4v8a.5.5 0 0 1-.812.39L3.825 10.5H1.5A.5.5 0 0 1 1 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06zM6 5.04L4.312 6.39A.5.5 0 0 1 4 6.5H2v3h2a.5.5 0 0 1 .312.11L6 10.96V5.04z" />
|
||||
<span
|
||||
id="audioplayer_volume_icon_playing"
|
||||
alt="player volume"
|
||||
bind:this={audioPlayerVolumeIcon}
|
||||
on:click={onMute}
|
||||
>
|
||||
<svg
|
||||
width="2em"
|
||||
height="2em"
|
||||
viewBox="0 0 16 16"
|
||||
class="bi bi-volume-up"
|
||||
fill="white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M6.717 3.55A.5.5 0 0 1 7 4v8a.5.5 0 0 1-.812.39L3.825 10.5H1.5A.5.5 0 0 1 1 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06zM6 5.04L4.312 6.39A.5.5 0 0 1 4 6.5H2v3h2a.5.5 0 0 1 .312.11L6 10.96V5.04z"
|
||||
/>
|
||||
<g id="audioplayer_volume_icon_playing_high">
|
||||
<path
|
||||
d="M11.536 14.01A8.473 8.473 0 0 0 14.026 8a8.473 8.473 0 0 0-2.49-6.01l-.708.707A7.476 7.476 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303l.708.707z" />
|
||||
d="M11.536 14.01A8.473 8.473 0 0 0 14.026 8a8.473 8.473 0 0 0-2.49-6.01l-.708.707A7.476 7.476 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303l.708.707z"
|
||||
/>
|
||||
</g>
|
||||
<g id="audioplayer_volume_icon_playing_mid">
|
||||
<path
|
||||
d="M10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.483 5.483 0 0 1 11.025 8a5.483 5.483 0 0 1-1.61 3.89l.706.706z" />
|
||||
d="M10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.483 5.483 0 0 1 11.025 8a5.483 5.483 0 0 1-1.61 3.89l.706.706z"
|
||||
/>
|
||||
</g>
|
||||
<g id="audioplayer_volume_icon_playing_low">
|
||||
<path
|
||||
d="M8.707 11.182A4.486 4.486 0 0 0 10.025 8a4.486 4.486 0 0 0-1.318-3.182L8 5.525A3.489 3.489 0 0 1 9.025 8 3.49 3.49 0 0 1 8 10.475l.707.707z" />
|
||||
d="M8.707 11.182A4.486 4.486 0 0 0 10.025 8a4.486 4.486 0 0 0-1.318-3.182L8 5.525A3.489 3.489 0 0 1 9.025 8 3.49 3.49 0 0 1 8 10.475l.707.707z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="range" min="0" max="1" step="0.025" bind:this={audioPlayerVol} on:change={setVolume} on:keydown={disallowKeys}>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.025"
|
||||
bind:this={audioPlayerVol}
|
||||
on:change={setVolume}
|
||||
on:keydown={disallowKeys}
|
||||
/>
|
||||
</div>
|
||||
<div class="audio-manager-reduce-conversation">
|
||||
<label>
|
||||
reduce in conversations
|
||||
<input type="checkbox" bind:checked={decreaseWhileTalking} on:change={setDecrease}>
|
||||
<input type="checkbox" bind:checked={decreaseWhileTalking} on:change={setDecrease} />
|
||||
</label>
|
||||
<section class="audio-manager-file">
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<audio class="audio-manager-audioplayer" bind:this={HTMLAudioPlayer}>
|
||||
<source src={$audioManagerFileStore}>
|
||||
<source src={$audioManagerFileStore} />
|
||||
</audio>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
div.main-audio-manager.nes-container.is-rounded {
|
||||
position: relative;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="typescript">
|
||||
import {requestedScreenSharingState, screenSharingAvailableStore} from "../Stores/ScreenSharingStore";
|
||||
import {isSilentStore, requestedCameraState, requestedMicrophoneState} from "../Stores/MediaStore";
|
||||
import { requestedScreenSharingState, screenSharingAvailableStore } from "../Stores/ScreenSharingStore";
|
||||
import { isSilentStore, requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore";
|
||||
import monitorImg from "./images/monitor.svg";
|
||||
import monitorCloseImg from "./images/monitor-close.svg";
|
||||
import cinemaImg from "./images/cinema.svg";
|
||||
@ -9,10 +9,10 @@
|
||||
import microphoneCloseImg from "./images/microphone-close.svg";
|
||||
import layoutPresentationImg from "./images/layout-presentation.svg";
|
||||
import layoutChatImg from "./images/layout-chat.svg";
|
||||
import {layoutModeStore} from "../Stores/StreamableCollectionStore";
|
||||
import {LayoutMode} from "../WebRtc/LayoutManager";
|
||||
import {peerStore} from "../Stores/PeerStore";
|
||||
import {onDestroy} from "svelte";
|
||||
import { layoutModeStore } from "../Stores/StreamableCollectionStore";
|
||||
import { LayoutMode } from "../WebRtc/LayoutManager";
|
||||
import { peerStore } from "../Stores/PeerStore";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
function screenSharingClick(): void {
|
||||
if (isSilent) return;
|
||||
@ -50,7 +50,7 @@
|
||||
}
|
||||
|
||||
let isSilent: boolean;
|
||||
const unsubscribeIsSilent = isSilentStore.subscribe(value => {
|
||||
const unsubscribeIsSilent = isSilentStore.subscribe((value) => {
|
||||
isSilent = value;
|
||||
});
|
||||
onDestroy(unsubscribeIsSilent);
|
||||
@ -58,36 +58,38 @@
|
||||
|
||||
<div>
|
||||
<div class="btn-cam-action">
|
||||
<div class="btn-layout nes-btn is-dark" on:click={switchLayoutMode} class:hide={$peerStore.size===0}>
|
||||
{#if $layoutModeStore === LayoutMode.Presentation }
|
||||
<img src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode">
|
||||
<div class="btn-layout nes-btn is-dark" on:click={switchLayoutMode} class:hide={$peerStore.size === 0}>
|
||||
{#if $layoutModeStore === LayoutMode.Presentation}
|
||||
<img src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode" />
|
||||
{:else}
|
||||
<img src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode">
|
||||
<img src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="btn-micro nes-btn is-dark" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState ||
|
||||
isSilent}>
|
||||
{#if $requestedMicrophoneState && !isSilent}
|
||||
<img src={microphoneImg} alt="Turn on microphone">
|
||||
<div
|
||||
class="btn-monitor nes-btn is-dark"
|
||||
on:click={screenSharingClick}
|
||||
class:hide={!$screenSharingAvailableStore || isSilent}
|
||||
class:enabled={$requestedScreenSharingState}
|
||||
>
|
||||
{#if $requestedScreenSharingState && !isSilent}
|
||||
<img src={monitorImg} alt="Start screen sharing" />
|
||||
{:else}
|
||||
|
||||
<img src={microphoneCloseImg} alt="Turn off microphone">
|
||||
<img src={monitorCloseImg} alt="Stop screen sharing" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="btn-video nes-btn is-dark" on:click={cameraClick} class:disabled={!$requestedCameraState ||
|
||||
isSilent}>
|
||||
{#if $requestedCameraState && !isSilent}
|
||||
<img src={cinemaImg} alt="Turn on webcam">
|
||||
<img src={cinemaImg} alt="Turn on webcam" />
|
||||
{:else}
|
||||
<img src={cinemaCloseImg} alt="Turn off webcam">
|
||||
<img src={cinemaCloseImg} alt="Turn off webcam" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="btn-monitor nes-btn is-dark" on:click={screenSharingClick} class:hide={!$screenSharingAvailableStore
|
||||
|| isSilent} class:enabled={$requestedScreenSharingState}>
|
||||
{#if $requestedScreenSharingState && !isSilent}
|
||||
<img src={monitorImg} alt="Start screen sharing">
|
||||
<div class="btn-micro nes-btn is-dark" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || isSilent}>
|
||||
{#if $requestedMicrophoneState && !isSilent}
|
||||
<img src={microphoneImg} alt="Turn on microphone" />
|
||||
{:else}
|
||||
<img src={monitorCloseImg} alt="Stop screen sharing">
|
||||
<img src={microphoneCloseImg} alt="Turn off microphone" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,23 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import { fly } from "svelte/transition";
|
||||
import { chatMessagesStore, chatVisibilityStore } from "../../Stores/ChatStore";
|
||||
import ChatMessageForm from './ChatMessageForm.svelte';
|
||||
import ChatElement from './ChatElement.svelte';
|
||||
import {afterUpdate, beforeUpdate, onMount} from "svelte";
|
||||
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
||||
|
||||
import ChatMessageForm from "./ChatMessageForm.svelte";
|
||||
import ChatElement from "./ChatElement.svelte";
|
||||
import { afterUpdate, beforeUpdate, onMount } from "svelte";
|
||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||
|
||||
let listDom: HTMLElement;
|
||||
let chatWindowElement: HTMLElement;
|
||||
let handleFormBlur: { blur():void };
|
||||
let handleFormBlur: { blur(): void };
|
||||
let autoscroll: boolean;
|
||||
|
||||
beforeUpdate(() => {
|
||||
autoscroll = listDom && (listDom.offsetHeight + listDom.scrollTop) > (listDom.scrollHeight - 20);
|
||||
autoscroll = listDom && listDom.offsetHeight + listDom.scrollTop > listDom.scrollHeight - 20;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
listDom.scrollTo(0, listDom.scrollHeight);
|
||||
})
|
||||
});
|
||||
|
||||
afterUpdate(() => {
|
||||
if (autoscroll) listDom.scrollTo(0, listDom.scrollHeight);
|
||||
@ -32,78 +32,81 @@
|
||||
function closeChat() {
|
||||
chatVisibilityStore.set(false);
|
||||
}
|
||||
function onKeyDown(e:KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
closeChat();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} on:click={onClick}/>
|
||||
<svelte:window on:keydown={onKeyDown} on:click={onClick} />
|
||||
|
||||
|
||||
<aside class="chatWindow nes-container is-rounded is-dark" transition:fly="{{ x: -1000, duration: 500 }}" bind:this={chatWindowElement}>
|
||||
<aside class="chatWindow nes-container is-rounded is-dark" transition:fly={{ x: -1000, duration: 500 }}
|
||||
bind:this={chatWindowElement}>
|
||||
<p class="close-icon" on:click={closeChat}>×</p>
|
||||
<section class="messagesList" bind:this={listDom}>
|
||||
<ul>
|
||||
<li><p class="system-text">Here is your chat history: </p></li>
|
||||
{#each $chatMessagesStore as message, i}
|
||||
<li><ChatElement message={message} line={i}></ChatElement></li>
|
||||
{/each}
|
||||
<li><p class="system-text">Here is your chat history:</p></li>
|
||||
{#each $chatMessagesStore as message, i}
|
||||
<li><ChatElement {message} line={i} /></li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
<section class="messageForm">
|
||||
<ChatMessageForm bind:handleForm={handleFormBlur}></ChatMessageForm>
|
||||
<ChatMessageForm bind:handleForm={handleFormBlur} />
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<style lang="scss">
|
||||
p.close-icon {
|
||||
position: absolute;
|
||||
padding: 4px;
|
||||
right: 12px;
|
||||
font-size: 30px;
|
||||
line-height: 25px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
padding: 4px;
|
||||
right: 12px;
|
||||
font-size: 30px;
|
||||
line-height: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
p.system-text {
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
padding:6px;
|
||||
overflow-wrap: break-word;
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
padding: 6px;
|
||||
overflow-wrap: break-word;
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
aside.chatWindow {
|
||||
z-index:100;
|
||||
pointer-events: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width:30vw;
|
||||
min-width: 350px;
|
||||
color: whitesmoke;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
|
||||
.messagesList {
|
||||
margin-top: 35px;
|
||||
overflow-y: auto;
|
||||
flex: auto;
|
||||
z-index: 100;
|
||||
pointer-events: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 30vw;
|
||||
min-width: 350px;
|
||||
color: whitesmoke;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
border-bottom-right-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
|
||||
.messagesList {
|
||||
margin-top: 35px;
|
||||
overflow-y: auto;
|
||||
flex: auto;
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
.messageForm {
|
||||
flex: 0 70px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
}
|
||||
.messageForm {
|
||||
flex: 0 70px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,47 +1,55 @@
|
||||
<script lang="ts">
|
||||
import {ChatMessageTypes} from "../../Stores/ChatStore";
|
||||
import type {ChatMessage} from "../../Stores/ChatStore";
|
||||
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
||||
import ChatPlayerName from './ChatPlayerName.svelte';
|
||||
import type {PlayerInterface} from "../../Phaser/Game/PlayerInterface";
|
||||
import { ChatMessageTypes } from "../../Stores/ChatStore";
|
||||
import type { ChatMessage } from "../../Stores/ChatStore";
|
||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||
import ChatPlayerName from "./ChatPlayerName.svelte";
|
||||
import type { PlayerInterface } from "../../Phaser/Game/PlayerInterface";
|
||||
|
||||
export let message: ChatMessage;
|
||||
export let line: number;
|
||||
const chatStyleLink = "color: white; text-decoration: underline;";
|
||||
|
||||
|
||||
$: author = message.author as PlayerInterface;
|
||||
$: targets = message.targets || [];
|
||||
$: texts = message.text || [];
|
||||
|
||||
|
||||
function urlifyText(text: string): string {
|
||||
return HtmlUtils.urlify(text, chatStyleLink);
|
||||
}
|
||||
function renderDate(date: Date) {
|
||||
return date.toLocaleTimeString(navigator.language, {
|
||||
hour: '2-digit',
|
||||
minute:'2-digit'
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
}
|
||||
function isLastIteration(index: number) {
|
||||
return targets.length -1 === index;
|
||||
return targets.length - 1 === index;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="chatElement">
|
||||
<div class="messagePart">
|
||||
{#if message.type === ChatMessageTypes.userIncoming}
|
||||
>> {#each targets as target, index}<ChatPlayerName player={target} line={line}></ChatPlayerName>{#if !isLastIteration(index)}, {/if}{/each} entered <span class="date">({renderDate(message.date)})</span>
|
||||
>> {#each targets as target, index}<ChatPlayerName
|
||||
player={target}
|
||||
{line}
|
||||
/>{#if !isLastIteration(index)}, {/if}{/each} entered
|
||||
<span class="date">({renderDate(message.date)})</span>
|
||||
{:else if message.type === ChatMessageTypes.userOutcoming}
|
||||
<< {#each targets as target, index}<ChatPlayerName player={target} line={line}></ChatPlayerName>{#if !isLastIteration(index)}, {/if}{/each} left <span class="date">({renderDate(message.date)})</span>
|
||||
<< {#each targets as target, index}<ChatPlayerName
|
||||
player={target}
|
||||
{line}
|
||||
/>{#if !isLastIteration(index)}, {/if}{/each} left
|
||||
<span class="date">({renderDate(message.date)})</span>
|
||||
{:else if message.type === ChatMessageTypes.me}
|
||||
<h4>Me: <span class="date">({renderDate(message.date)})</span></h4>
|
||||
{#each texts as text}
|
||||
<div><p class="my-text nes-balloon from-left is-dark">{@html urlifyText(text)}</p></div>
|
||||
<div><p class="my-text">{@html urlifyText(text)}</p></div>
|
||||
{/each}
|
||||
{:else}
|
||||
<h4><ChatPlayerName player={author} line={line}></ChatPlayerName>: <span class="date">({renderDate(message.date)})</span></h4>
|
||||
<h4><ChatPlayerName player={author} {line} />: <span class="date">({renderDate(message.date)})</span></h4>
|
||||
{#each texts as text}
|
||||
<div><p class="other-text nes-balloon from-right is-dark">{@html urlifyText(text)}</p></div>
|
||||
<div><p class="other-text">{@html urlifyText(text)}</p></div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
@ -49,29 +57,33 @@
|
||||
|
||||
<style lang="scss">
|
||||
div.chatElement {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.messagePart {
|
||||
flex-grow:1;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
|
||||
span.date {
|
||||
font-size: 80%;
|
||||
color: gray;
|
||||
.messagePart {
|
||||
flex-grow: 1;
|
||||
max-width: 100%;
|
||||
|
||||
span.date {
|
||||
font-size: 80%;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
div > p {
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
padding: 6px;
|
||||
overflow-wrap: break-word;
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
&.other-text {
|
||||
background: gray;
|
||||
}
|
||||
|
||||
&.other-text {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div > p {
|
||||
overflow-wrap: break-word;
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
min-width: 75px;
|
||||
padding: 5px;
|
||||
|
||||
&.other-text {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,14 +1,14 @@
|
||||
<script lang="ts">
|
||||
import {chatMessagesStore, chatInputFocusStore} from "../../Stores/ChatStore";
|
||||
import { chatMessagesStore, chatInputFocusStore } from "../../Stores/ChatStore";
|
||||
|
||||
export const handleForm = {
|
||||
blur() {
|
||||
inputElement.blur();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
let inputElement: HTMLElement;
|
||||
let newMessageText = '';
|
||||
|
||||
let newMessageText = "";
|
||||
|
||||
function onFocus() {
|
||||
chatInputFocusStore.set(true);
|
||||
}
|
||||
@ -19,14 +19,14 @@
|
||||
function saveMessage() {
|
||||
if (!newMessageText) return;
|
||||
chatMessagesStore.addPersonnalMessage(newMessageText);
|
||||
newMessageText = '';
|
||||
newMessageText = "";
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={saveMessage}>
|
||||
<input class="nes-input is-dark" type="text" bind:value={newMessageText} placeholder="Enter your message..." on:focus={onFocus} on:blur={onBlur} bind:this={inputElement}>
|
||||
<button class="nes-btn" type="submit">
|
||||
<img src="/static/images/send.png" alt="Send" width="20">
|
||||
<img src="/static/images/send.png" alt="Send" width="20" />
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@ -36,4 +36,4 @@
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type {PlayerInterface} from "../../Phaser/Game/PlayerInterface";
|
||||
import {chatSubMenuVisibilityStore} from "../../Stores/ChatStore";
|
||||
import {onDestroy, onMount} from "svelte";
|
||||
import type {Unsubscriber} from "svelte/store";
|
||||
import type { PlayerInterface } from "../../Phaser/Game/PlayerInterface";
|
||||
import { chatSubMenuVisibilityStore } from "../../Stores/ChatStore";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import type { Unsubscriber } from "svelte/store";
|
||||
import ChatSubMenu from "./ChatSubMenu.svelte";
|
||||
|
||||
export let player: PlayerInterface;
|
||||
@ -17,35 +17,33 @@
|
||||
|
||||
onMount(() => {
|
||||
chatSubMenuVisivilytUnsubcribe = chatSubMenuVisibilityStore.subscribe((newValue) => {
|
||||
isSubMenuOpen = (newValue === player.name + line);
|
||||
})
|
||||
})
|
||||
isSubMenuOpen = newValue === player.name + line;
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
chatSubMenuVisivilytUnsubcribe();
|
||||
})
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<span class="subMenu">
|
||||
<span class="chatPlayerName" style="color: {player.color || 'white'}" on:click={openSubMenu}>
|
||||
{player.name}
|
||||
{player.name}
|
||||
</span>
|
||||
{#if isSubMenuOpen}
|
||||
<ChatSubMenu player={player}/>
|
||||
<ChatSubMenu {player} />
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
span.subMenu {
|
||||
display: inline-block;
|
||||
}
|
||||
span.chatPlayerName {
|
||||
margin-left: 3px;
|
||||
}
|
||||
.chatPlayerName:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
span.subMenu {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
span.chatPlayerName {
|
||||
margin-left: 3px;
|
||||
}
|
||||
.chatPlayerName:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,10 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type {PlayerInterface} from "../../Phaser/Game/PlayerInterface";
|
||||
import {requestVisitCardsStore} from "../../Stores/GameStore";
|
||||
import type { PlayerInterface } from "../../Phaser/Game/PlayerInterface";
|
||||
import { requestVisitCardsStore } from "../../Stores/GameStore";
|
||||
|
||||
export let player: PlayerInterface;
|
||||
|
||||
|
||||
function openVisitCard() {
|
||||
if (player.visitCardUrl) {
|
||||
requestVisitCardsStore.set(player.visitCardUrl);
|
||||
@ -17,17 +16,16 @@
|
||||
<li><button class="text-btn" disabled>Add friend</button></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
ul.selectMenu {
|
||||
background-color: whitesmoke;
|
||||
position: absolute;
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
list-style-type: none;
|
||||
ul.selectMenu {
|
||||
background-color: whitesmoke;
|
||||
position: absolute;
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
list-style-type: none;
|
||||
|
||||
li {
|
||||
text-align: center;
|
||||
li {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="typescript">
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import {CustomizeScene, CustomizeSceneName} from "../../Phaser/Login/CustomizeScene";
|
||||
import {activeRowStore} from "../../Stores/CustomCharacterStore";
|
||||
import { CustomizeScene, CustomizeSceneName } from "../../Phaser/Login/CustomizeScene";
|
||||
import { activeRowStore } from "../../Stores/CustomCharacterStore";
|
||||
|
||||
export let game: Game;
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
function finish() {
|
||||
customCharacterScene.nextSceneToCamera();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<form class="customCharacterScene">
|
||||
@ -38,80 +37,100 @@
|
||||
<h2>Customize your Avatar</h2>
|
||||
</section>
|
||||
<section class="action action-move">
|
||||
<button class="customCharacterSceneButton customCharacterSceneButtonLeft nes-btn" on:click|preventDefault={ selectLeft }> < </button>
|
||||
<button class="customCharacterSceneButton customCharacterSceneButtonRight nes-btn" on:click|preventDefault={ selectRight }> > </button>
|
||||
<button
|
||||
class="customCharacterSceneButton customCharacterSceneButtonLeft nes-btn"
|
||||
on:click|preventDefault={selectLeft}
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<button
|
||||
class="customCharacterSceneButton customCharacterSceneButtonRight nes-btn"
|
||||
on:click|preventDefault={selectRight}
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</section>
|
||||
<section class="action">
|
||||
{#if $activeRowStore === 0}
|
||||
<button type="submit" class="customCharacterSceneFormBack nes-btn" on:click|preventDefault={ previousScene }>Return</button>
|
||||
<button type="submit" class="customCharacterSceneFormBack nes-btn" on:click|preventDefault={previousScene}
|
||||
>Return</button
|
||||
>
|
||||
{/if}
|
||||
{#if $activeRowStore !== 0}
|
||||
<button type="submit" class="customCharacterSceneFormBack nes-btn" on:click|preventDefault={ selectUp }>Back <img src="resources/objects/arrow_up_black.png" alt=""/></button>
|
||||
<button type="submit" class="customCharacterSceneFormBack nes-btn" on:click|preventDefault={selectUp}
|
||||
>Back <img src="resources/objects/arrow_up_black.png" alt="" /></button
|
||||
>
|
||||
{/if}
|
||||
{#if $activeRowStore === 5}
|
||||
<button type="submit" class="customCharacterSceneFormSubmit nes-btn is-primary" on:click|preventDefault={ finish }>Finish</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="customCharacterSceneFormSubmit nes-btn is-primary"
|
||||
on:click|preventDefault={finish}>Finish</button
|
||||
>
|
||||
{/if}
|
||||
{#if $activeRowStore !== 5}
|
||||
<button type="submit" class="customCharacterSceneFormSubmit nes-btn is-primary" on:click|preventDefault={ selectDown }>Next <img src="resources/objects/arrow_down.png" alt=""/></button>
|
||||
<button
|
||||
type="submit"
|
||||
class="customCharacterSceneFormSubmit nes-btn is-primary"
|
||||
on:click|preventDefault={selectDown}>Next <img src="resources/objects/arrow_down.png" alt="" /></button
|
||||
>
|
||||
{/if}
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
form.customCharacterScene {
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
color: #ebeeee;
|
||||
|
||||
section {
|
||||
margin: 10px;
|
||||
|
||||
&.action {
|
||||
text-align: center;
|
||||
margin-top: 55vh;
|
||||
}
|
||||
|
||||
h2 {
|
||||
form.customCharacterScene {
|
||||
font-family: "Press Start 2P";
|
||||
margin: 1px;
|
||||
}
|
||||
pointer-events: auto;
|
||||
color: #ebeeee;
|
||||
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
section {
|
||||
margin: 10px;
|
||||
|
||||
button.customCharacterSceneButton {
|
||||
position: absolute;
|
||||
top: 33vh;
|
||||
margin: 0;
|
||||
}
|
||||
&.action {
|
||||
text-align: center;
|
||||
margin-top: 55vh;
|
||||
}
|
||||
|
||||
button.customCharacterSceneFormBack {
|
||||
color: #292929;
|
||||
}
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button.customCharacterSceneButton {
|
||||
position: absolute;
|
||||
top: 33vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button.customCharacterSceneFormBack {
|
||||
color: #292929;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
&.customCharacterSceneButtonLeft {
|
||||
left: 33vw;
|
||||
}
|
||||
|
||||
&.customCharacterSceneButtonRight {
|
||||
right: 33vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
&.customCharacterSceneButtonLeft {
|
||||
left: 33vw;
|
||||
}
|
||||
|
||||
&.customCharacterSceneButtonRight {
|
||||
right: 33vw;
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
form.customCharacterScene button.customCharacterSceneButtonLeft {
|
||||
left: 5vw;
|
||||
}
|
||||
form.customCharacterScene button.customCharacterSceneButtonRight {
|
||||
right: 5vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
form.customCharacterScene button.customCharacterSceneButtonLeft{
|
||||
left: 5vw;
|
||||
}
|
||||
form.customCharacterScene button.customCharacterSceneButtonRight{
|
||||
right: 5vw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
@ -1,76 +1,72 @@
|
||||
<script lang="typescript">
|
||||
import type { Unsubscriber } from "svelte/store";
|
||||
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { EmojiButton } from "@joeattardi/emoji-button";
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
import type { Unsubscriber } from "svelte/store";
|
||||
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { EmojiButton } from '@joeattardi/emoji-button';
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
let emojiContainer: HTMLElement;
|
||||
let picker: EmojiButton;
|
||||
|
||||
let emojiContainer: HTMLElement;
|
||||
let picker: EmojiButton;
|
||||
let unsubscriber: Unsubscriber | null = null;
|
||||
|
||||
let unsubscriber: Unsubscriber | null = null;
|
||||
onMount(() => {
|
||||
picker = new EmojiButton({
|
||||
rootElement: emojiContainer,
|
||||
styleProperties: {
|
||||
"--font": "Press Start 2P",
|
||||
},
|
||||
emojisPerRow: isMobile() ? 6 : 8,
|
||||
autoFocusSearch: false,
|
||||
style: "twemoji",
|
||||
});
|
||||
//the timeout is here to prevent the menu from flashing
|
||||
setTimeout(() => picker.showPicker(emojiContainer), 100);
|
||||
|
||||
onMount(() => {
|
||||
picker = new EmojiButton({
|
||||
rootElement: emojiContainer,
|
||||
styleProperties: {
|
||||
'--font': 'Press Start 2P'
|
||||
},
|
||||
theme: 'dark',
|
||||
emojisPerRow: isMobile() ? 6 : 8,
|
||||
autoFocusSearch: false,
|
||||
style: 'twemoji',
|
||||
});
|
||||
//the timeout is here to prevent the menu from flashing
|
||||
setTimeout(() => picker.showPicker(emojiContainer), 100);
|
||||
picker.on("emoji", (selection) => {
|
||||
emoteStore.set({
|
||||
unicode: selection.emoji,
|
||||
url: selection.url,
|
||||
name: selection.name,
|
||||
});
|
||||
});
|
||||
|
||||
picker.on("emoji", (selection) => {
|
||||
emoteStore.set({
|
||||
unicode: selection.emoji,
|
||||
url: selection.url,
|
||||
name: selection.name
|
||||
});
|
||||
picker.on("hidden", () => {
|
||||
emoteMenuStore.closeEmoteMenu();
|
||||
});
|
||||
});
|
||||
|
||||
picker.on("hidden", () => {
|
||||
emoteMenuStore.closeEmoteMenu();
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
function onKeyDown(e:KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
emoteMenuStore.closeEmoteMenu();
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscriber) {
|
||||
unsubscriber();
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
emoteMenuStore.closeEmoteMenu();
|
||||
}
|
||||
}
|
||||
|
||||
picker.destroyPicker();
|
||||
})
|
||||
onDestroy(() => {
|
||||
if (unsubscriber) {
|
||||
unsubscriber();
|
||||
}
|
||||
|
||||
picker.destroyPicker();
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown}/>
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
<div class="emote-menu-container">
|
||||
<div class="emote-menu" bind:this={emojiContainer}></div>
|
||||
<div class="emote-menu" bind:this={emojiContainer} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.emote-menu-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.emote-menu-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.emote-menu {
|
||||
pointer-events: all;
|
||||
}
|
||||
.emote-menu {
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,22 +1,22 @@
|
||||
<script lang="typescript">
|
||||
import type {Game} from "../../Phaser/Game/Game";
|
||||
import {EnableCameraScene, EnableCameraSceneName} from "../../Phaser/Login/EnableCameraScene";
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import { EnableCameraScene, EnableCameraSceneName } from "../../Phaser/Login/EnableCameraScene";
|
||||
import {
|
||||
audioConstraintStore,
|
||||
cameraListStore,
|
||||
localStreamStore,
|
||||
microphoneListStore,
|
||||
videoConstraintStore
|
||||
videoConstraintStore,
|
||||
} from "../../Stores/MediaStore";
|
||||
import {onDestroy} from "svelte";
|
||||
import { onDestroy } from "svelte";
|
||||
import HorizontalSoundMeterWidget from "./HorizontalSoundMeterWidget.svelte";
|
||||
import cinemaCloseImg from "../images/cinema-close.svg";
|
||||
import cinemaImg from "../images/cinema.svg";
|
||||
import microphoneImg from "../images/microphone.svg";
|
||||
|
||||
export let game: Game;
|
||||
let selectedCamera : string|undefined = undefined;
|
||||
let selectedMicrophone : string|undefined = undefined;
|
||||
let selectedCamera: string | undefined = undefined;
|
||||
let selectedMicrophone: string | undefined = undefined;
|
||||
|
||||
const enableCameraScene = game.scene.getScene(EnableCameraSceneName) as EnableCameraScene;
|
||||
|
||||
@ -29,16 +29,16 @@
|
||||
return {
|
||||
update(newStream: MediaStream) {
|
||||
if (node.srcObject != newStream) {
|
||||
node.srcObject = newStream
|
||||
node.srcObject = newStream;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let stream: MediaStream | null;
|
||||
|
||||
const unsubscribe = localStreamStore.subscribe(value => {
|
||||
if (value.type === 'success') {
|
||||
const unsubscribe = localStreamStore.subscribe((value) => {
|
||||
if (value.type === "success") {
|
||||
stream = value.stream;
|
||||
|
||||
if (stream !== null) {
|
||||
@ -62,7 +62,7 @@
|
||||
|
||||
function normalizeDeviceName(label: string): string {
|
||||
// remove IDs (that can appear in Chrome, like: "HD Pro Webcam (4df7:4eda)"
|
||||
return label.replace(/(\([[0-9a-f]{4}:[0-9a-f]{4}\))/g, '').trim();
|
||||
return label.replace(/(\([[0-9a-f]{4}:[0-9a-f]{4}\))/g, "").trim();
|
||||
}
|
||||
|
||||
function selectCamera() {
|
||||
@ -72,28 +72,27 @@
|
||||
function selectMicrophone() {
|
||||
audioConstraintStore.setDeviceId(selectedMicrophone);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<form class="enableCameraScene" on:submit|preventDefault={submit}>
|
||||
<section class="text-center">
|
||||
<h2>Turn on your camera and microphone</h2>
|
||||
</section>
|
||||
{#if $localStreamStore.type === 'success' && $localStreamStore.stream}
|
||||
<video class="myCamVideoSetup" use:srcObject={$localStreamStore.stream} autoplay muted playsinline></video>
|
||||
{:else }
|
||||
<div class="webrtcsetup">
|
||||
<img class="background-img" src={cinemaCloseImg} alt="">
|
||||
</div>
|
||||
{/if}
|
||||
<HorizontalSoundMeterWidget stream={stream}></HorizontalSoundMeterWidget>
|
||||
{#if $localStreamStore.type === "success" && $localStreamStore.stream}
|
||||
<video class="myCamVideoSetup" use:srcObject={$localStreamStore.stream} autoplay muted playsinline />
|
||||
{:else}
|
||||
<div class="webrtcsetup">
|
||||
<img class="background-img" src={cinemaCloseImg} alt="" />
|
||||
</div>
|
||||
{/if}
|
||||
<HorizontalSoundMeterWidget {stream} />
|
||||
|
||||
<section class="selectWebcamForm">
|
||||
|
||||
{#if $cameraListStore.length > 1 }
|
||||
{#if $cameraListStore.length > 1}
|
||||
<div class="control-group">
|
||||
<img src={cinemaImg} alt="Camera" />
|
||||
<div class="nes-select is-dark">
|
||||
<!-- svelte-ignore a11y-no-onchange -->
|
||||
<select bind:value={selectedCamera} on:change={selectCamera}>
|
||||
{#each $cameraListStore as camera}
|
||||
<option value={camera.deviceId}>
|
||||
@ -105,10 +104,11 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if $microphoneListStore.length > 1 }
|
||||
{#if $microphoneListStore.length > 1}
|
||||
<div class="control-group">
|
||||
<img src={microphoneImg} alt="Microphone" />
|
||||
<div class="nes-select is-dark">
|
||||
<!-- svelte-ignore a11y-no-onchange -->
|
||||
<select bind:value={selectedMicrophone} on:change={selectMicrophone}>
|
||||
{#each $microphoneListStore as microphone}
|
||||
<option value={microphone.deviceId}>
|
||||
@ -119,111 +119,109 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</section>
|
||||
<section class="action">
|
||||
<button type="submit" class="nes-btn is-primary letsgo" >Let's go!</button>
|
||||
<button type="submit" class="nes-btn is-primary letsgo">Let's go!</button>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
.enableCameraScene {
|
||||
pointer-events: auto;
|
||||
margin: 20px auto 0;
|
||||
color: #ebeeee;
|
||||
pointer-events: auto;
|
||||
margin: 20px auto 0;
|
||||
color: #ebeeee;
|
||||
|
||||
section.selectWebcamForm {
|
||||
margin-top: 3vh;
|
||||
margin-bottom: 3vh;
|
||||
min-height: 10vh;
|
||||
width: 50vw;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
section.selectWebcamForm {
|
||||
margin-top: 3vh;
|
||||
margin-bottom: 3vh;
|
||||
min-height: 10vh;
|
||||
width: 50vw;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
select {
|
||||
font-family: "Press Start 2P";
|
||||
margin-top: 1vh;
|
||||
margin-bottom: 1vh;
|
||||
select {
|
||||
font-family: "Press Start 2P";
|
||||
margin-top: 1vh;
|
||||
margin-bottom: 1vh;
|
||||
}
|
||||
|
||||
option {
|
||||
font-family: "Press Start 2P";
|
||||
}
|
||||
}
|
||||
|
||||
option {
|
||||
section.action {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
}
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
section.action{
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-family: "Press Start 2P";
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
section.text-center{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button.letsgo {
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-height: 60px;
|
||||
margin-top: 10px;
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
margin-right: 10px;
|
||||
section.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.webrtcsetup{
|
||||
margin-top: 2vh;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
height: 28.125vw;
|
||||
width: 50vw;
|
||||
border: white 6px solid;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img.background-img {
|
||||
width: 40%;
|
||||
button.letsgo {
|
||||
font-size: 200%;
|
||||
}
|
||||
}
|
||||
.myCamVideoSetup {
|
||||
margin-top: 2vh;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-height: 50vh;
|
||||
width: 50vw;
|
||||
border: white 6px solid;
|
||||
-webkit-transform: scaleX(-1);
|
||||
transform: scaleX(-1);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.control-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-height: 60px;
|
||||
margin-top: 10px;
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.webrtcsetup {
|
||||
margin-top: 2vh;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
height: 28.125vw;
|
||||
width: 50vw;
|
||||
border: white 6px solid;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img.background-img {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
.myCamVideoSetup {
|
||||
margin-top: 2vh;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-height: 50vh;
|
||||
width: 50vw;
|
||||
border: white 6px solid;
|
||||
-webkit-transform: scaleX(-1);
|
||||
transform: scaleX(-1);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
.enableCameraScene h2 {
|
||||
font-size: 80%;
|
||||
}
|
||||
.enableCameraScene .control-group .nes-select {
|
||||
font-size: 80%;
|
||||
}
|
||||
.enableCameraScene button.letsgo {
|
||||
font-size: 160%;
|
||||
}
|
||||
.enableCameraScene h2 {
|
||||
font-size: 80%;
|
||||
}
|
||||
.enableCameraScene .control-group .nes-select {
|
||||
font-size: 80%;
|
||||
}
|
||||
.enableCameraScene button.letsgo {
|
||||
font-size: 160%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="typescript">
|
||||
import { AudioContext } from 'standardized-audio-context';
|
||||
import {SoundMeter} from "../../Phaser/Components/SoundMeter";
|
||||
import {onDestroy} from "svelte";
|
||||
import { AudioContext } from "standardized-audio-context";
|
||||
import { SoundMeter } from "../../Phaser/Components/SoundMeter";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
export let stream: MediaStream | null;
|
||||
let volume = 0;
|
||||
@ -11,6 +11,7 @@
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
const soundMeter = new SoundMeter();
|
||||
let display = false;
|
||||
let error = false;
|
||||
|
||||
$: {
|
||||
if (stream && stream.getAudioTracks().length > 0) {
|
||||
@ -19,17 +20,19 @@
|
||||
|
||||
if (timeout) {
|
||||
clearInterval(timeout);
|
||||
error = false;
|
||||
}
|
||||
|
||||
timeout = setInterval(() => {
|
||||
try{
|
||||
volume = parseInt((soundMeter.getVolume() / 100 * NB_BARS).toFixed(0));
|
||||
//console.log(volume);
|
||||
}catch(err){
|
||||
|
||||
try {
|
||||
volume = parseInt(((soundMeter.getVolume() / 100) * NB_BARS).toFixed(0));
|
||||
} catch (err) {
|
||||
if (!error) {
|
||||
console.error(err);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
|
||||
} else {
|
||||
display = false;
|
||||
}
|
||||
@ -40,11 +43,10 @@
|
||||
if (timeout) {
|
||||
clearInterval(timeout);
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
function color(i: number, volume: number) {
|
||||
const red = 255 * i / NB_BARS;
|
||||
const red = (255 * i) / NB_BARS;
|
||||
const green = 255 * (1 - i / NB_BARS);
|
||||
|
||||
let alpha = 1;
|
||||
@ -52,31 +54,29 @@
|
||||
alpha = 0.5;
|
||||
}
|
||||
|
||||
return 'background-color:rgba('+red+', '+green+', 0, '+alpha+')';
|
||||
return "background-color:rgba(" + red + ", " + green + ", 0, " + alpha + ")";
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="horizontal-sound-meter" class:active={display}>
|
||||
{#each [...Array(NB_BARS).keys()] as i (i)}
|
||||
<div style={color(i, volume)}></div>
|
||||
<div style={color(i, volume)} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.horizontal-sound-meter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 50%;
|
||||
height: 30px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 1vh;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 50%;
|
||||
height: 30px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 1vh;
|
||||
}
|
||||
|
||||
.horizontal-sound-meter div {
|
||||
margin-left: 5px;
|
||||
flex-grow: 1;
|
||||
margin-left: 5px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<script lang="typescript">
|
||||
import { fly } from 'svelte/transition';
|
||||
import {helpCameraSettingsVisibleStore} from "../../Stores/HelpCameraSettingsStore";
|
||||
import { fly } from "svelte/transition";
|
||||
import { helpCameraSettingsVisibleStore } from "../../Stores/HelpCameraSettingsStore";
|
||||
import firefoxImg from "./images/help-setting-camera-permission-firefox.png";
|
||||
import chromeImg from "./images/help-setting-camera-permission-chrome.png";
|
||||
import {getNavigatorType, isAndroid as isAndroidFct, NavigatorType} from "../../WebRtc/DeviceUtils";
|
||||
import { getNavigatorType, isAndroid as isAndroidFct, NavigatorType } from "../../WebRtc/DeviceUtils";
|
||||
|
||||
let isAndroid = isAndroidFct();
|
||||
let isFirefox = getNavigatorType() === NavigatorType.firefox;
|
||||
@ -16,30 +16,37 @@
|
||||
function close() {
|
||||
helpCameraSettingsVisibleStore.set(false);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<form class="helpCameraSettings nes-container" on:submit|preventDefault={close} transition:fly="{{ y: -900, duration: 500 }}">
|
||||
<form
|
||||
class="helpCameraSettings nes-container"
|
||||
on:submit|preventDefault={close}
|
||||
transition:fly={{ y: -900, duration: 500 }}
|
||||
>
|
||||
<section>
|
||||
<h2>Camera / Microphone access needed</h2>
|
||||
<p class="err">Permission denied</p>
|
||||
<p>You must allow camera and microphone access in your browser.</p>
|
||||
<p>
|
||||
{#if isFirefox }
|
||||
<p class="err">Please click the "Remember this decision" checkbox, if you don't want Firefox to keep asking you the authorization.</p>
|
||||
{#if isFirefox}
|
||||
<p class="err">
|
||||
Please click the "Remember this decision" checkbox, if you don't want Firefox to keep asking you the
|
||||
authorization.
|
||||
</p>
|
||||
<img src={firefoxImg} alt="" />
|
||||
{:else if isChrome && !isAndroid }
|
||||
{:else if isChrome && !isAndroid}
|
||||
<img src={chromeImg} alt="" />
|
||||
{/if}
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<button class="helpCameraSettingsFormRefresh nes-btn" on:click|preventDefault={refresh}>Refresh</button>
|
||||
<button type="submit" class="helpCameraSettingsFormContinue nes-btn is-primary" on:click|preventDefault={close}>Continue without webcam</button>
|
||||
<button type="submit" class="helpCameraSettingsFormContinue nes-btn is-primary" on:click|preventDefault={close}
|
||||
>Continue without webcam</button
|
||||
>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
.helpCameraSettings {
|
||||
pointer-events: auto;
|
||||
@ -53,13 +60,13 @@
|
||||
text-align: center;
|
||||
|
||||
h2 {
|
||||
font-family: 'Press Start 2P';
|
||||
font-family: "Press Start 2P";
|
||||
}
|
||||
|
||||
section {
|
||||
p {
|
||||
margin: 15px;
|
||||
font-family: 'Press Start 2P';
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
& .err {
|
||||
color: #ff0000;
|
||||
|
@ -11,7 +11,6 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="layout-manager-list">
|
||||
{#each $layoutManagerActionStore as action}
|
||||
<div class="nes-container is-dark {action.type}" on:click={() => onClick(action.callback)}>
|
||||
@ -20,39 +19,48 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
div.layout-manager-list {
|
||||
pointer-events: auto;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 40px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: clamp(200px, 20vw, 20vw);
|
||||
div.layout-manager-list {
|
||||
pointer-events: auto;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 40px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: clamp(200px, 20vw, 20vw);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
animation: moveMessage .5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
div.nes-container {
|
||||
padding: 8px 4px;
|
||||
text-align: center;
|
||||
|
||||
&.warning {
|
||||
background-color: #ff9800eb;
|
||||
color: #000;
|
||||
animation: moveMessage 0.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes moveMessage {
|
||||
0% {bottom: 40px;}
|
||||
50% {bottom: 30px;}
|
||||
100% {bottom: 40px;}
|
||||
}
|
||||
div.nes-container {
|
||||
padding: 8px 4px;
|
||||
text-align: center;
|
||||
|
||||
font-family: Lato;
|
||||
color: whitesmoke;
|
||||
background-color: rgb(0, 0, 0, 0.5);
|
||||
|
||||
&.warning {
|
||||
background-color: #ff9800eb;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes moveMessage {
|
||||
0% {
|
||||
bottom: 40px;
|
||||
}
|
||||
50% {
|
||||
bottom: 30px;
|
||||
}
|
||||
100% {
|
||||
bottom: 40px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,22 +1,22 @@
|
||||
<script lang="typescript">
|
||||
import type {Game} from "../../Phaser/Game/Game";
|
||||
import {LoginScene, LoginSceneName} from "../../Phaser/Login/LoginScene";
|
||||
import {DISPLAY_TERMS_OF_USE, MAX_USERNAME_LENGTH} from "../../Enum/EnvironmentVariable";
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import { LoginScene, LoginSceneName } from "../../Phaser/Login/LoginScene";
|
||||
import { DISPLAY_TERMS_OF_USE, MAX_USERNAME_LENGTH } from "../../Enum/EnvironmentVariable";
|
||||
import logoImg from "../images/logo.png";
|
||||
import {gameManager} from "../../Phaser/Game/GameManager";
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
|
||||
export let game: Game;
|
||||
|
||||
const loginScene = game.scene.getScene(LoginSceneName) as LoginScene;
|
||||
|
||||
let name = gameManager.getPlayerName() || '';
|
||||
let name = gameManager.getPlayerName() || "";
|
||||
let startValidating = false;
|
||||
|
||||
function submit() {
|
||||
startValidating = true;
|
||||
|
||||
let finalName = name.trim();
|
||||
if (finalName !== '') {
|
||||
if (finalName !== "") {
|
||||
loginScene.login(finalName);
|
||||
}
|
||||
}
|
||||
@ -29,17 +29,34 @@
|
||||
<section class="text-center">
|
||||
<h2>Enter your name</h2>
|
||||
</section>
|
||||
<input type="text" name="loginSceneName" class="nes-input is-dark" autofocus maxlength={MAX_USERNAME_LENGTH} bind:value={name} on:keypress={() => {startValidating = true}} class:is-error={name.trim() === '' && startValidating} />
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input
|
||||
type="text"
|
||||
name="loginSceneName"
|
||||
class="nes-input is-dark"
|
||||
autofocus
|
||||
maxlength={MAX_USERNAME_LENGTH}
|
||||
bind:value={name}
|
||||
on:keypress={() => {
|
||||
startValidating = true;
|
||||
}}
|
||||
class:is-error={name.trim() === "" && startValidating}
|
||||
/>
|
||||
<section class="error-section">
|
||||
{#if name.trim() === '' && startValidating }
|
||||
<p class="err">The name is empty</p>
|
||||
{/if}
|
||||
{#if name.trim() === "" && startValidating}
|
||||
<p class="err">The name is empty</p>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
{#if DISPLAY_TERMS_OF_USE}
|
||||
<section class="terms-and-conditions">
|
||||
<p>By continuing, you are agreeing our <a href="https://workadventu.re/terms-of-use" target="_blank">terms of use</a>, <a href="https://workadventu.re/privacy-policy" target="_blank">privacy policy</a> and <a href="https://workadventu.re/cookie-policy" target="_blank">cookie policy</a>.</p>
|
||||
</section>
|
||||
<section class="terms-and-conditions">
|
||||
<p>
|
||||
By continuing, you are agreeing our <a href="https://workadventu.re/terms-of-use" target="_blank"
|
||||
>terms of use</a
|
||||
>, <a href="https://workadventu.re/privacy-policy" target="_blank">privacy policy</a> and
|
||||
<a href="https://workadventu.re/cookie-policy" target="_blank">cookie policy</a>.
|
||||
</p>
|
||||
</section>
|
||||
{/if}
|
||||
<section class="action">
|
||||
<button type="submit" class="nes-btn is-primary loginSceneFormSubmit">Continue</button>
|
||||
@ -47,76 +64,75 @@
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
.loginScene {
|
||||
pointer-events: auto;
|
||||
margin: 20px auto 0;
|
||||
width: 90%;
|
||||
color: #ebeeee;
|
||||
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
text-align: center;
|
||||
font-family: "Press Start 2P";
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.terms-and-conditions {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
p.err {
|
||||
color: #ce372b;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
section {
|
||||
margin: 10px;
|
||||
|
||||
&.error-section {
|
||||
min-height: 2rem;
|
||||
margin: 0;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.action {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
.loginScene {
|
||||
pointer-events: auto;
|
||||
margin: 20px auto 0;
|
||||
width: 90%;
|
||||
color: #ebeeee;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
font-weight: 700;
|
||||
}
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
text-align: left;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
input {
|
||||
text-align: center;
|
||||
font-family: "Press Start 2P";
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.terms-and-conditions {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
p.err {
|
||||
color: #ce372b;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
section {
|
||||
margin: 10px;
|
||||
|
||||
&.error-section {
|
||||
min-height: 2rem;
|
||||
margin: 0;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.action {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: #ebeeee;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: left;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
import {onMount} from "svelte";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let gameScene = gameManager.getCurrentGameScene();
|
||||
|
||||
@ -14,29 +14,33 @@
|
||||
|
||||
onMount(() => {
|
||||
if (gameScene.mapFile.properties !== undefined) {
|
||||
const propertyName = gameScene.mapFile.properties.find((property) => property.name === 'mapName')
|
||||
if ( propertyName !== undefined && typeof propertyName.value === 'string') {
|
||||
const propertyName = gameScene.mapFile.properties.find((property) => property.name === "mapName");
|
||||
if (propertyName !== undefined && typeof propertyName.value === "string") {
|
||||
mapName = propertyName.value;
|
||||
}
|
||||
const propertyDescription = gameScene.mapFile.properties.find((property) => property.name === 'mapDescription')
|
||||
if (propertyDescription !== undefined && typeof propertyDescription.value === 'string') {
|
||||
const propertyDescription = gameScene.mapFile.properties.find(
|
||||
(property) => property.name === "mapDescription"
|
||||
);
|
||||
if (propertyDescription !== undefined && typeof propertyDescription.value === "string") {
|
||||
mapDescription = propertyDescription.value;
|
||||
}
|
||||
const propertyCopyright = gameScene.mapFile.properties.find((property) => property.name === 'mapCopyright')
|
||||
if (propertyCopyright !== undefined && typeof propertyCopyright.value === 'string') {
|
||||
const propertyCopyright = gameScene.mapFile.properties.find((property) => property.name === "mapCopyright");
|
||||
if (propertyCopyright !== undefined && typeof propertyCopyright.value === "string") {
|
||||
mapCopyright = propertyCopyright.value;
|
||||
}
|
||||
}
|
||||
|
||||
for (const tileset of gameScene.mapFile.tilesets) {
|
||||
if (tileset.properties !== undefined) {
|
||||
const propertyTilesetCopyright = tileset.properties.find((property) => property.name === 'tilesetCopyright')
|
||||
if (propertyTilesetCopyright !== undefined && typeof propertyTilesetCopyright.value === 'string') {
|
||||
const propertyTilesetCopyright = tileset.properties.find(
|
||||
(property) => property.name === "tilesetCopyright"
|
||||
);
|
||||
if (propertyTilesetCopyright !== undefined && typeof propertyTilesetCopyright.value === "string") {
|
||||
tilesetCopyright = [...tilesetCopyright, propertyTilesetCopyright.value]; //Assignment needed to trigger Svelte's reactivity
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="about-room-main">
|
||||
@ -44,51 +48,58 @@
|
||||
<section class="container-overflow">
|
||||
<h3>{mapName}</h3>
|
||||
<p class="string-HTML">{mapDescription}</p>
|
||||
<h3 class="nes-pointer hoverable" on:click={() => expandedMapCopyright = !expandedMapCopyright}>Copyrights of the map</h3>
|
||||
<p class="string-HTML" hidden="{!expandedMapCopyright}">{mapCopyright}</p>
|
||||
<h3 class="nes-pointer hoverable" on:click={() => expandedTilesetCopyright = !expandedTilesetCopyright}>Copyrights of the tilesets</h3>
|
||||
<section hidden="{!expandedTilesetCopyright}">
|
||||
<h3 class="nes-pointer hoverable" on:click={() => (expandedMapCopyright = !expandedMapCopyright)}>
|
||||
Copyrights of the map
|
||||
</h3>
|
||||
<p class="string-HTML" hidden={!expandedMapCopyright}>{mapCopyright}</p>
|
||||
<h3 class="nes-pointer hoverable" on:click={() => (expandedTilesetCopyright = !expandedTilesetCopyright)}>
|
||||
Copyrights of the tilesets
|
||||
</h3>
|
||||
<section hidden={!expandedTilesetCopyright}>
|
||||
{#each tilesetCopyright as copyright}
|
||||
<p class="string-HTML">{copyright}</p>
|
||||
{:else}
|
||||
<p>The map creator did not declare a copyright for the tilesets. Warning, This doesn't mean that those tilesets have no license.</p>
|
||||
<p>
|
||||
The map creator did not declare a copyright for the tilesets. Warning, This doesn't mean that those
|
||||
tilesets have no license.
|
||||
</p>
|
||||
{/each}
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
.string-HTML{
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
div.about-room-main {
|
||||
height: calc(100% - 56px);
|
||||
|
||||
h2, h3 {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
.string-HTML {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
h3.hoverable:hover {
|
||||
background-color: #3c3e40;
|
||||
border-radius: 32px;
|
||||
}
|
||||
|
||||
section.container-overflow {
|
||||
height: calc(100% - 220px);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
|
||||
div.about-room-main {
|
||||
section.container-overflow {
|
||||
height: calc(100% - 120px);
|
||||
}
|
||||
height: calc(100% - 56px);
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h3.hoverable:hover {
|
||||
background-color: #3c3e40;
|
||||
border-radius: 32px;
|
||||
}
|
||||
|
||||
section.container-overflow {
|
||||
height: calc(100% - 220px);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
|
||||
div.about-room-main {
|
||||
section.container-overflow {
|
||||
height: calc(100% - 120px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -26,31 +26,31 @@
|
||||
const selectedFile = inputAudio.files ? inputAudio.files[0] : null;
|
||||
if (!selectedFile) {
|
||||
errorFile = true;
|
||||
throw 'no file selected';
|
||||
throw "no file selected";
|
||||
}
|
||||
|
||||
const fd = new FormData();
|
||||
fd.append('file', selectedFile);
|
||||
fd.append("file", selectedFile);
|
||||
const res = await gameScene.connection?.uploadAudio(fd);
|
||||
|
||||
const audioGlobalMessage: PlayGlobalMessageInterface = {
|
||||
content: (res as { path: string }).path,
|
||||
type: AUDIO_TYPE,
|
||||
broadcastToWorld: broadcast
|
||||
}
|
||||
inputAudio.value = '';
|
||||
broadcastToWorld: broadcast,
|
||||
};
|
||||
inputAudio.value = "";
|
||||
gameScene.connection?.emitGlobalMessage(audioGlobalMessage);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function inputAudioFile(event: Event) {
|
||||
const eventTarget : EventTargetFiles = (event.target as EventTargetFiles);
|
||||
if(!eventTarget || !eventTarget.files || eventTarget.files.length === 0){
|
||||
const eventTarget: EventTargetFiles = event.target as EventTargetFiles;
|
||||
if (!eventTarget || !eventTarget.files || eventTarget.files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const file = eventTarget.files[0];
|
||||
if(!file) {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -61,52 +61,65 @@
|
||||
|
||||
function getFileSize(number: number) {
|
||||
if (number < 1024) {
|
||||
return number + 'bytes';
|
||||
return number + "bytes";
|
||||
} else if (number >= 1024 && number < 1048576) {
|
||||
return (number / 1024).toFixed(1) + 'KB';
|
||||
return (number / 1024).toFixed(1) + "KB";
|
||||
} else if (number >= 1048576) {
|
||||
return (number / 1048576).toFixed(1) + 'MB';
|
||||
return (number / 1048576).toFixed(1) + "MB";
|
||||
} else {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<section class="section-input-send-audio">
|
||||
<img class="nes-pointer" src="{uploadFile}" alt="Upload a file" on:click|preventDefault={ () => {fileInput.click();}}>
|
||||
<img
|
||||
class="nes-pointer"
|
||||
src={uploadFile}
|
||||
alt="Upload a file"
|
||||
on:click|preventDefault={() => {
|
||||
fileInput.click();
|
||||
}}
|
||||
/>
|
||||
{#if fileName !== undefined}
|
||||
<p>{fileName} : {fileSize}</p>
|
||||
{/if}
|
||||
{#if errorFile}
|
||||
<p class="err">No file selected. You need to upload a file before sending it.</p>
|
||||
{/if}
|
||||
<input type="file" id="input-send-audio" bind:this={fileInput} on:change={(e) => {inputAudioFile(e)}}>
|
||||
<input
|
||||
type="file"
|
||||
id="input-send-audio"
|
||||
bind:this={fileInput}
|
||||
on:change={(e) => {
|
||||
inputAudioFile(e);
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
section.section-input-send-audio {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
section.section-input-send-audio {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
flex: 1 1 auto;
|
||||
max-height: 80%;
|
||||
margin-bottom: 20px;
|
||||
img {
|
||||
flex: 1 1 auto;
|
||||
max-height: 80%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 5px;
|
||||
color: whitesmoke;
|
||||
font-size: 1rem;
|
||||
&.err {
|
||||
color: #ce372b;
|
||||
}
|
||||
}
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
p {
|
||||
margin-bottom: 5px;
|
||||
color: whitesmoke;
|
||||
font-size: 1rem;
|
||||
&.err {
|
||||
color: #ce372b;
|
||||
}
|
||||
}
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
|
||||
function goToGettingStarted() {
|
||||
const sparkHost = "https://workadventu.re/getting-started";
|
||||
window.open(sparkHost, "_blank");
|
||||
@ -10,7 +9,7 @@
|
||||
window.open(sparkHost, "_blank");
|
||||
}
|
||||
|
||||
import {contactPageStore} from "../../Stores/MenuStore";
|
||||
import { contactPageStore } from "../../Stores/MenuStore";
|
||||
</script>
|
||||
|
||||
<div class="create-map-main">
|
||||
@ -18,8 +17,8 @@
|
||||
<section>
|
||||
<h3>Getting started</h3>
|
||||
<p>
|
||||
WorkAdventure allows you to create an online space to communicate spontaneously with others.
|
||||
And it all starts with creating your own space. Choose from a large selection of prefabricated maps by our team.
|
||||
WorkAdventure allows you to create an online space to communicate spontaneously with others. And it all
|
||||
starts with creating your own space. Choose from a large selection of prefabricated maps by our team.
|
||||
</p>
|
||||
<button type="button" class="nes-btn is-primary" on:click={goToGettingStarted}>Getting started</button>
|
||||
</section>
|
||||
@ -30,35 +29,37 @@
|
||||
<button type="button" class="nes-btn" on:click={goToBuildingMap}>Create your map</button>
|
||||
</section>
|
||||
|
||||
<iframe title="contact"
|
||||
src="{$contactPageStore}"
|
||||
allow="clipboard-read; clipboard-write self {$contactPageStore}"
|
||||
allowfullscreen></iframe>
|
||||
<iframe
|
||||
title="contact"
|
||||
src={$contactPageStore}
|
||||
allow="clipboard-read; clipboard-write self {$contactPageStore}"
|
||||
allowfullscreen
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.create-map-main {
|
||||
height: calc(100% - 56px);
|
||||
height: calc(100% - 56px);
|
||||
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
|
||||
section {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
section {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
section.container-overflow {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
section.container-overflow {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: none;
|
||||
height: calc(100% - 56px);
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border: none;
|
||||
height: calc(100% - 56px);
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,33 +1,32 @@
|
||||
<script lang="ts">
|
||||
import {onDestroy, onMount} from "svelte";
|
||||
import {iframeListener} from "../../Api/IframeListener";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { iframeListener } from "../../Api/IframeListener";
|
||||
|
||||
export let url: string;
|
||||
export let allowApi: boolean;
|
||||
|
||||
let HTMLIframe: HTMLIFrameElement;
|
||||
|
||||
onMount( () => {
|
||||
onMount(() => {
|
||||
if (allowApi) {
|
||||
iframeListener.registerIframe(HTMLIframe);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
onDestroy( () => {
|
||||
onDestroy(() => {
|
||||
if (allowApi) {
|
||||
iframeListener.unregisterIframe(HTMLIframe);
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<iframe title="customSubMenu" src="{url}" bind:this={HTMLIframe}></iframe>
|
||||
<iframe title="customSubMenu" src={url} bind:this={HTMLIframe} />
|
||||
|
||||
<style lang="scss">
|
||||
iframe {
|
||||
border: none;
|
||||
height: calc(100% - 56px);
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
iframe {
|
||||
border: none;
|
||||
height: calc(100% - 56px);
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import TextGlobalMessage from './TextGlobalMessage.svelte';
|
||||
import AudioGlobalMessage from './AudioGlobalMessage.svelte';
|
||||
import TextGlobalMessage from "./TextGlobalMessage.svelte";
|
||||
import AudioGlobalMessage from "./AudioGlobalMessage.svelte";
|
||||
|
||||
let handleSendText: { sendTextMessage(broadcast: boolean): void };
|
||||
let handleSendAudio: { sendAudioMessage(broadcast: boolean): Promise<void> };
|
||||
@ -32,23 +32,31 @@
|
||||
<div class="global-message-main">
|
||||
<div class="global-message-subOptions">
|
||||
<section>
|
||||
<button type="button" class="nes-btn {inputSendTextActive ? 'is-disabled' : ''}" on:click|preventDefault={activateInputText}>Text</button>
|
||||
<button
|
||||
type="button"
|
||||
class="nes-btn {inputSendTextActive ? 'is-disabled' : ''}"
|
||||
on:click|preventDefault={activateInputText}>Text</button
|
||||
>
|
||||
</section>
|
||||
<section>
|
||||
<button type="button" class="nes-btn {uploadAudioActive ? 'is-disabled' : ''}" on:click|preventDefault={activateUploadAudio}>Audio</button>
|
||||
<button
|
||||
type="button"
|
||||
class="nes-btn {uploadAudioActive ? 'is-disabled' : ''}"
|
||||
on:click|preventDefault={activateUploadAudio}>Audio</button
|
||||
>
|
||||
</section>
|
||||
</div>
|
||||
<div class="global-message-content">
|
||||
{#if inputSendTextActive}
|
||||
<TextGlobalMessage bind:handleSending={handleSendText}/>
|
||||
<TextGlobalMessage bind:handleSending={handleSendText} />
|
||||
{/if}
|
||||
{#if uploadAudioActive}
|
||||
<AudioGlobalMessage bind:handleSending={handleSendAudio}/>
|
||||
<AudioGlobalMessage bind:handleSending={handleSendAudio} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="global-message-footer">
|
||||
<label>
|
||||
<input type="checkbox" class="nes-checkbox is-dark nes-pointer" bind:checked={broadcastToWorld}>
|
||||
<input type="checkbox" class="nes-checkbox is-dark nes-pointer" bind:checked={broadcastToWorld} />
|
||||
<span>Broadcast to all rooms of the world</span>
|
||||
</label>
|
||||
<section>
|
||||
@ -57,62 +65,59 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
div.global-message-main {
|
||||
height: calc(100% - 50px);
|
||||
display: grid;
|
||||
grid-template-rows: 15% 65% 20%;
|
||||
|
||||
div.global-message-subOptions {
|
||||
height: calc(100% - 50px);
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
margin-bottom: 20px;
|
||||
grid-template-rows: 15% 65% 20%;
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
div.global-message-subOptions {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
margin-bottom: 20px;
|
||||
|
||||
div.global-message-footer {
|
||||
margin-bottom: 10px;
|
||||
|
||||
display: grid;
|
||||
grid-template-rows: 50% 50%;
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
div.global-message-footer {
|
||||
margin-bottom: 10px;
|
||||
|
||||
span {
|
||||
font-family: "Press Start 2P";
|
||||
}
|
||||
display: grid;
|
||||
grid-template-rows: 50% 50%;
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-family: "Press Start 2P";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
|
||||
.global-message-content {
|
||||
height: calc(100% - 5px);
|
||||
}
|
||||
.global-message-footer {
|
||||
margin-bottom: 0;
|
||||
|
||||
label {
|
||||
width: calc(100% - 10px);
|
||||
.global-message-content {
|
||||
height: calc(100% - 5px);
|
||||
}
|
||||
.global-message-footer {
|
||||
margin-bottom: 0;
|
||||
|
||||
label {
|
||||
width: calc(100% - 10px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,18 +1,18 @@
|
||||
<script lang="ts">
|
||||
function copyLink() {
|
||||
const input: HTMLInputElement = document.getElementById('input-share-link') as HTMLInputElement;
|
||||
const input: HTMLInputElement = document.getElementById("input-share-link") as HTMLInputElement;
|
||||
input.focus();
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
document.execCommand("copy");
|
||||
}
|
||||
|
||||
async function shareLink() {
|
||||
const shareData = {url: location.toString()};
|
||||
const shareData = { url: location.toString() };
|
||||
|
||||
try {
|
||||
await navigator.share(shareData);
|
||||
} catch (err) {
|
||||
console.error('Error: ' + err);
|
||||
console.error("Error: " + err);
|
||||
copyLink();
|
||||
}
|
||||
}
|
||||
@ -22,12 +22,12 @@
|
||||
<section class="container-overflow">
|
||||
<section class="share-url not-mobile">
|
||||
<h3>Share the link of the room !</h3>
|
||||
<input type="text" readonly id="input-share-link" value={location.toString()}>
|
||||
<input type="text" readonly id="input-share-link" value={location.toString()} />
|
||||
<button type="button" class="nes-btn is-primary" on:click={copyLink}>Copy</button>
|
||||
</section>
|
||||
<section class="is-mobile">
|
||||
<h3>Share the link of the room !</h3>
|
||||
<input type="hidden" readonly id="input-share-link" value={location.toString()}>
|
||||
<input type="hidden" readonly id="input-share-link" value={location.toString()} />
|
||||
<button type="button" class="nes-btn is-primary" on:click={shareLink}>Share</button>
|
||||
</section>
|
||||
</section>
|
||||
@ -35,41 +35,41 @@
|
||||
|
||||
<style lang="scss">
|
||||
div.guest-main {
|
||||
height: calc(100% - 56px);
|
||||
height: calc(100% - 56px);
|
||||
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
|
||||
section {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
section.container-overflow {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
section.is-mobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 900px), only screen and (max-height: 600px) {
|
||||
div.guest-main {
|
||||
section.share-url.not-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section.is-mobile {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
section {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
section.container-overflow {
|
||||
height: calc(100% - 120px);
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
section.is-mobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@media only screen and (max-width: 900px), only screen and (max-height: 600px) {
|
||||
div.guest-main {
|
||||
section.share-url.not-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section.is-mobile {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
section.container-overflow {
|
||||
height: calc(100% - 120px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,47 +1,47 @@
|
||||
<script lang="typescript">
|
||||
import {fly} from "svelte/transition";
|
||||
import { fly } from "svelte/transition";
|
||||
import SettingsSubMenu from "./SettingsSubMenu.svelte";
|
||||
import ProfileSubMenu from "./ProfileSubMenu.svelte";
|
||||
import WorldsSubMenu from "./WorldsSubMenu.svelte";
|
||||
import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte";
|
||||
import GlobalMessageSubMenu from "./GlobalMessagesSubMenu.svelte";
|
||||
import ContactSubMenu from "./ContactSubMenu.svelte";
|
||||
import CustomSubMenu from "./CustomSubMenu.svelte"
|
||||
import CustomSubMenu from "./CustomSubMenu.svelte";
|
||||
import GuestSubMenu from "./GuestSubMenu.svelte";
|
||||
import {
|
||||
checkSubMenuToShow,
|
||||
customMenuIframe,
|
||||
menuVisiblilityStore,
|
||||
SubMenusInterface,
|
||||
subMenusStore
|
||||
subMenusStore,
|
||||
} from "../../Stores/MenuStore";
|
||||
import {onDestroy, onMount} from "svelte";
|
||||
import {get} from "svelte/store";
|
||||
import type {Unsubscriber} from "svelte/store";
|
||||
import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { get } from "svelte/store";
|
||||
import type { Unsubscriber } from "svelte/store";
|
||||
import { sendMenuClickedEvent } from "../../Api/iframe/Ui/MenuItem";
|
||||
|
||||
let activeSubMenu: string = SubMenusInterface.profile;
|
||||
let activeComponent: typeof ProfileSubMenu | typeof CustomSubMenu = ProfileSubMenu;
|
||||
let props: { url: string, allowApi: boolean };
|
||||
let props: { url: string; allowApi: boolean };
|
||||
let unsubscriberSubMenuStore: Unsubscriber;
|
||||
|
||||
onMount(() => {
|
||||
unsubscriberSubMenuStore = subMenusStore.subscribe(() => {
|
||||
if(!get(subMenusStore).includes(activeSubMenu)) {
|
||||
if (!get(subMenusStore).includes(activeSubMenu)) {
|
||||
switchMenu(SubMenusInterface.profile);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
checkSubMenuToShow();
|
||||
|
||||
switchMenu(SubMenusInterface.profile);
|
||||
})
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if(unsubscriberSubMenuStore) {
|
||||
if (unsubscriberSubMenuStore) {
|
||||
unsubscriberSubMenuStore();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function switchMenu(menu: string) {
|
||||
if (get(subMenusStore).find((subMenu) => subMenu === menu)) {
|
||||
@ -68,7 +68,7 @@
|
||||
case SubMenusInterface.contact:
|
||||
activeComponent = ContactSubMenu;
|
||||
break;
|
||||
default:
|
||||
default: {
|
||||
const customMenu = customMenuIframe.get(menu);
|
||||
if (customMenu !== undefined) {
|
||||
props = { url: customMenu.url, allowApi: customMenu.allowApi };
|
||||
@ -78,109 +78,113 @@
|
||||
menuVisiblilityStore.set(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else throw ("There is no menu called " + menu);
|
||||
} else throw "There is no menu called " + menu;
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
menuVisiblilityStore.set(false);
|
||||
}
|
||||
function onKeyDown(e:KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
closeMenu();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown}/>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
<div class="menu-container-main">
|
||||
<div class="menu-nav-sidebar nes-container is-rounded" transition:fly="{{ x: -1000, duration: 500 }}">
|
||||
<div class="menu-nav-sidebar nes-container is-rounded" transition:fly={{ x: -1000, duration: 500 }}>
|
||||
<h2>Menu</h2>
|
||||
<nav>
|
||||
{#each $subMenusStore as submenu}
|
||||
<button type="button" class="nes-btn {activeSubMenu === submenu ? 'is-disabled' : ''}" on:click|preventDefault={() => switchMenu(submenu)}>
|
||||
<button
|
||||
type="button"
|
||||
class="nes-btn {activeSubMenu === submenu ? 'is-disabled' : ''}"
|
||||
on:click|preventDefault={() => switchMenu(submenu)}
|
||||
>
|
||||
{submenu}
|
||||
</button>
|
||||
{/each}
|
||||
</nav>
|
||||
</div>
|
||||
<div class="menu-submenu-container nes-container is-rounded" transition:fly="{{ y: -1000, duration: 500 }}">
|
||||
<div class="menu-submenu-container nes-container is-rounded" transition:fly={{ y: -1000, duration: 500 }}>
|
||||
<button type="button" class="nes-btn is-error close" on:click={closeMenu}>×</button>
|
||||
<h2>{activeSubMenu}</h2>
|
||||
<svelte:component this={activeComponent} {...props}/>
|
||||
<svelte:component this={activeComponent} {...props} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.nes-container {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
div.menu-container-main {
|
||||
--size-first-columns-grid: 200px;
|
||||
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
height: 80%;
|
||||
width: 75%;
|
||||
top: 10%;
|
||||
|
||||
position: relative;
|
||||
z-index: 80;
|
||||
margin: auto;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: var(--size-first-columns-grid) calc(100% - var(--size-first-columns-grid));
|
||||
grid-template-rows: 100%;
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
.nes-container {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
div.menu-nav-sidebar {
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
nav button {
|
||||
width: calc(100% - 10px);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
div.menu-submenu-container {
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
.nes-btn.is-error.close {
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
right: -20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
div.menu-container-main {
|
||||
--size-first-columns-grid: 120px;
|
||||
height: 70%;
|
||||
top: 55px;
|
||||
width: 100%;
|
||||
font-size: 0.5em;
|
||||
--size-first-columns-grid: 200px;
|
||||
|
||||
div.menu-nav-sidebar {
|
||||
overflow-y: auto;
|
||||
}
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
height: 80%;
|
||||
width: 75%;
|
||||
top: 10%;
|
||||
|
||||
div.menu-submenu-container {
|
||||
.nes-btn.is-error.close {
|
||||
position: absolute;
|
||||
top: -35px;
|
||||
right: 0;
|
||||
position: relative;
|
||||
z-index: 80;
|
||||
margin: auto;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: var(--size-first-columns-grid) calc(100% - var(--size-first-columns-grid));
|
||||
grid-template-rows: 100%;
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
div.menu-nav-sidebar {
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
nav button {
|
||||
width: calc(100% - 10px);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
div.menu-submenu-container {
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
.nes-btn.is-error.close {
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
right: -20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
div.menu-container-main {
|
||||
--size-first-columns-grid: 120px;
|
||||
height: 70%;
|
||||
top: 55px;
|
||||
width: 100%;
|
||||
font-size: 0.5em;
|
||||
|
||||
div.menu-nav-sidebar {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
div.menu-submenu-container {
|
||||
.nes-btn.is-error.close {
|
||||
position: absolute;
|
||||
top: -35px;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,26 +1,26 @@
|
||||
<script lang="typescript">
|
||||
import logoWA from "../images/menu.svg"
|
||||
import logoTalk from "../images/chat.svg"
|
||||
import {menuVisiblilityStore} from "../../Stores/MenuStore";
|
||||
import {chatVisibilityStore} from "../../Stores/ChatStore";
|
||||
import {get} from "svelte/store";
|
||||
import { menuVisiblilityStore } from "../../Stores/MenuStore";
|
||||
import { chatVisibilityStore } from "../../Stores/ChatStore";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
function showMenu(){
|
||||
menuVisiblilityStore.set(!get(menuVisiblilityStore))
|
||||
function showMenu() {
|
||||
menuVisiblilityStore.set(!get(menuVisiblilityStore));
|
||||
}
|
||||
function showChat(){
|
||||
function showChat() {
|
||||
chatVisibilityStore.set(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window/>
|
||||
<svelte:window />
|
||||
|
||||
<main class="menuIcon">
|
||||
<span class=" nes-btn is-dark">
|
||||
<img src={logoWA} alt="open menu" on:click|preventDefault={showMenu}>
|
||||
<span class="nes-btn is-dark">
|
||||
<img src={logoWA} alt="open menu" on:click|preventDefault={showMenu} />
|
||||
</span>
|
||||
<span class=" nes-btn is-dark">
|
||||
<img src={logoTalk} alt="open menu" on:click|preventDefault={showChat}>
|
||||
<span class="nes-btn is-dark">
|
||||
<img src={logoTalk} alt="open menu" on:click|preventDefault={showChat} />
|
||||
</span>
|
||||
</main>
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
margin: 3px
|
||||
}
|
||||
}
|
||||
|
||||
.menuIcon img:hover{
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
@ -1,60 +1,59 @@
|
||||
<script lang="typescript">
|
||||
import {gameManager} from "../../Phaser/Game/GameManager";
|
||||
import {SelectCompanionScene, SelectCompanionSceneName} from "../../Phaser/Login/SelectCompanionScene";
|
||||
import {menuIconVisiblilityStore, menuVisiblilityStore, userIsConnected} from "../../Stores/MenuStore";
|
||||
import {selectCompanionSceneVisibleStore} from "../../Stores/SelectCompanionStore";
|
||||
import {LoginScene, LoginSceneName} from "../../Phaser/Login/LoginScene";
|
||||
import {loginSceneVisibleStore} from "../../Stores/LoginSceneStore";
|
||||
import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore";
|
||||
import {SelectCharacterScene, SelectCharacterSceneName} from "../../Phaser/Login/SelectCharacterScene";
|
||||
import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||
import {PROFILE_URL} from "../../Enum/EnvironmentVariable";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {EnableCameraScene, EnableCameraSceneName} from "../../Phaser/Login/EnableCameraScene";
|
||||
import {enableCameraSceneVisibilityStore} from "../../Stores/MediaStore";
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
import { SelectCompanionScene, SelectCompanionSceneName } from "../../Phaser/Login/SelectCompanionScene";
|
||||
import { menuIconVisiblilityStore, menuVisiblilityStore, userIsConnected } from "../../Stores/MenuStore";
|
||||
import { selectCompanionSceneVisibleStore } from "../../Stores/SelectCompanionStore";
|
||||
import { LoginScene, LoginSceneName } from "../../Phaser/Login/LoginScene";
|
||||
import { loginSceneVisibleStore } from "../../Stores/LoginSceneStore";
|
||||
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
|
||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
|
||||
import { connectionManager } from "../../Connexion/ConnectionManager";
|
||||
import { PROFILE_URL } from "../../Enum/EnvironmentVariable";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { EnableCameraScene, EnableCameraSceneName } from "../../Phaser/Login/EnableCameraScene";
|
||||
import { enableCameraSceneVisibilityStore } from "../../Stores/MediaStore";
|
||||
import btnProfileSubMenuCamera from "../images/btn-menu-profile-camera.svg";
|
||||
import btnProfileSubMenuIdentity from "../images/btn-menu-profile-identity.svg";
|
||||
import btnProfileSubMenuCompanion from "../images/btn-menu-profile-companion.svg";
|
||||
import btnProfileSubMenuWoka from "../images/btn-menu-profile-woka.svg";
|
||||
|
||||
|
||||
function disableMenuStores(){
|
||||
function disableMenuStores() {
|
||||
menuVisiblilityStore.set(false);
|
||||
menuIconVisiblilityStore.set(false);
|
||||
}
|
||||
|
||||
function openEditCompanionScene(){
|
||||
function openEditCompanionScene() {
|
||||
disableMenuStores();
|
||||
selectCompanionSceneVisibleStore.set(true);
|
||||
gameManager.leaveGame(SelectCompanionSceneName,new SelectCompanionScene());
|
||||
gameManager.leaveGame(SelectCompanionSceneName, new SelectCompanionScene());
|
||||
}
|
||||
|
||||
function openEditNameScene(){
|
||||
function openEditNameScene() {
|
||||
disableMenuStores();
|
||||
loginSceneVisibleStore.set(true);
|
||||
gameManager.leaveGame(LoginSceneName,new LoginScene());
|
||||
gameManager.leaveGame(LoginSceneName, new LoginScene());
|
||||
}
|
||||
|
||||
function openEditSkinScene(){
|
||||
function openEditSkinScene() {
|
||||
disableMenuStores();
|
||||
selectCharacterSceneVisibleStore.set(true);
|
||||
gameManager.leaveGame(SelectCharacterSceneName,new SelectCharacterScene());
|
||||
gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene());
|
||||
}
|
||||
|
||||
function logOut(){
|
||||
function logOut() {
|
||||
disableMenuStores();
|
||||
loginSceneVisibleStore.set(true);
|
||||
connectionManager.logout();
|
||||
}
|
||||
|
||||
function getProfileUrl(){
|
||||
function getProfileUrl() {
|
||||
return PROFILE_URL + `?token=${localUserStore.getAuthToken()}`;
|
||||
}
|
||||
|
||||
function openEnableCameraScene(){
|
||||
function openEnableCameraScene() {
|
||||
disableMenuStores();
|
||||
enableCameraSceneVisibilityStore.showEnableCameraScene();
|
||||
gameManager.leaveGame(EnableCameraSceneName,new EnableCameraScene());
|
||||
gameManager.leaveGame(EnableCameraSceneName, new EnableCameraScene());
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -63,20 +62,20 @@
|
||||
<section>
|
||||
<!--
|
||||
<button type="button" class="nes-btn" on:click|preventDefault={openEditNameScene}>
|
||||
<img src={btnProfileSubMenuIdentity} alt="Edit your name">
|
||||
<img src={btnProfileSubMenuIdentity} alt="Edit your name" />
|
||||
<span class="btn-hover">Edit your name</span>
|
||||
</button>
|
||||
-->
|
||||
<button type="button" class="nes-btn" on:click|preventDefault={openEditSkinScene}>
|
||||
<img src={btnProfileSubMenuWoka} alt="Edit your Avatar">
|
||||
<img src={btnProfileSubMenuWoka} alt="Edit your WOKA" />
|
||||
<span class="btn-hover">Edit your Avatar</span>
|
||||
</button>
|
||||
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>
|
||||
<img src={btnProfileSubMenuCompanion} alt="Change your companion">
|
||||
<img src={btnProfileSubMenuCompanion} alt="Edit your companion" />
|
||||
<span class="btn-hover">Change your companion</span>
|
||||
</button>
|
||||
<button type="button" class="nes-btn" on:click|preventDefault={openEnableCameraScene}>
|
||||
<img src={btnProfileSubMenuCamera} alt="Edit your camera">
|
||||
<img src={btnProfileSubMenuCamera} alt="Edit your camera" />
|
||||
<span class="btn-hover">Edit your camera</span>
|
||||
</button>
|
||||
</section>
|
||||
@ -86,7 +85,7 @@
|
||||
{#if $userIsConnected}
|
||||
<section>
|
||||
{#if PROFILE_URL != undefined}
|
||||
<iframe title="profile" src="{getProfileUrl()}"></iframe>
|
||||
<iframe title="profile" src={getProfileUrl()} />
|
||||
{/if}
|
||||
</section>
|
||||
<section>
|
||||
@ -101,68 +100,68 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.customize-main{
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
|
||||
div.submenu{
|
||||
height: 100%;
|
||||
width: 50px;
|
||||
|
||||
button {
|
||||
transition: all .5s ease;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 10px;
|
||||
max-height: 44px;
|
||||
|
||||
img {
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span.btn-hover{
|
||||
display: none;
|
||||
font-family: "Press Start 2P";
|
||||
}
|
||||
|
||||
&:hover{
|
||||
width: auto;
|
||||
|
||||
span.btn-hover {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.content {
|
||||
div.customize-main {
|
||||
width: 100%;
|
||||
section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
display: inline-flex;
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
border: none;
|
||||
}
|
||||
div.submenu {
|
||||
height: 100%;
|
||||
width: 50px;
|
||||
|
||||
button {
|
||||
height: 50px;
|
||||
width: 250px;
|
||||
}
|
||||
button {
|
||||
transition: all 0.5s ease;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 10px;
|
||||
max-height: 44px;
|
||||
|
||||
img {
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span.btn-hover {
|
||||
display: none;
|
||||
font-family: "Press Start 2P";
|
||||
}
|
||||
|
||||
&:hover {
|
||||
width: auto;
|
||||
|
||||
span.btn-hover {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.content {
|
||||
width: 100%;
|
||||
section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
border: none;
|
||||
}
|
||||
|
||||
button {
|
||||
height: 50px;
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
div.customize-main.content section button {
|
||||
width: 130px;
|
||||
}
|
||||
div.customize-main.content section button {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,62 +1,62 @@
|
||||
<script lang="typescript">
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {videoConstraintStore} from "../../Stores/MediaStore";
|
||||
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
||||
import {isMobile} from "../../Enum/EnvironmentVariable";
|
||||
import {menuVisiblilityStore} from "../../Stores/MenuStore";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { videoConstraintStore } from "../../Stores/MediaStore";
|
||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
import { menuVisiblilityStore } from "../../Stores/MenuStore";
|
||||
|
||||
let fullscreen : boolean = localUserStore.getFullscreen();
|
||||
let notification : boolean = localUserStore.getNotification() === 'granted';
|
||||
let valueGame : number = localUserStore.getGameQualityValue();
|
||||
let valueVideo : number = localUserStore.getVideoQualityValue();
|
||||
let previewValueGame = valueGame;
|
||||
let previewValueVideo = valueVideo;
|
||||
let fullscreen: boolean = localUserStore.getFullscreen();
|
||||
let notification: boolean = localUserStore.getNotification() === "granted";
|
||||
let valueGame: number = localUserStore.getGameQualityValue();
|
||||
let valueVideo: number = localUserStore.getVideoQualityValue();
|
||||
let previewValueGame = valueGame;
|
||||
let previewValueVideo = valueVideo;
|
||||
|
||||
function saveSetting(){
|
||||
if (valueGame !== previewValueGame) {
|
||||
previewValueGame = valueGame;
|
||||
localUserStore.setGameQualityValue(valueGame);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
if (valueVideo !== previewValueVideo) {
|
||||
previewValueVideo = valueVideo;
|
||||
videoConstraintStore.setFrameRate(valueVideo);
|
||||
}
|
||||
|
||||
closeMenu();
|
||||
}
|
||||
|
||||
function changeFullscreen() {
|
||||
const body = HtmlUtils.querySelectorOrFail('body');
|
||||
if (body) {
|
||||
if (document.fullscreenElement !== null && !fullscreen) {
|
||||
document.exitFullscreen()
|
||||
} else {
|
||||
body.requestFullscreen();
|
||||
function saveSetting() {
|
||||
if (valueGame !== previewValueGame) {
|
||||
previewValueGame = valueGame;
|
||||
localUserStore.setGameQualityValue(valueGame);
|
||||
window.location.reload();
|
||||
}
|
||||
localUserStore.setFullscreen(fullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
function changeNotification() {
|
||||
if (Notification.permission === 'granted') {
|
||||
localUserStore.setNotification(notification ? 'granted' : 'denied');
|
||||
} else {
|
||||
Notification.requestPermission().then((response) => {
|
||||
if (response === 'granted') {
|
||||
localUserStore.setNotification(notification ? 'granted' : 'denied');
|
||||
if (valueVideo !== previewValueVideo) {
|
||||
previewValueVideo = valueVideo;
|
||||
videoConstraintStore.setFrameRate(valueVideo);
|
||||
}
|
||||
|
||||
closeMenu();
|
||||
}
|
||||
|
||||
function changeFullscreen() {
|
||||
const body = HtmlUtils.querySelectorOrFail("body");
|
||||
if (body) {
|
||||
if (document.fullscreenElement !== null && !fullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else {
|
||||
localUserStore.setNotification('denied');
|
||||
notification = false;
|
||||
body.requestFullscreen();
|
||||
}
|
||||
})
|
||||
localUserStore.setFullscreen(fullscreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
menuVisiblilityStore.set(false);
|
||||
}
|
||||
function changeNotification() {
|
||||
if (Notification.permission === "granted") {
|
||||
localUserStore.setNotification(notification ? "granted" : "denied");
|
||||
} else {
|
||||
Notification.requestPermission().then((response) => {
|
||||
if (response === "granted") {
|
||||
localUserStore.setNotification(notification ? "granted" : "denied");
|
||||
} else {
|
||||
localUserStore.setNotification("denied");
|
||||
notification = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
menuVisiblilityStore.set(false);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="settings-main" on:submit|preventDefault={saveSetting}>
|
||||
@ -64,10 +64,12 @@ function closeMenu() {
|
||||
<h3>Game quality</h3>
|
||||
<div class="nes-select is-dark">
|
||||
<select bind:value={valueGame}>
|
||||
<option value="{120}">{isMobile() ? 'High (120 fps)' : 'High video quality (120 fps)'}</option>
|
||||
<option value="{60}">{isMobile() ? 'Medium (60 fps)' : 'Medium video quality (60 fps, recommended)'}</option>
|
||||
<option value="{40}">{isMobile() ? 'Minimum (40 fps)' : 'Minimum video quality (40 fps)'}</option>
|
||||
<option value="{20}">{isMobile() ? 'Small (20 fps)' : 'Small video quality (20 fps)'}</option>
|
||||
<option value={120}>{isMobile() ? "High (120 fps)" : "High video quality (120 fps)"}</option>
|
||||
<option value={60}
|
||||
>{isMobile() ? "Medium (60 fps)" : "Medium video quality (60 fps, recommended)"}</option
|
||||
>
|
||||
<option value={40}>{isMobile() ? "Minimum (40 fps)" : "Minimum video quality (40 fps)"}</option>
|
||||
<option value={20}>{isMobile() ? "Small (20 fps)" : "Small video quality (20 fps)"}</option>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
@ -75,10 +77,12 @@ function closeMenu() {
|
||||
<h3>Video quality</h3>
|
||||
<div class="nes-select is-dark">
|
||||
<select bind:value={valueVideo}>
|
||||
<option value="{30}">{isMobile() ? 'High (30 fps)' : 'High video quality (30 fps)'}</option>
|
||||
<option value="{20}">{isMobile() ? 'Medium (20 fps)' : 'Medium video quality (20 fps, recommended)'}</option>
|
||||
<option value="{10}">{isMobile() ? 'Minimum (10 fps)' : 'Minimum video quality (10 fps)'}</option>
|
||||
<option value="{5}">{isMobile() ? 'Small (5 fps)' : 'Small video quality (5 fps)'}</option>
|
||||
<option value={30}>{isMobile() ? "High (30 fps)" : "High video quality (30 fps)"}</option>
|
||||
<option value={20}
|
||||
>{isMobile() ? "Medium (20 fps)" : "Medium video quality (20 fps, recommended)"}</option
|
||||
>
|
||||
<option value={10}>{isMobile() ? "Minimum (10 fps)" : "Minimum video quality (10 fps)"}</option>
|
||||
<option value={5}>{isMobile() ? "Small (5 fps)" : "Small video quality (5 fps)"}</option>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
@ -88,55 +92,65 @@ function closeMenu() {
|
||||
</section>
|
||||
<section class="settings-section-noSaveOption">
|
||||
<label>
|
||||
<input type="checkbox" class="nes-checkbox is-dark" bind:checked={fullscreen} on:change={changeFullscreen}/>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="nes-checkbox is-dark"
|
||||
bind:checked={fullscreen}
|
||||
on:change={changeFullscreen}
|
||||
/>
|
||||
<span>Fullscreen</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" class="nes-checkbox is-dark" bind:checked={notification} on:change={changeNotification}>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="nes-checkbox is-dark"
|
||||
bind:checked={notification}
|
||||
on:change={changeNotification}
|
||||
/>
|
||||
<span>Notifications</span>
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.settings-main {
|
||||
height: calc(100% - 40px);
|
||||
overflow-y: auto;
|
||||
|
||||
section {
|
||||
width: 100%;
|
||||
padding: 20px 20px 0;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
|
||||
div.nes-select select:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
section.settings-section-save {
|
||||
text-align: center;
|
||||
p {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
section.settings-section-noSaveOption {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
label {
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
|
||||
div.settings-main {
|
||||
section {
|
||||
padding: 0;
|
||||
}
|
||||
height: calc(100% - 40px);
|
||||
overflow-y: auto;
|
||||
|
||||
section {
|
||||
width: 100%;
|
||||
padding: 20px 20px 0;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
|
||||
div.nes-select select:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
section.settings-section-save {
|
||||
text-align: center;
|
||||
p {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
section.settings-section-noSaveOption {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
label {
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
|
||||
div.settings-main {
|
||||
section {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -8,25 +8,25 @@
|
||||
|
||||
//toolbar
|
||||
const toolbarOptions = [
|
||||
['bold', 'italic', 'underline', 'strike'], // toggled buttons
|
||||
['blockquote', 'code-block'],
|
||||
["bold", "italic", "underline", "strike"], // toggled buttons
|
||||
["blockquote", "code-block"],
|
||||
|
||||
[{'header': 1}, {'header': 2}], // custom button values
|
||||
[{'list': 'ordered'}, {'list': 'bullet'}],
|
||||
[{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
|
||||
[{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
|
||||
[{'direction': 'rtl'}], // text direction
|
||||
[{ header: 1 }, { header: 2 }], // custom button values
|
||||
[{ list: "ordered" }, { list: "bullet" }],
|
||||
[{ script: "sub" }, { script: "super" }], // superscript/subscript
|
||||
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
|
||||
[{ direction: "rtl" }], // text direction
|
||||
|
||||
[{'size': ['small', false, 'large', 'huge']}], // custom dropdown
|
||||
[{'header': [1, 2, 3, 4, 5, 6, false]}],
|
||||
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
||||
|
||||
[{'color': []}, {'background': []}], // dropdown with defaults from theme
|
||||
[{'font': []}],
|
||||
[{'align': []}],
|
||||
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
|
||||
[{ font: [] }],
|
||||
[{ align: [] }],
|
||||
|
||||
['clean'], // remove formatting button
|
||||
["clean"], // remove formatting button
|
||||
|
||||
['link', 'image', 'video']
|
||||
["link", "image", "video"],
|
||||
];
|
||||
|
||||
const gameScene = gameManager.getCurrentGameScene();
|
||||
@ -44,24 +44,24 @@
|
||||
const textGlobalMessage: PlayGlobalMessageInterface = {
|
||||
type: MESSAGE_TYPE,
|
||||
content: text,
|
||||
broadcastToWorld: broadcastToWorld
|
||||
broadcastToWorld: broadcastToWorld,
|
||||
};
|
||||
|
||||
quill.deleteText(0, quill.getLength());
|
||||
gameScene.connection?.emitGlobalMessage(textGlobalMessage);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
//Quill
|
||||
onMount(async () => {
|
||||
// Import quill
|
||||
const {default: Quill} = await import("quill"); // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
const { default: Quill } = await import("quill"); // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
quill = new Quill(QUILL_EDITOR, {
|
||||
placeholder: 'Enter your message here...',
|
||||
theme: 'snow',
|
||||
placeholder: "Enter your message here...",
|
||||
theme: "snow",
|
||||
modules: {
|
||||
toolbar: toolbarOptions
|
||||
toolbar: toolbarOptions,
|
||||
},
|
||||
});
|
||||
menuInputFocusStore.set(true);
|
||||
@ -69,9 +69,9 @@
|
||||
|
||||
onDestroy(() => {
|
||||
menuInputFocusStore.set(false);
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<section class="section-input-send-text">
|
||||
<div class="input-send-text" bind:this={QUILL_EDITOR}></div>
|
||||
<div class="input-send-text" bind:this={QUILL_EDITOR} />
|
||||
</section>
|
||||
|
@ -1,14 +1,14 @@
|
||||
<script lang="typescript">
|
||||
import {obtainedMediaConstraintStore} from "../Stores/MediaStore";
|
||||
import {localStreamStore, isSilentStore} from "../Stores/MediaStore";
|
||||
import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
|
||||
import { localStreamStore, isSilentStore } from "../Stores/MediaStore";
|
||||
import SoundMeterWidget from "./SoundMeterWidget.svelte";
|
||||
import {onDestroy} from "svelte";
|
||||
import {srcObject} from "./Video/utils";
|
||||
import { onDestroy } from "svelte";
|
||||
import { srcObject } from "./Video/utils";
|
||||
|
||||
let stream : MediaStream|null;
|
||||
let stream: MediaStream | null;
|
||||
|
||||
const unsubscribe = localStreamStore.subscribe(value => {
|
||||
if (value.type === 'success') {
|
||||
const unsubscribe = localStreamStore.subscribe((value) => {
|
||||
if (value.type === "success") {
|
||||
stream = value.stream;
|
||||
} else {
|
||||
stream = null;
|
||||
@ -17,26 +17,21 @@
|
||||
|
||||
onDestroy(unsubscribe);
|
||||
|
||||
|
||||
let isSilent: boolean;
|
||||
const unsubscribeIsSilent = isSilentStore.subscribe(value => {
|
||||
const unsubscribeIsSilent = isSilentStore.subscribe((value) => {
|
||||
isSilent = value;
|
||||
});
|
||||
|
||||
onDestroy(unsubscribeIsSilent);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<div>
|
||||
<div class="video-container nes-container is-rounded is-dark div-myCamVideo"
|
||||
class:hide={!$obtainedMediaConstraintStore.video || isSilent}>
|
||||
{#if $localStreamStore.type === "success" && $localStreamStore.stream}
|
||||
<video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline></video>
|
||||
<SoundMeterWidget stream={stream}></SoundMeterWidget>
|
||||
<video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline />
|
||||
<SoundMeterWidget {stream} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="is-silent" class:hide={isSilent}>
|
||||
Silent zone
|
||||
</div>
|
||||
<div class="is-silent" class:hide={isSilent}>Silent zone</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {blackListManager} from "../../WebRtc/BlackListManager";
|
||||
import {showReportScreenStore, userReportEmpty} from "../../Stores/ShowReportScreenStore";
|
||||
import {onMount} from "svelte";
|
||||
import { blackListManager } from "../../WebRtc/BlackListManager";
|
||||
import { showReportScreenStore, userReportEmpty } from "../../Stores/ShowReportScreenStore";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
export let userUUID: string | undefined;
|
||||
export let userName: string;
|
||||
@ -14,7 +14,7 @@
|
||||
} else {
|
||||
userIsBlocked = blackListManager.isBlackListed(userUUID);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function blockUser(): void {
|
||||
if (userUUID === undefined) {
|
||||
@ -32,13 +32,12 @@
|
||||
<h3>Block</h3>
|
||||
<p>Block any communication from and to {userName}. This can be reverted.</p>
|
||||
<button type="button" class="nes-btn is-error" on:click|preventDefault={blockUser}>
|
||||
{userIsBlocked ? 'Unblock this user' : 'Block this user'}
|
||||
{userIsBlocked ? "Unblock this user" : "Block this user"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
div.block-container {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,20 +1,20 @@
|
||||
<script lang="ts">
|
||||
import {showReportScreenStore, userReportEmpty} from "../../Stores/ShowReportScreenStore";
|
||||
import { showReportScreenStore, userReportEmpty } from "../../Stores/ShowReportScreenStore";
|
||||
import BlockSubMenu from "./BlockSubMenu.svelte";
|
||||
import ReportSubMenu from "./ReportSubMenu.svelte";
|
||||
import {onDestroy, onMount} from "svelte";
|
||||
import type {Unsubscriber} from "svelte/store";
|
||||
import {playersStore} from "../../Stores/PlayersStore";
|
||||
import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||
import {GameConnexionTypes} from "../../Url/UrlManager";
|
||||
import {get} from "svelte/store";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import type { Unsubscriber } from "svelte/store";
|
||||
import { playersStore } from "../../Stores/PlayersStore";
|
||||
import { connectionManager } from "../../Connexion/ConnectionManager";
|
||||
import { GameConnexionTypes } from "../../Url/UrlManager";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
let blockActive = true;
|
||||
let blockActive = true;
|
||||
let reportActive = !blockActive;
|
||||
let anonymous: boolean = false;
|
||||
let userUUID: string | undefined = playersStore.getPlayerById(get(showReportScreenStore).userId)?.userUuid;
|
||||
let userName = "No name";
|
||||
let unsubscriber: Unsubscriber
|
||||
let unsubscriber: Unsubscriber;
|
||||
|
||||
onMount(() => {
|
||||
unsubscriber = showReportScreenStore.subscribe((reportScreenStore) => {
|
||||
@ -25,15 +25,15 @@
|
||||
console.error("Could not find UUID for user with ID " + reportScreenStore.userId);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
anonymous = connectionManager.getConnexionType === GameConnexionTypes.anonymous;
|
||||
})
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscriber) {
|
||||
unsubscriber();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function close() {
|
||||
showReportScreenStore.set(userReportEmpty);
|
||||
@ -49,14 +49,14 @@
|
||||
reportActive = true;
|
||||
}
|
||||
|
||||
function onKeyDown(e:KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
close();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown}/>
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
<div class="report-menu-main nes-container is-rounded">
|
||||
<section class="report-menu-title">
|
||||
@ -67,75 +67,83 @@
|
||||
</section>
|
||||
<section class="report-menu-action {anonymous ? 'hidden' : ''}">
|
||||
<section class="justify-center">
|
||||
<button type="button" class="nes-btn {blockActive ? 'is-disabled' : ''}" on:click|preventDefault={activateBlock}>Block</button>
|
||||
<button
|
||||
type="button"
|
||||
class="nes-btn {blockActive ? 'is-disabled' : ''}"
|
||||
on:click|preventDefault={activateBlock}>Block</button
|
||||
>
|
||||
</section>
|
||||
<section class="justify-center">
|
||||
<button type="button" class="nes-btn {reportActive ? 'is-disabled' : ''}" on:click|preventDefault={activateReport}>Report</button>
|
||||
<button
|
||||
type="button"
|
||||
class="nes-btn {reportActive ? 'is-disabled' : ''}"
|
||||
on:click|preventDefault={activateReport}>Report</button
|
||||
>
|
||||
</section>
|
||||
</section>
|
||||
<section class="report-menu-content">
|
||||
{#if blockActive}
|
||||
<BlockSubMenu userUUID="{userUUID}" userName="{userName}"/>
|
||||
<BlockSubMenu {userUUID} {userName} />
|
||||
{:else if reportActive}
|
||||
<ReportSubMenu userUUID="{userUUID}"/>
|
||||
{:else }
|
||||
<ReportSubMenu {userUUID} />
|
||||
{:else}
|
||||
<p>ERROR : There is no action selected.</p>
|
||||
{/if}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.nes-container {
|
||||
padding: 5px;
|
||||
}
|
||||
.nes-container {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
section.justify-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div.report-menu-main {
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
position: relative;
|
||||
height: 70vh;
|
||||
width: 50vw;
|
||||
top: 10vh;
|
||||
margin: auto;
|
||||
|
||||
section.report-menu-title {
|
||||
display: grid;
|
||||
grid-template-columns: calc(100% - 45px) 40px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
h2 {
|
||||
section.justify-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
section.report-menu-action {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
section.report-menu-action.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
div.report-menu-main {
|
||||
top: 21vh;
|
||||
height: 60vh;
|
||||
width: 100vw;
|
||||
font-size: 0.5em;
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
position: relative;
|
||||
height: 70vh;
|
||||
width: 50vw;
|
||||
top: 10vh;
|
||||
margin: auto;
|
||||
|
||||
section.report-menu-title {
|
||||
display: grid;
|
||||
grid-template-columns: calc(100% - 45px) 40px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
h2 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
section.report-menu-action {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
section.report-menu-action.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
div.report-menu-main {
|
||||
top: 21vh;
|
||||
height: 60vh;
|
||||
width: 100vw;
|
||||
font-size: 0.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,22 +1,22 @@
|
||||
<script lang="ts">
|
||||
import {showReportScreenStore, userReportEmpty} from "../../Stores/ShowReportScreenStore";
|
||||
import {gameManager} from "../../Phaser/Game/GameManager";
|
||||
import { showReportScreenStore, userReportEmpty } from "../../Stores/ShowReportScreenStore";
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
|
||||
export let userUUID: string | undefined;
|
||||
let reportMessage: string;
|
||||
let hiddenError = true;
|
||||
|
||||
function submitReport() {
|
||||
if (reportMessage === '') {
|
||||
if (reportMessage === "") {
|
||||
hiddenError = true;
|
||||
} else {
|
||||
hiddenError = false;
|
||||
if( userUUID === undefined) {
|
||||
console.error('User UUID is not valid.');
|
||||
if (userUUID === undefined) {
|
||||
console.error("User UUID is not valid.");
|
||||
return;
|
||||
}
|
||||
gameManager.getCurrentGameScene().connection?.emitReportPlayerMessage(userUUID, reportMessage);
|
||||
showReportScreenStore.set(userReportEmpty)
|
||||
showReportScreenStore.set(userReportEmpty);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -28,9 +28,9 @@
|
||||
<section>
|
||||
<label>
|
||||
<span>Your message: </span>
|
||||
<textarea type="text" class="nes-textarea" bind:value={reportMessage}></textarea>
|
||||
<textarea type="text" class="nes-textarea" bind:value={reportMessage} />
|
||||
</label>
|
||||
<p hidden="{hiddenError}">Report message cannot to be empty.</p>
|
||||
<p hidden={hiddenError}>Report message cannot to be empty.</p>
|
||||
</section>
|
||||
<section>
|
||||
<button type="submit" class="nes-btn is-error" on:click={submitReport}>Report this user</button>
|
||||
@ -40,16 +40,16 @@
|
||||
|
||||
<style lang="scss">
|
||||
div.report-container-main {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
|
||||
textarea {
|
||||
height: clamp(100px, 15vh, 300px);
|
||||
}
|
||||
textarea {
|
||||
height: clamp(100px, 15vh, 300px);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 630px) {
|
||||
div.report-container-main textarea {
|
||||
height: 50px;
|
||||
}
|
||||
div.report-container-main textarea {
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="typescript">
|
||||
import type {Game} from "../../Phaser/Game/Game";
|
||||
import {SelectCompanionScene, SelectCompanionSceneName} from "../../Phaser/Login/SelectCompanionScene";
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import { SelectCompanionScene, SelectCompanionSceneName } from "../../Phaser/Login/SelectCompanionScene";
|
||||
|
||||
export let game: Game;
|
||||
|
||||
@ -26,62 +26,70 @@
|
||||
<form class="selectCompanionScene">
|
||||
<section class="text-center">
|
||||
<h2>Select your companion</h2>
|
||||
<button class="selectCharacterButton selectCharacterButtonLeft nes-btn" on:click|preventDefault={selectLeft}> < </button>
|
||||
<button class="selectCharacterButton selectCharacterButtonRight nes-btn" on:click|preventDefault={selectRight}> > </button>
|
||||
<button class="selectCharacterButton selectCharacterButtonLeft nes-btn" on:click|preventDefault={selectLeft}>
|
||||
<
|
||||
</button>
|
||||
<button class="selectCharacterButton selectCharacterButtonRight nes-btn" on:click|preventDefault={selectRight}>
|
||||
>
|
||||
</button>
|
||||
</section>
|
||||
<section class="action">
|
||||
<button href="/" class="selectCompanionSceneFormBack nes-btn" on:click|preventDefault={noCompanion}>No companion</button>
|
||||
<button type="submit" class="selectCompanionSceneFormSubmit nes-btn is-primary" on:click|preventDefault={selectCompanion}>Continue</button>
|
||||
<button href="/" class="selectCompanionSceneFormBack nes-btn" on:click|preventDefault={noCompanion}
|
||||
>No companion</button
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="selectCompanionSceneFormSubmit nes-btn is-primary"
|
||||
on:click|preventDefault={selectCompanion}>Continue</button
|
||||
>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
form.selectCompanionScene {
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
color: #ebeeee;
|
||||
|
||||
section {
|
||||
margin: 10px;
|
||||
|
||||
&.action {
|
||||
text-align: center;
|
||||
margin-top: 55vh;
|
||||
}
|
||||
|
||||
h2 {
|
||||
form.selectCompanionScene {
|
||||
font-family: "Press Start 2P";
|
||||
margin: 1px;
|
||||
}
|
||||
pointer-events: auto;
|
||||
color: #ebeeee;
|
||||
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
section {
|
||||
margin: 10px;
|
||||
|
||||
button.selectCharacterButton {
|
||||
position: absolute;
|
||||
top: 33vh;
|
||||
margin: 0;
|
||||
}
|
||||
&.action {
|
||||
text-align: center;
|
||||
margin-top: 55vh;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button.selectCharacterButton {
|
||||
position: absolute;
|
||||
top: 33vh;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
button.selectCharacterButtonLeft {
|
||||
left: 33vw;
|
||||
}
|
||||
|
||||
button.selectCharacterButtonRight {
|
||||
right: 33vw;
|
||||
}
|
||||
}
|
||||
|
||||
button.selectCharacterButtonLeft {
|
||||
left: 33vw;
|
||||
@media only screen and (max-width: 800px) {
|
||||
form.selectCompanionScene button.selectCharacterButtonLeft {
|
||||
left: 5vw;
|
||||
}
|
||||
form.selectCompanionScene button.selectCharacterButtonRight {
|
||||
right: 5vw;
|
||||
}
|
||||
}
|
||||
|
||||
button.selectCharacterButtonRight {
|
||||
right: 33vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
form.selectCompanionScene button.selectCharacterButtonLeft{
|
||||
left: 5vw;
|
||||
}
|
||||
form.selectCompanionScene button.selectCharacterButtonRight{
|
||||
right: 5vw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
@ -1,14 +1,15 @@
|
||||
<script lang="typescript">
|
||||
import { AudioContext } from 'standardized-audio-context';
|
||||
import {SoundMeter} from "../Phaser/Components/SoundMeter";
|
||||
import {onDestroy} from "svelte";
|
||||
import { AudioContext } from "standardized-audio-context";
|
||||
import { SoundMeter } from "../Phaser/Components/SoundMeter";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
export let stream: MediaStream|null;
|
||||
export let stream: MediaStream | null;
|
||||
let volume = 0;
|
||||
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
const soundMeter = new SoundMeter();
|
||||
let display = false;
|
||||
let error = false;
|
||||
|
||||
$: {
|
||||
if (stream && stream.getAudioTracks().length > 0) {
|
||||
@ -17,17 +18,19 @@
|
||||
|
||||
if (timeout) {
|
||||
clearInterval(timeout);
|
||||
error = false;
|
||||
}
|
||||
|
||||
timeout = setInterval(() => {
|
||||
try{
|
||||
try {
|
||||
volume = soundMeter.getVolume();
|
||||
//console.log(volume);
|
||||
}catch(err){
|
||||
|
||||
} catch (err) {
|
||||
if (!error) {
|
||||
console.error(err);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
|
||||
} else {
|
||||
display = false;
|
||||
}
|
||||
@ -38,14 +41,13 @@
|
||||
if (timeout) {
|
||||
clearInterval(timeout);
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<div class="sound-progress" class:active={display}>
|
||||
<span class:active={volume > 5}></span>
|
||||
<span class:active={volume > 10}></span>
|
||||
<span class:active={volume > 15}></span>
|
||||
<span class:active={volume > 40}></span>
|
||||
<span class:active={volume > 70}></span>
|
||||
<span class:active={volume > 5} />
|
||||
<span class:active={volume > 10} />
|
||||
<span class:active={volume > 15} />
|
||||
<span class:active={volume > 40} />
|
||||
<span class:active={volume > 70} />
|
||||
</div>
|
||||
|
@ -1,22 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import {banMessageVisibleStore, banMessageContentStore} from "../../Stores/TypeMessageStore/BanMessageStore";
|
||||
import {onMount} from "svelte";
|
||||
import { banMessageVisibleStore, banMessageContentStore } from "../../Stores/TypeMessageStore/BanMessageStore";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
const text = $banMessageContentStore;
|
||||
const NAME_BUTTON = 'Ok';
|
||||
const NAME_BUTTON = "Ok";
|
||||
let nbSeconds = 10;
|
||||
let nameButton = '';
|
||||
let nameButton = "";
|
||||
|
||||
onMount(() => {
|
||||
timeToRead()
|
||||
})
|
||||
timeToRead();
|
||||
});
|
||||
|
||||
function timeToRead() {
|
||||
nbSeconds -= 1;
|
||||
nameButton = nbSeconds.toString();
|
||||
if ( nbSeconds > 0 ) {
|
||||
setTimeout( () => {
|
||||
if (nbSeconds > 0) {
|
||||
setTimeout(() => {
|
||||
timeToRead();
|
||||
}, 1000);
|
||||
} else {
|
||||
@ -29,68 +29,76 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="main-ban-message nes-container is-rounded" transition:fly="{{ y: -1000, duration: 500 }}">
|
||||
<h2 class="title-ban-message"><img src="resources/logos/report.svg" alt="***"/> Important message <img src="resources/logos/report.svg" alt="***"/></h2>
|
||||
<div class="main-ban-message nes-container is-rounded" transition:fly={{ y: -1000, duration: 500 }}>
|
||||
<h2 class="title-ban-message">
|
||||
<img src="resources/logos/report.svg" alt="***" /> Important message
|
||||
<img src="resources/logos/report.svg" alt="***" />
|
||||
</h2>
|
||||
<div class="content-ban-message">
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
<div class="footer-ban-message">
|
||||
<button type="button" class="nes-btn {nameButton === NAME_BUTTON ? 'is-primary' : 'is-error'}" disabled="{!(nameButton === NAME_BUTTON)}" on:click|preventDefault={closeBanMessage}>{nameButton}</button>
|
||||
<button
|
||||
type="button"
|
||||
class="nes-btn {nameButton === NAME_BUTTON ? 'is-primary' : 'is-error'}"
|
||||
disabled={!(nameButton === NAME_BUTTON)}
|
||||
on:click|preventDefault={closeBanMessage}>{nameButton}</button
|
||||
>
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<audio id="report-message" autoplay>
|
||||
<source src="/resources/objects/report-message.mp3" type="audio/mp3">
|
||||
<source src="/resources/objects/report-message.mp3" type="audio/mp3" />
|
||||
</audio>
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
div.main-ban-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
top: 15vh;
|
||||
div.main-ban-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
top: 15vh;
|
||||
|
||||
height: 70vh;
|
||||
width: 60vw;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-bottom: 0;
|
||||
height: 70vh;
|
||||
width: 60vw;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-bottom: 0;
|
||||
|
||||
pointer-events: auto;
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
pointer-events: auto;
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
h2.title-ban-message {
|
||||
flex: 1 1 auto;
|
||||
max-height: 50px;
|
||||
margin-bottom: 20px;
|
||||
h2.title-ban-message {
|
||||
flex: 1 1 auto;
|
||||
max-height: 50px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
height: 50px;
|
||||
}
|
||||
img {
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
div.content-ban-message {
|
||||
flex: 1 1 auto;
|
||||
max-height: calc(100% - 50px);
|
||||
overflow: auto;
|
||||
|
||||
p {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
div.footer-ban-message {
|
||||
height: 50px;
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
|
||||
button {
|
||||
width: 88px;
|
||||
height: 44px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.content-ban-message {
|
||||
flex: 1 1 auto;
|
||||
max-height: calc(100% - 50px);
|
||||
overflow: auto;
|
||||
|
||||
p {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
div.footer-ban-message {
|
||||
height: 50px;
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
|
||||
button {
|
||||
width: 88px;
|
||||
height: 44px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,59 +1,61 @@
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import {textMessageContentStore, textMessageVisibleStore} from "../../Stores/TypeMessageStore/TextMessageStore";
|
||||
import { textMessageContentStore, textMessageVisibleStore } from "../../Stores/TypeMessageStore/TextMessageStore";
|
||||
import { QuillDeltaToHtmlConverter } from "quill-delta-to-html";
|
||||
|
||||
const content = JSON.parse($textMessageContentStore);
|
||||
const converter = new QuillDeltaToHtmlConverter(content.ops, {inlineStyles: true});
|
||||
const NAME_BUTTON = 'Ok';
|
||||
const converter = new QuillDeltaToHtmlConverter(content.ops, { inlineStyles: true });
|
||||
const NAME_BUTTON = "Ok";
|
||||
|
||||
function closeTextMessage() {
|
||||
textMessageVisibleStore.set(false);
|
||||
}
|
||||
|
||||
function onKeyDown(e:KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
closeTextMessage();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown}/>
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
<div class="main-text-message nes-container is-rounded" transition:fly="{{ x: -1000, duration: 500 }}">
|
||||
<div class="main-text-message nes-container is-rounded" transition:fly={{ x: -1000, duration: 500 }}>
|
||||
<div class="content-text-message">
|
||||
{@html converter.convert()}
|
||||
</div>
|
||||
<div class="footer-text-message">
|
||||
<button type="button" class="nes-btn is-primary" on:click|preventDefault={closeTextMessage}>{NAME_BUTTON}</button>
|
||||
<button type="button" class="nes-btn is-primary" on:click|preventDefault={closeTextMessage}
|
||||
>{NAME_BUTTON}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.main-text-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
div.main-text-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
max-height: 25vh;
|
||||
width: 80vw;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-bottom: 0;
|
||||
max-height: 25vh;
|
||||
width: 80vw;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-bottom: 0;
|
||||
|
||||
pointer-events: auto;
|
||||
background-color: #333333;
|
||||
pointer-events: auto;
|
||||
background-color: #333333;
|
||||
|
||||
div.content-text-message {
|
||||
flex: 1 1 auto;
|
||||
max-height: calc(100% - 50px);
|
||||
color: whitesmoke;
|
||||
div.content-text-message {
|
||||
flex: 1 1 auto;
|
||||
max-height: calc(100% - 50px);
|
||||
color: whitesmoke;
|
||||
|
||||
overflow: auto;
|
||||
}
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
div.footer-text-message {
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
div.footer-text-message {
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import { fly } from "svelte/transition";
|
||||
import megaphoneImg from "./images/megaphone.svg";
|
||||
import {soundPlayingStore} from "../../Stores/SoundPlayingStore";
|
||||
import {afterUpdate} from "svelte";
|
||||
import { soundPlayingStore } from "../../Stores/SoundPlayingStore";
|
||||
import { afterUpdate } from "svelte";
|
||||
|
||||
export let url: string;
|
||||
let audio: HTMLAudioElement;
|
||||
@ -16,37 +16,37 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="audio-playing" transition:fly="{{ x: 210, duration: 500 }}">
|
||||
<div class="audio-playing" transition:fly={{ x: 210, duration: 500 }}>
|
||||
<img src={megaphoneImg} alt="Audio playing" />
|
||||
<p>Audio message</p>
|
||||
<audio bind:this={audio} src={url} on:ended={soundEnded} >
|
||||
<track kind="captions">
|
||||
<audio bind:this={audio} src={url} on:ended={soundEnded}>
|
||||
<track kind="captions" />
|
||||
</audio>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
/*audio html when audio message playing*/
|
||||
.audio-playing {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 54px;
|
||||
right: 0;
|
||||
top: 40px;
|
||||
transition: all 0.1s ease-out;
|
||||
background-color: black;
|
||||
border-radius: 30px 0 0 30px;
|
||||
display: inline-flex;
|
||||
/*audio html when audio message playing*/
|
||||
.audio-playing {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 54px;
|
||||
right: 0;
|
||||
top: 40px;
|
||||
transition: all 0.1s ease-out;
|
||||
background-color: black;
|
||||
border-radius: 30px 0 0 30px;
|
||||
display: inline-flex;
|
||||
|
||||
img {
|
||||
border-radius: 50%;
|
||||
background-color: #ffda01;
|
||||
padding: 10px;
|
||||
}
|
||||
img {
|
||||
border-radius: 50%;
|
||||
background-color: #ffda01;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: white;
|
||||
margin-left: 10px;
|
||||
margin-top: 14px;
|
||||
p {
|
||||
color: white;
|
||||
margin-left: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,48 +1,49 @@
|
||||
<script lang="ts">
|
||||
import {errorStore} from "../../Stores/ErrorStore";
|
||||
|
||||
function close(): boolean {
|
||||
errorStore.clearMessages();
|
||||
return false;
|
||||
}
|
||||
import { errorStore, hasClosableMessagesInErrorStore } from "../../Stores/ErrorStore";
|
||||
|
||||
function close(): boolean {
|
||||
errorStore.clearClosableMessages();
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="error-div nes-container is-dark is-rounded" open>
|
||||
<p class="nes-text is-error title">Error</p>
|
||||
<div class="body">
|
||||
{#each $errorStore as error}
|
||||
<p>{error}</p>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="button-bar">
|
||||
<button class="nes-btn is-error" on:click={close}>Close</button>
|
||||
{#each $errorStore as error}
|
||||
<p>{error.message}</p>
|
||||
{/each}
|
||||
</div>
|
||||
{#if $hasClosableMessagesInErrorStore}
|
||||
<div class="button-bar">
|
||||
<button class="nes-btn is-error" on:click={close}>Close</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.error-div {
|
||||
pointer-events: auto;
|
||||
margin-top: 10vh;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
width: max-content;
|
||||
max-width: 80vw;
|
||||
pointer-events: auto;
|
||||
margin-top: 10vh;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
width: max-content;
|
||||
max-width: 80vw;
|
||||
|
||||
.button-bar {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.body {
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
&.title {
|
||||
text-align: center;
|
||||
.button-bar {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.body {
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
&.title {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,21 +1,21 @@
|
||||
<script lang="ts">
|
||||
import {streamableCollectionStore} from "../../Stores/StreamableCollectionStore";
|
||||
import {afterUpdate, onDestroy} from "svelte";
|
||||
import {biggestAvailableAreaStore} from "../../Stores/BiggestAvailableAreaStore";
|
||||
import { streamableCollectionStore } from "../../Stores/StreamableCollectionStore";
|
||||
import { afterUpdate, onDestroy } from "svelte";
|
||||
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
|
||||
import MediaBox from "./MediaBox.svelte";
|
||||
|
||||
let cssClass = 'one-col';
|
||||
let cssClass = "one-col";
|
||||
|
||||
const unsubscribe = streamableCollectionStore.subscribe((displayableMedias) => {
|
||||
const nbUsers = displayableMedias.size;
|
||||
if (nbUsers <= 1) {
|
||||
cssClass = 'one-col';
|
||||
cssClass = "one-col";
|
||||
} else if (nbUsers <= 4) {
|
||||
cssClass = 'two-col';
|
||||
cssClass = "two-col";
|
||||
} else if (nbUsers <= 9) {
|
||||
cssClass = 'three-col';
|
||||
cssClass = "three-col";
|
||||
} else {
|
||||
cssClass = 'four-col';
|
||||
cssClass = "four-col";
|
||||
}
|
||||
});
|
||||
|
||||
@ -25,11 +25,11 @@
|
||||
|
||||
afterUpdate(() => {
|
||||
biggestAvailableAreaStore.recompute();
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="chat-mode {cssClass}">
|
||||
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
|
||||
<MediaBox streamable={peer}></MediaBox>
|
||||
<MediaBox streamable={peer} />
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -1,16 +1,15 @@
|
||||
<script lang="typescript">
|
||||
import type {ScreenSharingLocalMedia} from "../../Stores/ScreenSharingStore";
|
||||
import {videoFocusStore} from "../../Stores/VideoFocusStore";
|
||||
import {srcObject} from "./utils";
|
||||
import type { ScreenSharingLocalMedia } from "../../Stores/ScreenSharingStore";
|
||||
import { videoFocusStore } from "../../Stores/VideoFocusStore";
|
||||
import { srcObject } from "./utils";
|
||||
|
||||
export let peer : ScreenSharingLocalMedia;
|
||||
export let peer: ScreenSharingLocalMedia;
|
||||
let stream = peer.stream;
|
||||
export let cssClass : string|undefined;
|
||||
export let cssClass: string | undefined;
|
||||
</script>
|
||||
|
||||
|
||||
<div class="video-container {cssClass ? cssClass : ''}" class:hide={!stream}>
|
||||
{#if stream}
|
||||
<video use:srcObject={stream} autoplay muted playsinline on:click={() => videoFocusStore.toggleFocus(peer)}></video>
|
||||
<video use:srcObject={stream} autoplay muted playsinline on:click={() => videoFocusStore.toggleFocus(peer)} />
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -1,20 +1,20 @@
|
||||
<script lang="ts">
|
||||
import {VideoPeer} from "../../WebRtc/VideoPeer";
|
||||
import { VideoPeer } from "../../WebRtc/VideoPeer";
|
||||
import VideoMediaBox from "./VideoMediaBox.svelte";
|
||||
import ScreenSharingMediaBox from "./ScreenSharingMediaBox.svelte";
|
||||
import {ScreenSharingPeer} from "../../WebRtc/ScreenSharingPeer";
|
||||
import { ScreenSharingPeer } from "../../WebRtc/ScreenSharingPeer";
|
||||
import LocalStreamMediaBox from "./LocalStreamMediaBox.svelte";
|
||||
import type {Streamable} from "../../Stores/StreamableCollectionStore";
|
||||
import type { Streamable } from "../../Stores/StreamableCollectionStore";
|
||||
|
||||
export let streamable: Streamable;
|
||||
</script>
|
||||
|
||||
<div class="media-container">
|
||||
{#if streamable instanceof VideoPeer}
|
||||
<VideoMediaBox peer={streamable}/>
|
||||
<VideoMediaBox peer={streamable} />
|
||||
{:else if streamable instanceof ScreenSharingPeer}
|
||||
<ScreenSharingMediaBox peer={streamable}/>
|
||||
<ScreenSharingMediaBox peer={streamable} />
|
||||
{:else}
|
||||
<LocalStreamMediaBox peer={streamable} cssClass=""/>
|
||||
<LocalStreamMediaBox peer={streamable} cssClass="" />
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -1,26 +1,26 @@
|
||||
<script lang="ts">
|
||||
import {streamableCollectionStore} from "../../Stores/StreamableCollectionStore";
|
||||
import {videoFocusStore} from "../../Stores/VideoFocusStore";
|
||||
import {afterUpdate} from "svelte";
|
||||
import {biggestAvailableAreaStore} from "../../Stores/BiggestAvailableAreaStore";
|
||||
import { streamableCollectionStore } from "../../Stores/StreamableCollectionStore";
|
||||
import { videoFocusStore } from "../../Stores/VideoFocusStore";
|
||||
import { afterUpdate } from "svelte";
|
||||
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
|
||||
import MediaBox from "./MediaBox.svelte";
|
||||
|
||||
afterUpdate(() => {
|
||||
biggestAvailableAreaStore.recompute();
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="main-section">
|
||||
{#if $videoFocusStore }
|
||||
{#if $videoFocusStore}
|
||||
{#key $videoFocusStore.uniqueId}
|
||||
<MediaBox streamable={$videoFocusStore}></MediaBox>
|
||||
<MediaBox streamable={$videoFocusStore} />
|
||||
{/key}
|
||||
{/if}
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
|
||||
{#if peer !== $videoFocusStore }
|
||||
<MediaBox streamable={peer}></MediaBox>
|
||||
{#if peer !== $videoFocusStore}
|
||||
<MediaBox streamable={peer} />
|
||||
{/if}
|
||||
{/each}
|
||||
</aside>
|
||||
|
@ -1,33 +1,33 @@
|
||||
<script lang="ts">
|
||||
import type {ScreenSharingPeer} from "../../WebRtc/ScreenSharingPeer";
|
||||
import {videoFocusStore} from "../../Stores/VideoFocusStore";
|
||||
import {getColorByString, srcObject} from "./utils";
|
||||
import type { ScreenSharingPeer } from "../../WebRtc/ScreenSharingPeer";
|
||||
import { videoFocusStore } from "../../Stores/VideoFocusStore";
|
||||
import { getColorByString, srcObject } from "./utils";
|
||||
|
||||
export let peer: ScreenSharingPeer;
|
||||
let streamStore = peer.streamStore;
|
||||
let name = peer.userName;
|
||||
let statusStore = peer.statusStore;
|
||||
|
||||
</script>
|
||||
|
||||
<div class="video-container">
|
||||
{#if $statusStore === 'connecting'}
|
||||
<div class="connecting-spinner"></div>
|
||||
{#if $statusStore === "connecting"}
|
||||
<div class="connecting-spinner" />
|
||||
{/if}
|
||||
{#if $statusStore === 'error'}
|
||||
<div class="rtc-error"></div>
|
||||
{#if $statusStore === "error"}
|
||||
<div class="rtc-error" />
|
||||
{/if}
|
||||
{#if $streamStore === null}
|
||||
<i style="background-color: {getColorByString(name)};">{name}</i>
|
||||
{:else}
|
||||
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)}></video>
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.video-container {
|
||||
video {
|
||||
width: 100%;
|
||||
.video-container {
|
||||
video {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type {VideoPeer} from "../../WebRtc/VideoPeer";
|
||||
import type { VideoPeer } from "../../WebRtc/VideoPeer";
|
||||
import SoundMeterWidget from "../SoundMeterWidget.svelte";
|
||||
import microphoneCloseImg from "../images/microphone-close.svg";
|
||||
import reportImg from "./images/report.svg";
|
||||
import blockSignImg from "./images/blockSign.svg";
|
||||
import {videoFocusStore} from "../../Stores/VideoFocusStore";
|
||||
import {showReportScreenStore} from "../../Stores/ShowReportScreenStore";
|
||||
import {getColorByString, srcObject} from "./utils";
|
||||
import { videoFocusStore } from "../../Stores/VideoFocusStore";
|
||||
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
|
||||
import { getColorByString, srcObject } from "./utils";
|
||||
|
||||
export let peer: VideoPeer;
|
||||
let streamStore = peer.streamStore;
|
||||
@ -15,32 +15,31 @@
|
||||
let constraintStore = peer.constraintsStore;
|
||||
|
||||
function openReport(peer: VideoPeer): void {
|
||||
showReportScreenStore.set({ userId:peer.userId, userName: peer.userName });
|
||||
showReportScreenStore.set({ userId: peer.userId, userName: peer.userName });
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="video-container nes-container is-rounded is-dark">
|
||||
{#if $statusStore === 'connecting'}
|
||||
<div class="connecting-spinner"></div>
|
||||
{#if $statusStore === "connecting"}
|
||||
<div class="connecting-spinner" />
|
||||
{/if}
|
||||
{#if $statusStore === 'error'}
|
||||
<div class="rtc-error"></div>
|
||||
{#if $statusStore === "error"}
|
||||
<div class="rtc-error" />
|
||||
{/if}
|
||||
{#if !$constraintStore || $constraintStore.video === false}
|
||||
<i style="background-color: {getColorByString(name)};">{name}</i>
|
||||
{/if}
|
||||
{#if $constraintStore && $constraintStore.audio === false}
|
||||
<img src={microphoneCloseImg} class="active" alt="Muted">
|
||||
<img src={microphoneCloseImg} class="active" alt="Muted" />
|
||||
{/if}
|
||||
<button class="report nes-button is-dark" on:click={() => openReport(peer)}>
|
||||
<img alt="Report this user" src={reportImg}>
|
||||
<img alt="Report this user" src={reportImg} />
|
||||
<span>Report/Block</span>
|
||||
</button>
|
||||
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)}></video>
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)} />
|
||||
<img src={blockSignImg} class="block-logo" alt="Block" />
|
||||
{#if $constraintStore && $constraintStore.audio !== false}
|
||||
<SoundMeterWidget stream={$streamStore}></SoundMeterWidget>
|
||||
<SoundMeterWidget stream={$streamStore} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
@ -1,23 +1,22 @@
|
||||
<script lang="ts">
|
||||
import {LayoutMode} from "../../WebRtc/LayoutManager";
|
||||
import {layoutModeStore} from "../../Stores/StreamableCollectionStore";
|
||||
import { LayoutMode } from "../../WebRtc/LayoutManager";
|
||||
import { layoutModeStore } from "../../Stores/StreamableCollectionStore";
|
||||
import PresentationLayout from "./PresentationLayout.svelte";
|
||||
import ChatLayout from "./ChatLayout.svelte";
|
||||
|
||||
</script>
|
||||
|
||||
<div class="video-overlay">
|
||||
{#if $layoutModeStore === LayoutMode.Presentation }
|
||||
{#if $layoutModeStore === LayoutMode.Presentation}
|
||||
<PresentationLayout />
|
||||
{:else }
|
||||
{:else}
|
||||
<ChatLayout />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.video-overlay {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<script lang="typescript">
|
||||
import { fly } from 'svelte/transition';
|
||||
import {requestVisitCardsStore} from "../../Stores/GameStore";
|
||||
import {onMount} from "svelte";
|
||||
import { fly } from "svelte/transition";
|
||||
import { requestVisitCardsStore } from "../../Stores/GameStore";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
export let visitCardUrl: string;
|
||||
let w = '500px';
|
||||
let h = '250px';
|
||||
let w = "500px";
|
||||
let h = "250px";
|
||||
let hidden = true;
|
||||
let cvIframe: HTMLIFrameElement;
|
||||
|
||||
@ -13,73 +13,81 @@
|
||||
requestVisitCardsStore.set(null);
|
||||
}
|
||||
|
||||
function handleIframeMessage(message:any) {
|
||||
if (message.data.type === 'cvIframeSize') {
|
||||
w = (message.data.data.w) + 'px';
|
||||
h = (message.data.data.h) + 'px';
|
||||
function handleIframeMessage(message: MessageEvent) {
|
||||
if (message.data.type === "cvIframeSize") {
|
||||
w = message.data.data.w + "px";
|
||||
h = message.data.data.h + "px";
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
cvIframe.onload = () => hidden = false
|
||||
cvIframe.onerror = () => hidden = false
|
||||
})
|
||||
cvIframe.onload = () => (hidden = false);
|
||||
cvIframe.onerror = () => (hidden = false);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.loader {
|
||||
border: 16px solid #f3f3f3; /* Light grey */
|
||||
border-top: 16px solid #3498db; /* Blue */
|
||||
border-radius: 50%;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin:auto;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.visitCard {
|
||||
pointer-events: all;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
margin-top: 200px;
|
||||
max-width: 80vw;
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
max-width: 80vw;
|
||||
overflow: hidden;
|
||||
|
||||
&.hidden {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<section class="visitCard" transition:fly="{{ y: -200, duration: 1000 }}" style="width: {w}">
|
||||
<section class="visitCard" transition:fly={{ y: -200, duration: 1000 }} style="width: {w}">
|
||||
{#if hidden}
|
||||
<div class="loader"></div>
|
||||
<div class="loader" />
|
||||
{/if}
|
||||
<iframe title="visitCard" src={visitCardUrl} allow="clipboard-read; clipboard-write self {visitCardUrl}" style="width: {w}; height: {h}" class:hidden={hidden} bind:this={cvIframe}></iframe>
|
||||
<iframe
|
||||
title="visitCard"
|
||||
src={visitCardUrl}
|
||||
allow="clipboard-read; clipboard-write self {visitCardUrl}"
|
||||
style="width: {w}; height: {h}"
|
||||
class:hidden
|
||||
bind:this={cvIframe}
|
||||
/>
|
||||
{#if !hidden}
|
||||
<div class="buttonContainer">
|
||||
<button class="nes-btn is-popUpElement" on:click={closeCard}>Close</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</section>
|
||||
|
||||
<svelte:window on:message={handleIframeMessage}/>
|
||||
<svelte:window on:message={handleIframeMessage} />
|
||||
|
||||
<style lang="scss">
|
||||
.loader {
|
||||
border: 16px solid #f3f3f3; /* Light grey */
|
||||
border-top: 16px solid #3498db; /* Blue */
|
||||
border-radius: 50%;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin: auto;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.visitCard {
|
||||
pointer-events: all;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
margin-top: 200px;
|
||||
max-width: 80vw;
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
max-width: 80vw;
|
||||
overflow: hidden;
|
||||
|
||||
&.hidden {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,37 +1,39 @@
|
||||
<script lang="typescript">
|
||||
import { fly } from 'svelte/transition';
|
||||
import {userIsAdminStore} from "../../Stores/GameStore";
|
||||
import {ADMIN_URL} from "../../Enum/EnvironmentVariable";
|
||||
|
||||
const upgradeLink = ADMIN_URL+'/pricing';
|
||||
|
||||
import { fly } from "svelte/transition";
|
||||
import { userIsAdminStore } from "../../Stores/GameStore";
|
||||
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
const upgradeLink = ADMIN_URL + "/pricing";
|
||||
</script>
|
||||
|
||||
<main class="warningMain" transition:fly="{{ y: -200, duration: 500 }}">
|
||||
<main class="warningMain" transition:fly={{ y: -200, duration: 500 }}>
|
||||
<h2>Warning!</h2>
|
||||
{#if $userIsAdminStore}
|
||||
<p>This world is close to its limit!. You can upgrade its capacity <a href="{upgradeLink}" target="_blank">here</a></p>
|
||||
<p>
|
||||
This world is close to its limit!. You can upgrade its capacity <a href={upgradeLink} target="_blank"
|
||||
>here</a
|
||||
>
|
||||
</p>
|
||||
{:else}
|
||||
<p>This world is close to its limit!</p>
|
||||
{/if}
|
||||
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
main.warningMain {
|
||||
pointer-events: auto;
|
||||
width: 100vw;
|
||||
background-color: red;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
font-family: Lato;
|
||||
min-width: 300px;
|
||||
opacity: 0.9;
|
||||
z-index: 2;
|
||||
main.warningMain {
|
||||
pointer-events: auto;
|
||||
width: 100vw;
|
||||
background-color: red;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
font-family: Lato;
|
||||
min-width: 300px;
|
||||
opacity: 0.9;
|
||||
z-index: 2;
|
||||
h2 {
|
||||
padding: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="typescript">
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import {SelectCharacterScene, SelectCharacterSceneName} from "../../Phaser/Login/SelectCharacterScene";
|
||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
|
||||
|
||||
export let game: Game;
|
||||
|
||||
@ -21,72 +21,81 @@
|
||||
function customizeScene() {
|
||||
selectCharacterScene.nextSceneToCustomizeScene();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<form class="selectCharacterScene">
|
||||
<section class="text-center">
|
||||
<h2>Select your Avatar</h2>
|
||||
<button class="selectCharacterButton selectCharacterButtonLeft nes-btn" on:click|preventDefault={ selectLeft }> < </button>
|
||||
<button class="selectCharacterButton selectCharacterButtonRight nes-btn" on:click|preventDefault={ selectRight }> > </button>
|
||||
<button class="selectCharacterButton selectCharacterButtonLeft nes-btn" on:click|preventDefault={selectLeft}>
|
||||
<
|
||||
</button>
|
||||
<button class="selectCharacterButton selectCharacterButtonRight nes-btn" on:click|preventDefault={selectRight}>
|
||||
>
|
||||
</button>
|
||||
</section>
|
||||
<section class="action">
|
||||
<button type="submit" class="selectCharacterSceneFormSubmit nes-btn is-primary" on:click|preventDefault={ cameraScene }>Continue</button>
|
||||
<button type="submit" class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn" on:click|preventDefault={ customizeScene }>Customize your Avatar</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="selectCharacterSceneFormSubmit nes-btn is-primary"
|
||||
on:click|preventDefault={cameraScene}>Continue</button
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn"
|
||||
on:click|preventDefault={customizeScene}>Customize your Avatar</button
|
||||
>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
form.selectCharacterScene {
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
color: #ebeeee;
|
||||
|
||||
section {
|
||||
margin: 10px;
|
||||
|
||||
&.action {
|
||||
text-align: center;
|
||||
margin-top: 55vh;
|
||||
}
|
||||
|
||||
h2 {
|
||||
form.selectCharacterScene {
|
||||
font-family: "Press Start 2P";
|
||||
margin: 1px;
|
||||
}
|
||||
pointer-events: auto;
|
||||
color: #ebeeee;
|
||||
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
section {
|
||||
margin: 10px;
|
||||
|
||||
button.selectCharacterButton {
|
||||
position: absolute;
|
||||
top: 33vh;
|
||||
margin: 0;
|
||||
}
|
||||
&.action {
|
||||
text-align: center;
|
||||
margin-top: 55vh;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
&.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button.selectCharacterButton {
|
||||
position: absolute;
|
||||
top: 33vh;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
&.selectCharacterButtonLeft {
|
||||
left: 33vw;
|
||||
}
|
||||
|
||||
&.selectCharacterButtonRight {
|
||||
right: 33vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
&.selectCharacterButtonLeft {
|
||||
left: 33vw;
|
||||
}
|
||||
|
||||
&.selectCharacterButtonRight {
|
||||
right: 33vw;
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
form.selectCharacterScene button.selectCharacterButtonLeft {
|
||||
left: 5vw;
|
||||
}
|
||||
form.selectCharacterScene button.selectCharacterButtonRight {
|
||||
right: 5vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
form.selectCharacterScene button.selectCharacterButtonLeft{
|
||||
left: 5vw;
|
||||
}
|
||||
form.selectCharacterScene button.selectCharacterButtonRight{
|
||||
right: 5vw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
37
front/src/Connexion/AxiosUtils.ts
Normal file
37
front/src/Connexion/AxiosUtils.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import axios from "axios";
|
||||
import * as rax from "retry-axios";
|
||||
import { errorStore } from "../Stores/ErrorStore";
|
||||
|
||||
/**
|
||||
* This instance of Axios will retry in case of an issue and display an error message as a HTML overlay.
|
||||
*/
|
||||
export const axiosWithRetry = axios.create();
|
||||
axiosWithRetry.defaults.raxConfig = {
|
||||
instance: axiosWithRetry,
|
||||
retry: Infinity,
|
||||
noResponseRetries: Infinity,
|
||||
|
||||
maxRetryAfter: 60_000,
|
||||
|
||||
// You can detect when a retry is happening, and figure out how many
|
||||
// retry attempts have been made
|
||||
onRetryAttempt: (err) => {
|
||||
const cfg = rax.getConfig(err);
|
||||
console.log(err);
|
||||
console.log(cfg);
|
||||
console.log(`Retry attempt #${cfg?.currentRetryAttempt} on URL '${err.config.url}'`);
|
||||
errorStore.addErrorMessage("Unable to connect to WorkAdventure. Are you connected to internet?", {
|
||||
closable: false,
|
||||
id: "axios_retry",
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
axiosWithRetry.interceptors.response.use((res) => {
|
||||
if (res.status < 400) {
|
||||
errorStore.clearMessageById("axios_retry");
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
const interceptorId = rax.attach(axiosWithRetry);
|
@ -11,6 +11,7 @@ import { loginSceneVisibleIframeStore } from "../Stores/LoginSceneStore";
|
||||
import { userIsConnected } from "../Stores/MenuStore";
|
||||
import { analyticsClient } from "../Administration/AnalyticsClient";
|
||||
import { gameManager } from "../Phaser/Game/GameManager";
|
||||
import { axiosWithRetry } from "./AxiosUtils";
|
||||
|
||||
class ConnectionManager {
|
||||
private localUser!: LocalUser;
|
||||
@ -233,7 +234,7 @@ class ConnectionManager {
|
||||
}
|
||||
|
||||
public async anonymousLogin(isBenchmark: boolean = false): Promise<void> {
|
||||
const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data);
|
||||
const data = await axiosWithRetry.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data);
|
||||
this.localUser = new LocalUser(data.userUuid, [], data.email);
|
||||
this.authToken = data.authToken;
|
||||
if (!isBenchmark) {
|
||||
|
@ -1,7 +1,10 @@
|
||||
import * as rax from "retry-axios";
|
||||
import Axios from "axios";
|
||||
import { CONTACT_URL, PUSHER_URL, DISABLE_ANONYMOUS, OPID_LOGIN_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable";
|
||||
import type { CharacterTexture } from "./LocalUser";
|
||||
import { localUserStore } from "./LocalUserStore";
|
||||
import axios from "axios";
|
||||
import { axiosWithRetry } from "./AxiosUtils";
|
||||
|
||||
export class MapDetail {
|
||||
constructor(public readonly mapUrl: string, public readonly textures: CharacterTexture[] | undefined) {}
|
||||
@ -90,7 +93,7 @@ export class Room {
|
||||
|
||||
private async getMapDetail(): Promise<MapDetail | RoomRedirect> {
|
||||
try {
|
||||
const result = await Axios.get(`${PUSHER_URL}/map`, {
|
||||
const result = await axiosWithRetry.get(`${PUSHER_URL}/map`, {
|
||||
params: {
|
||||
playUri: this.roomUrl.toString(),
|
||||
authToken: localUserStore.getAuthToken(),
|
||||
|
@ -255,6 +255,9 @@ export class RoomConnection implements RoomConnection {
|
||||
warningContainerStore.activateWarningContainer();
|
||||
} else if (message.hasRefreshroommessage()) {
|
||||
//todo: implement a way to notify the user the room was refreshed.
|
||||
} else if (message.hasErrormessage()) {
|
||||
const errorMessage = message.getErrormessage() as ErrorMessage;
|
||||
console.error("An error occurred server side: " + errorMessage.getMessage());
|
||||
} else {
|
||||
throw new Error("Unknown message received");
|
||||
}
|
||||
|
@ -271,8 +271,10 @@ export class GameScene extends DirtyScene {
|
||||
// 127.0.0.1, localhost and *.localhost are considered secure, even on HTTP.
|
||||
// So if we are in https, we can still try to load a HTTP local resource (can be useful for testing purposes)
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure
|
||||
const url = new URL(file.src);
|
||||
const host = url.host.split(":")[ 0 ];
|
||||
const base = new URL(window.location.href);
|
||||
base.pathname = "";
|
||||
const url = new URL(file.src, base.toString());
|
||||
const host = url.host.split(":")[0];
|
||||
if (
|
||||
window.location.protocol === "https:" &&
|
||||
file.src === this.MapUrlFile &&
|
||||
@ -324,12 +326,11 @@ export class GameScene extends DirtyScene {
|
||||
this.onMapLoad(data);
|
||||
}
|
||||
|
||||
this.load.bitmapFont("main_font", "resources/fonts/arcade.png", "resources/fonts/arcade.xml");
|
||||
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this.load as any).rexWebFont({
|
||||
custom: {
|
||||
families: [ "Press Start 2P" ],
|
||||
urls: [ "/resources/fonts/fonts.css" ],
|
||||
families: ["Press Start 2P"],
|
||||
urls: ["/resources/fonts/fonts.css"],
|
||||
testString: "abcdefg",
|
||||
},
|
||||
});
|
||||
@ -375,7 +376,7 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
}
|
||||
|
||||
for (const [ itemType, objectsOfType ] of this.objectsByType) {
|
||||
for (const [itemType, objectsOfType] of this.objectsByType) {
|
||||
// FIXME: we would ideally need for the loader to WAIT for the import to be performed, which means writing our own loader plugin.
|
||||
|
||||
let itemFactory: ItemFactoryInterface;
|
||||
@ -406,7 +407,7 @@ export class GameScene extends DirtyScene {
|
||||
// TODO: we should pass here a factory to create sprites (maybe?)
|
||||
|
||||
// Do we have a state for this object?
|
||||
const state = roomJoinedAnswer.items[ object.id ];
|
||||
const state = roomJoinedAnswer.items[object.id];
|
||||
|
||||
const actionableItem = itemFactory.factory(this, object, state);
|
||||
this.actionableItems.set(actionableItem.getId(), actionableItem);
|
||||
@ -636,7 +637,7 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
});
|
||||
|
||||
Promise.all([ this.connectionAnswerPromise as Promise<unknown>, ...scriptPromises ]).then(() => {
|
||||
Promise.all([this.connectionAnswerPromise as Promise<unknown>, ...scriptPromises]).then(() => {
|
||||
this.scene.wake();
|
||||
});
|
||||
}
|
||||
@ -724,8 +725,8 @@ export class GameScene extends DirtyScene {
|
||||
if (item === undefined) {
|
||||
console.warn(
|
||||
'Received an event about object "' +
|
||||
message.itemId +
|
||||
'" but cannot find this item on the map.'
|
||||
message.itemId +
|
||||
'" but cannot find this item on the map.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -933,8 +934,8 @@ export class GameScene extends DirtyScene {
|
||||
} else {
|
||||
console.error(
|
||||
"Error while opening a popup. Cannot find an object on the map with name '" +
|
||||
openPopupEvent.targetObject +
|
||||
"'. The first parameter of WA.openPopup() must be the name of a rectangle object in your map."
|
||||
openPopupEvent.targetObject +
|
||||
"'. The first parameter of WA.openPopup() must be the name of a rectangle object in your map."
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -1198,7 +1199,7 @@ export class GameScene extends DirtyScene {
|
||||
const jsonTilesetDir = eventTileset.url.substr(0, eventTileset.url.lastIndexOf("/"));
|
||||
//Initialise the firstgid to 1 because if there is no tileset in the tilemap, the firstgid will be 1
|
||||
let newFirstgid = 1;
|
||||
const lastTileset = this.mapFile.tilesets[ this.mapFile.tilesets.length - 1 ];
|
||||
const lastTileset = this.mapFile.tilesets[this.mapFile.tilesets.length - 1];
|
||||
if (lastTileset) {
|
||||
//If there is at least one tileset in the tilemap then calculate the firstgid of the new tileset
|
||||
newFirstgid = lastTileset.firstgid + lastTileset.tilecount;
|
||||
@ -1298,14 +1299,14 @@ export class GameScene extends DirtyScene {
|
||||
if (phaserLayers === []) {
|
||||
console.warn(
|
||||
'Could not find layer with name that contains "' +
|
||||
layerName +
|
||||
'" when calling WA.hideLayer / WA.showLayer'
|
||||
layerName +
|
||||
'" when calling WA.hideLayer / WA.showLayer'
|
||||
);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < phaserLayers.length; i++) {
|
||||
phaserLayers[ i ].setVisible(visible);
|
||||
phaserLayers[ i ].setCollisionByProperty({ collides: true }, visible);
|
||||
phaserLayers[i].setVisible(visible);
|
||||
phaserLayers[i].setCollisionByProperty({ collides: true }, visible);
|
||||
}
|
||||
}
|
||||
this.markDirty();
|
||||
|
@ -3,6 +3,7 @@ import { Scene } from "phaser";
|
||||
import { ErrorScene, ErrorSceneName } from "../Reconnecting/ErrorScene";
|
||||
import { WAError } from "../Reconnecting/WAError";
|
||||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene";
|
||||
|
||||
export const EntrySceneName = "EntryScene";
|
||||
|
||||
@ -17,6 +18,14 @@ export class EntryScene extends Scene {
|
||||
});
|
||||
}
|
||||
|
||||
// From the very start, let's preload images used in the ReconnectingScene.
|
||||
preload() {
|
||||
this.load.image(ReconnectingTextures.icon, "static/images/favicons/favicon-32x32.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
this.load.bitmapFont(ReconnectingTextures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml");
|
||||
this.load.spritesheet("cat", "resources/characters/pipoya/Cat 01-1.png", { frameWidth: 32, frameHeight: 32 });
|
||||
}
|
||||
|
||||
create() {
|
||||
gameManager
|
||||
.init(this.scene)
|
||||
|
@ -4,6 +4,7 @@ import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Text = Phaser.GameObjects.Text;
|
||||
import ScenePlugin = Phaser.Scenes.ScenePlugin;
|
||||
import { WAError } from "./WAError";
|
||||
import Axios from "axios";
|
||||
|
||||
export const ErrorSceneName = "ErrorScene";
|
||||
enum Textures {
|
||||
@ -36,7 +37,11 @@ export class ErrorScene extends Phaser.Scene {
|
||||
preload() {
|
||||
this.load.image(Textures.icon, "static/images/favicons/favicon-32x32.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
this.load.bitmapFont(Textures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml");
|
||||
if (!this.cache.bitmapFont.has("main_font")) {
|
||||
// We put this inside a "if" because despite the cache, Phaser will make a query to the XML file. And if there is no connection (which
|
||||
// is not unlikely given the fact we are in an error scene), this will cause an error.
|
||||
this.load.bitmapFont(Textures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml");
|
||||
}
|
||||
this.load.spritesheet("cat", "resources/characters/pipoya/Cat 01-1.png", { frameWidth: 32, frameHeight: 32 });
|
||||
}
|
||||
|
||||
@ -71,9 +76,9 @@ export class ErrorScene extends Phaser.Scene {
|
||||
/**
|
||||
* Displays the error page, with an error message matching the "error" parameters passed in.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public static showError(error: any, scene: ScenePlugin): void {
|
||||
public static showError(error: unknown, scene: ScenePlugin): void {
|
||||
console.error(error);
|
||||
console.trace();
|
||||
|
||||
if (typeof error === "string" || error instanceof String) {
|
||||
scene.start(ErrorSceneName, {
|
||||
@ -86,9 +91,10 @@ export class ErrorScene extends Phaser.Scene {
|
||||
subTitle: error.subTitle,
|
||||
message: error.details,
|
||||
});
|
||||
} else if (error.response) {
|
||||
} else if (Axios.isAxiosError(error) && error.response) {
|
||||
// Axios HTTP error
|
||||
// client received an error response (5xx, 4xx)
|
||||
console.error("Axios error. Request:", error.request, " - Response: ", error.response);
|
||||
scene.start(ErrorSceneName, {
|
||||
title:
|
||||
"HTTP " +
|
||||
@ -98,9 +104,10 @@ export class ErrorScene extends Phaser.Scene {
|
||||
subTitle: "An error occurred while accessing URL:",
|
||||
message: error.response.config.url,
|
||||
});
|
||||
} else if (error.request) {
|
||||
} else if (Axios.isAxiosError(error)) {
|
||||
// Axios HTTP error
|
||||
// client never received a response, or request never left
|
||||
console.error("Axios error. No full HTTP response received. Request to URL:", error.config.url);
|
||||
scene.start(ErrorSceneName, {
|
||||
title: "Network error",
|
||||
subTitle: error.message,
|
||||
|
@ -3,7 +3,7 @@ import Image = Phaser.GameObjects.Image;
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
|
||||
export const ReconnectingSceneName = "ReconnectingScene";
|
||||
enum ReconnectingTextures {
|
||||
export enum ReconnectingTextures {
|
||||
icon = "icon",
|
||||
mainFont = "main_font",
|
||||
}
|
||||
@ -41,7 +41,7 @@ export class ReconnectingScene extends Phaser.Scene {
|
||||
"Connection lost. Reconnecting..."
|
||||
);
|
||||
|
||||
const cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, "cat");
|
||||
const cat = this.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, "cat");
|
||||
this.anims.create({
|
||||
key: "right",
|
||||
frames: this.anims.generateFrameNumbers("cat", { start: 6, end: 8 }),
|
||||
|
@ -1,15 +1,27 @@
|
||||
import { writable } from "svelte/store";
|
||||
import { derived, writable } from "svelte/store";
|
||||
|
||||
interface ErrorMessage {
|
||||
id: string | undefined;
|
||||
closable: boolean; // Whether it can be closed by a user action or not
|
||||
message: string | number | boolean | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A store that contains a list of error messages to be displayed.
|
||||
*/
|
||||
function createErrorStore() {
|
||||
const { subscribe, set, update } = writable<string[]>([]);
|
||||
const { subscribe, set, update } = writable<ErrorMessage[]>([]);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
addErrorMessage: (e: string | Error): void => {
|
||||
update((messages: string[]) => {
|
||||
addErrorMessage: (
|
||||
e: string | Error,
|
||||
options?: {
|
||||
closable?: boolean;
|
||||
id?: string;
|
||||
}
|
||||
): void => {
|
||||
update((messages: ErrorMessage[]) => {
|
||||
let message: string;
|
||||
if (e instanceof Error) {
|
||||
message = e.message;
|
||||
@ -17,17 +29,35 @@ function createErrorStore() {
|
||||
message = e;
|
||||
}
|
||||
|
||||
if (!messages.includes(message)) {
|
||||
messages.push(message);
|
||||
if (!messages.find((errorMessage) => errorMessage.message === message)) {
|
||||
messages.push({
|
||||
message,
|
||||
closable: options?.closable ?? true,
|
||||
id: options?.id,
|
||||
});
|
||||
}
|
||||
|
||||
return messages;
|
||||
});
|
||||
},
|
||||
clearMessages: (): void => {
|
||||
set([]);
|
||||
clearMessageById: (id: string): void => {
|
||||
update((messages: ErrorMessage[]) => {
|
||||
messages = messages.filter((message) => message.id !== id);
|
||||
return messages;
|
||||
});
|
||||
},
|
||||
clearClosableMessages: (): void => {
|
||||
update((messages: ErrorMessage[]) => {
|
||||
messages = messages.filter((message) => message.closable);
|
||||
return messages;
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const errorStore = createErrorStore();
|
||||
|
||||
export const hasClosableMessagesInErrorStore = derived(errorStore, ($errorStore) => {
|
||||
const closableMessage = $errorStore.find((errorMessage) => errorMessage.closable);
|
||||
return !!closableMessage;
|
||||
});
|
||||
|
@ -32,6 +32,7 @@ module.exports = {
|
||||
rewrites: [{ from: /^_\/.*$/, to: "/index.html" }],
|
||||
disableDotRule: true,
|
||||
},
|
||||
liveReload: process.env.LIVE_RELOAD != "0" && process.env.LIVE_RELOAD != "false",
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
|
@ -2070,6 +2070,11 @@ escape-string-regexp@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
eslint-plugin-svelte3@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.2.1.tgz#f0f24150ecea3061c38c69e282bea26dc3e660c6"
|
||||
integrity sha512-YoBR9mLoKCjGghJ/gvpnFZKaMEu/VRcuxpSRS8KuozuEo7CdBH7bmBHa6FmMm0i4kJnOyx+PVsaptz96K6H/4Q==
|
||||
|
||||
eslint-scope@^5.0.0, eslint-scope@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
@ -2395,14 +2400,6 @@ file-entry-cache@^6.0.1:
|
||||
dependencies:
|
||||
flat-cache "^3.0.4"
|
||||
|
||||
file-loader@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d"
|
||||
integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
schema-utils "^3.0.0"
|
||||
|
||||
file-uri-to-path@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||
@ -4580,6 +4577,11 @@ prelude-ls@^1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
||||
|
||||
prettier-plugin-svelte@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.5.0.tgz#7922534729f7febe59b4c56c3f5360539f0d8ab1"
|
||||
integrity sha512-+iHY2uGChOngrgKielJUnqo74gIL/EO5oeWm8MftFWjEi213lq9QYTOwm1pv4lI1nA61tdgf80CF2i5zMcu1kw==
|
||||
|
||||
prettier@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6"
|
||||
@ -4934,6 +4936,11 @@ ret@~0.1.10:
|
||||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
|
||||
|
||||
retry-axios@^2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/retry-axios/-/retry-axios-2.6.0.tgz#d4dc5c8a8e73982e26a705e46a33df99a28723e0"
|
||||
integrity sha512-pOLi+Gdll3JekwuFjXO3fTq+L9lzMQGcSq7M5gIjExcl3Gu1hd4XXuf5o3+LuSBsaULQH7DiNbsqPd1chVpQGQ==
|
||||
|
||||
retry@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
||||
|
33
maps/tests/Variables/E2ETests/script.js
Normal file
33
maps/tests/Variables/E2ETests/script.js
Normal file
@ -0,0 +1,33 @@
|
||||
WA.onInit().then(() => {
|
||||
console.log('Trying to read variable "doorOpened" whose default property is true. This should display "true".');
|
||||
console.log('doorOpened', WA.state.loadVariable('doorOpened'));
|
||||
|
||||
console.log('Trying to set variable "not_exists". This should display an error in the console, followed by a log saying the error was caught.')
|
||||
WA.state.saveVariable('not_exists', 'foo').catch((e) => {
|
||||
console.log('Successfully caught error: ', e);
|
||||
});
|
||||
|
||||
console.log('Trying to set variable "myvar". This should work.');
|
||||
WA.state.saveVariable('myvar', {'foo': 'bar'});
|
||||
|
||||
console.log('Trying to read variable "myvar". This should display a {"foo": "bar"} object.');
|
||||
console.log(WA.state.loadVariable('myvar'));
|
||||
|
||||
console.log('Trying to set variable "myvar" using proxy. This should work.');
|
||||
WA.state.myvar = {'baz': 42};
|
||||
|
||||
console.log('Trying to read variable "myvar" using proxy. This should display a {"baz": 42} object.');
|
||||
console.log(WA.state.myvar);
|
||||
|
||||
console.log('Trying to set variable "config". This should not work because we are not logged as admin.');
|
||||
WA.state.saveVariable('config', {'foo': 'bar'}).catch(e => {
|
||||
console.log('Successfully caught error because variable "config" is not writable: ', e);
|
||||
});
|
||||
|
||||
console.log('Trying to read variable "readableByAdmin" that can only be read by "admin". We are not admin so we should not get the default value.');
|
||||
if (WA.state.readableByAdmin === true) {
|
||||
console.error('Failed test: readableByAdmin can be read.');
|
||||
} else {
|
||||
console.log('Success test: readableByAdmin was not read.');
|
||||
}
|
||||
});
|
47
maps/tests/Variables/E2ETests/shared_variables.html
Normal file
47
maps/tests/Variables/E2ETests/shared_variables.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script>
|
||||
var script = document.createElement('script');
|
||||
// Don't do this at home kids! The "document.referrer" part is actually inserting a XSS security.
|
||||
// We are OK in this precise case because the HTML page is hosted on the "maps" domain that contains only static files.
|
||||
document.head.appendChild(script);
|
||||
window.addEventListener('load', () => {
|
||||
console.log('On load');
|
||||
WA.onInit().then(() => {
|
||||
console.log('After WA init');
|
||||
const textField = document.getElementById('textField');
|
||||
textField.value = WA.state.textField;
|
||||
|
||||
textField.addEventListener('change', function (evt) {
|
||||
console.log('saving variable')
|
||||
WA.state.textField = this.value;
|
||||
});
|
||||
|
||||
WA.state.onVariableChange('textField').subscribe((value) => {
|
||||
console.log('variable changed received')
|
||||
textField.value = value;
|
||||
});
|
||||
|
||||
document.getElementById('btn').addEventListener('click', () => {
|
||||
console.log(WA.state.loadVariable('textField'));
|
||||
document.getElementById('placeholder').innerText = WA.state.loadVariable('textField');
|
||||
});
|
||||
|
||||
document.getElementById('setUndefined').addEventListener('click', () => {
|
||||
WA.state.textField = undefined;
|
||||
document.getElementById('textField').value = '';
|
||||
});
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<input type="text" id="textField" />
|
||||
|
||||
<button id="setUndefined">Delete variable</button>
|
||||
|
||||
<button id="btn">Display textField variable value</button>
|
||||
<div id="placeholder"></div>
|
||||
</body>
|
||||
</html>
|
131
maps/tests/Variables/E2ETests/shared_variables.json
Normal file
131
maps/tests/Variables/E2ETests/shared_variables.json
Normal file
@ -0,0 +1,131 @@
|
||||
{ "compressionlevel":-1,
|
||||
"height":10,
|
||||
"infinite":false,
|
||||
"layers":[
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":10,
|
||||
"id":1,
|
||||
"name":"floor",
|
||||
"opacity":1,
|
||||
"properties":[
|
||||
{
|
||||
"name":"openWebsite",
|
||||
"type":"string",
|
||||
"value":"shared_variables.html"
|
||||
},
|
||||
{
|
||||
"name":"openWebsiteAllowApi",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}],
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":10,
|
||||
"id":2,
|
||||
"name":"start",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"draworder":"topdown",
|
||||
"id":3,
|
||||
"name":"floorLayer",
|
||||
"objects":[
|
||||
{
|
||||
"height":67,
|
||||
"id":3,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"text":
|
||||
{
|
||||
"fontfamily":"Sans Serif",
|
||||
"pixelsize":11,
|
||||
"text":"Test:\nChange the form\nConnect with another user\n\nResult:\nThe form should open in the same state for the other user\nAlso, a change on one user is directly propagated to the other user",
|
||||
"wrap":true
|
||||
},
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":252.4375,
|
||||
"x":2.78125,
|
||||
"y":2.5
|
||||
},
|
||||
{
|
||||
"height":0,
|
||||
"id":5,
|
||||
"name":"textField",
|
||||
"point":true,
|
||||
"properties":[
|
||||
{
|
||||
"name":"default",
|
||||
"type":"string",
|
||||
"value":"default value"
|
||||
},
|
||||
{
|
||||
"name":"jsonSchema",
|
||||
"type":"string",
|
||||
"value":"{}"
|
||||
},
|
||||
{
|
||||
"name":"persist",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
},
|
||||
{
|
||||
"name":"readableBy",
|
||||
"type":"string",
|
||||
"value":""
|
||||
},
|
||||
{
|
||||
"name":"writableBy",
|
||||
"type":"string",
|
||||
"value":""
|
||||
}],
|
||||
"rotation":0,
|
||||
"type":"variable",
|
||||
"visible":true,
|
||||
"width":0,
|
||||
"x":57.5,
|
||||
"y":111
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":8,
|
||||
"nextobjectid":10,
|
||||
"orientation":"orthogonal",
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"2021.03.23",
|
||||
"tileheight":32,
|
||||
"tilesets":[
|
||||
{
|
||||
"columns":11,
|
||||
"firstgid":1,
|
||||
"image":"..\/tileset1.png",
|
||||
"imageheight":352,
|
||||
"imagewidth":352,
|
||||
"margin":0,
|
||||
"name":"tileset1",
|
||||
"spacing":0,
|
||||
"tilecount":121,
|
||||
"tileheight":32,
|
||||
"tilewidth":32
|
||||
}],
|
||||
"tilewidth":32,
|
||||
"type":"map",
|
||||
"version":1.5,
|
||||
"width":10
|
||||
}
|
@ -274,12 +274,12 @@ message ServerToClientMessage {
|
||||
SendJitsiJwtMessage sendJitsiJwtMessage = 11;
|
||||
SendUserMessage sendUserMessage = 12;
|
||||
BanUserMessage banUserMessage = 13;
|
||||
AdminRoomMessage adminRoomMessage = 14;
|
||||
//AdminRoomMessage adminRoomMessage = 14;
|
||||
WorldFullWarningMessage worldFullWarningMessage = 15;
|
||||
WorldFullMessage worldFullMessage = 16;
|
||||
RefreshRoomMessage refreshRoomMessage = 17;
|
||||
WorldConnexionMessage worldConnexionMessage = 18;
|
||||
EmoteEventMessage emoteEventMessage = 19;
|
||||
//EmoteEventMessage emoteEventMessage = 19;
|
||||
TokenExpiredMessage tokenExpiredMessage = 20;
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import { jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenMana
|
||||
import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi";
|
||||
import { SocketManager, socketManager } from "../Services/SocketManager";
|
||||
import { emitInBatch } from "../Services/IoSocketHelpers";
|
||||
import { ADMIN_SOCKETS_TOKEN, ADMIN_API_URL, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable";
|
||||
import { ADMIN_API_URL, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable";
|
||||
import { Zone } from "_Model/Zone";
|
||||
import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface";
|
||||
import { CharacterTexture } from "../Services/AdminApi/CharacterTexture";
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { ADMIN_API_URL, ADMIN_SOCKETS_TOKEN, ALLOW_ARTILLERY, SECRET_KEY } from "../Enum/EnvironmentVariable";
|
||||
import { uuid } from "uuidv4";
|
||||
import Jwt, { verify } from "jsonwebtoken";
|
||||
import { TokenInterface } from "../Controller/AuthenticateController";
|
||||
import { adminApi, AdminBannedData } from "../Services/AdminApi";
|
||||
import { ADMIN_SOCKETS_TOKEN, SECRET_KEY } from "../Enum/EnvironmentVariable";
|
||||
import Jwt from "jsonwebtoken";
|
||||
|
||||
export interface AuthTokenData {
|
||||
identifier: string; //will be a sub (id) if logged in or an uuid if anonymous
|
||||
|
@ -3,11 +3,28 @@ const BROWSER = process.env.BROWSER || "chrome --use-fake-ui-for-media-stream --
|
||||
module.exports = {
|
||||
"browsers": BROWSER,
|
||||
"hostname": "localhost",
|
||||
//"skipJsErrors": true,
|
||||
"src": "tests/",
|
||||
"screenshots": {
|
||||
"path": "screenshots/",
|
||||
"takeOnFails": true,
|
||||
"thumbnails": false,
|
||||
},
|
||||
"assertionTimeout": 20000,
|
||||
"selectorTimeout": 60000,
|
||||
|
||||
|
||||
"videoPath": "screenshots/videos",
|
||||
"videoOptions": {
|
||||
"failedOnly": true,
|
||||
}
|
||||
/*"skipJsErrors": true,
|
||||
"clientScripts": [ { "content": `
|
||||
window.addEventListener('error', function (e) {
|
||||
if (e instanceof Error && e.message.includes('_jp')) {
|
||||
console.log('Ignoring sockjs related error');
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
});
|
||||
` } ]*/
|
||||
}
|
||||
|
5
tests/Dockerfile
Normal file
5
tests/Dockerfile
Normal file
@ -0,0 +1,5 @@
|
||||
FROM testcafe/testcafe:1.17.1
|
||||
|
||||
USER root
|
||||
RUN apk add docker-compose
|
||||
USER user
|
734
tests/package-lock.json
generated
734
tests/package-lock.json
generated
@ -4,7 +4,13 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||
"@types/dockerode": "^3.3.0",
|
||||
"axios": "^0.24.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dockerode": "^3.3.1",
|
||||
"testcafe": "^1.17.1"
|
||||
}
|
||||
},
|
||||
@ -1804,6 +1810,123 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ffmpeg-installer/darwin-arm64": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-arm64/-/darwin-arm64-4.1.5.tgz",
|
||||
"integrity": "sha512-hYqTiP63mXz7wSQfuqfFwfLOfwwFChUedeCVKkBtl/cliaTM7/ePI9bVzfZ2c+dWu3TqCwLDRWNSJ5pqZl8otA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@ffmpeg-installer/darwin-x64": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-x64/-/darwin-x64-4.1.0.tgz",
|
||||
"integrity": "sha512-Z4EyG3cIFjdhlY8wI9aLUXuH8nVt7E9SlMVZtWvSPnm2sm37/yC2CwjUzyCQbJbySnef1tQwGG2Sx+uWhd9IAw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@ffmpeg-installer/ffmpeg": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/ffmpeg/-/ffmpeg-1.1.0.tgz",
|
||||
"integrity": "sha512-Uq4rmwkdGxIa9A6Bd/VqqYbT7zqh1GrT5/rFwCwKM70b42W5gIjWeVETq6SdcL0zXqDtY081Ws/iJWhr1+xvQg==",
|
||||
"optionalDependencies": {
|
||||
"@ffmpeg-installer/darwin-arm64": "4.1.5",
|
||||
"@ffmpeg-installer/darwin-x64": "4.1.0",
|
||||
"@ffmpeg-installer/linux-arm": "4.1.3",
|
||||
"@ffmpeg-installer/linux-arm64": "4.1.4",
|
||||
"@ffmpeg-installer/linux-ia32": "4.1.0",
|
||||
"@ffmpeg-installer/linux-x64": "4.1.0",
|
||||
"@ffmpeg-installer/win32-ia32": "4.1.0",
|
||||
"@ffmpeg-installer/win32-x64": "4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ffmpeg-installer/linux-arm": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm/-/linux-arm-4.1.3.tgz",
|
||||
"integrity": "sha512-NDf5V6l8AfzZ8WzUGZ5mV8O/xMzRag2ETR6+TlGIsMHp81agx51cqpPItXPib/nAZYmo55Bl2L6/WOMI3A5YRg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@ffmpeg-installer/linux-arm64": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm64/-/linux-arm64-4.1.4.tgz",
|
||||
"integrity": "sha512-dljEqAOD0oIM6O6DxBW9US/FkvqvQwgJ2lGHOwHDDwu/pX8+V0YsDL1xqHbj1DMX/+nP9rxw7G7gcUvGspSoKg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@ffmpeg-installer/linux-ia32": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-ia32/-/linux-ia32-4.1.0.tgz",
|
||||
"integrity": "sha512-0LWyFQnPf+Ij9GQGD034hS6A90URNu9HCtQ5cTqo5MxOEc7Rd8gLXrJvn++UmxhU0J5RyRE9KRYstdCVUjkNOQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@ffmpeg-installer/linux-x64": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-x64/-/linux-x64-4.1.0.tgz",
|
||||
"integrity": "sha512-Y5BWhGLU/WpQjOArNIgXD3z5mxxdV8c41C+U15nsE5yF8tVcdCGet5zPs5Zy3Ta6bU7haGpIzryutqCGQA/W8A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@ffmpeg-installer/win32-ia32": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-ia32/-/win32-ia32-4.1.0.tgz",
|
||||
"integrity": "sha512-FV2D7RlaZv/lrtdhaQ4oETwoFUsUjlUiasiZLDxhEUPdNDWcH1OU9K1xTvqz+OXLdsmYelUDuBS/zkMOTtlUAw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@ffmpeg-installer/win32-x64": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-x64/-/win32-x64-4.1.0.tgz",
|
||||
"integrity": "sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@miherlosev/esm": {
|
||||
"version": "3.2.26",
|
||||
"resolved": "https://registry.npmjs.org/@miherlosev/esm/-/esm-3.2.26.tgz",
|
||||
@ -1848,6 +1971,24 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/docker-modem": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.2.tgz",
|
||||
"integrity": "sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ==",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/ssh2": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/dockerode": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.0.tgz",
|
||||
"integrity": "sha512-3Mc0b2gnypJB8Gwmr+8UVPkwjpf4kg1gVxw8lAI4Y/EzpK50LixU1wBSPN9D+xqiw2Ubb02JO8oM0xpwzvi2mg==",
|
||||
"dependencies": {
|
||||
"@types/docker-modem": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/error-stack-parser": {
|
||||
"version": "1.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz",
|
||||
@ -1885,8 +2026,24 @@
|
||||
"node_modules/@types/node": {
|
||||
"version": "12.20.37",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz",
|
||||
"integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA=="
|
||||
},
|
||||
"node_modules/@types/ssh2": {
|
||||
"version": "0.5.49",
|
||||
"resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.49.tgz",
|
||||
"integrity": "sha512-ffxhQhJqgTzrw8NxHTgkaDtAmAj2qxCyoves7ztpRgqvzbHcZTpTcm+ATWuuCbPQzxnnF4F3SGGTLGEWTZpwqA==",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/ssh2-streams": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ssh2-streams": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.9.tgz",
|
||||
"integrity": "sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-hammerhead": {
|
||||
"version": "0.5.0",
|
||||
@ -2012,6 +2169,15 @@
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
|
||||
@ -2048,6 +2214,14 @@
|
||||
"node": ">= 4.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-dynamic-import-node": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
|
||||
@ -2153,12 +2327,46 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/bin-v8-flags-filter": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bin-v8-flags-filter/-/bin-v8-flags-filter-1.2.0.tgz",
|
||||
"integrity": "sha512-g8aeYkY7GhyyKRvQMBsJQZjhm2iCX3dKYvfrMpwVR8IxmUGrkpCBFoKbB9Rh0o3sTLCjU/1tFpZ4C7j3f+D+3g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bl/node_modules/readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@ -2225,6 +2433,30 @@
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
@ -2319,6 +2551,12 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/chrome-remote-interface": {
|
||||
"version": "0.30.1",
|
||||
"resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.30.1.tgz",
|
||||
@ -2449,6 +2687,20 @@
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cpu-features": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.2.tgz",
|
||||
"integrity": "sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"nan": "^2.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -2649,6 +2901,60 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/docker-modem": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.3.tgz",
|
||||
"integrity": "sha512-Tgkn2a+yiNP9FoZgMa/D9Wk+D2Db///0KOyKSYZRJa8w4+DzKyzQMkczKSdR/adQ0x46BOpeNkoyEOKjPhCzjw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"readable-stream": "^3.5.0",
|
||||
"split-ca": "^1.0.1",
|
||||
"ssh2": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/docker-modem/node_modules/readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/dockerode": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz",
|
||||
"integrity": "sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"docker-modem": "^3.0.0",
|
||||
"tar-fs": "~2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dockerode/node_modules/tar-fs": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
|
||||
"integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.3.906",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.906.tgz",
|
||||
@ -2852,6 +3158,31 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
|
||||
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@ -3100,6 +3431,26 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz",
|
||||
@ -3549,6 +3900,12 @@
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.29.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||
@ -3582,6 +3939,13 @@
|
||||
"npm": ">=1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
|
||||
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz",
|
||||
@ -4281,6 +4645,30 @@
|
||||
"integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/split-ca": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
|
||||
"integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ssh2": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.5.0.tgz",
|
||||
"integrity": "sha512-iUmRkhH9KGeszQwDW7YyyqjsMTf4z+0o48Cp4xOwlY5LjtbIAvyd3fwnsoUZW/hXmTCRA3yt7S/Jb9uVjErVlA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"asn1": "^0.2.4",
|
||||
"bcrypt-pbkdf": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"cpu-features": "0.0.2",
|
||||
"nan": "^2.15.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stackframe": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz",
|
||||
@ -4355,6 +4743,36 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/tar-stream": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tar-stream/node_modules/readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/testcafe": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz",
|
||||
@ -4889,6 +5307,12 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/type-detect": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||
@ -6330,6 +6754,69 @@
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@ffmpeg-installer/darwin-arm64": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-arm64/-/darwin-arm64-4.1.5.tgz",
|
||||
"integrity": "sha512-hYqTiP63mXz7wSQfuqfFwfLOfwwFChUedeCVKkBtl/cliaTM7/ePI9bVzfZ2c+dWu3TqCwLDRWNSJ5pqZl8otA==",
|
||||
"optional": true
|
||||
},
|
||||
"@ffmpeg-installer/darwin-x64": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-x64/-/darwin-x64-4.1.0.tgz",
|
||||
"integrity": "sha512-Z4EyG3cIFjdhlY8wI9aLUXuH8nVt7E9SlMVZtWvSPnm2sm37/yC2CwjUzyCQbJbySnef1tQwGG2Sx+uWhd9IAw==",
|
||||
"optional": true
|
||||
},
|
||||
"@ffmpeg-installer/ffmpeg": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/ffmpeg/-/ffmpeg-1.1.0.tgz",
|
||||
"integrity": "sha512-Uq4rmwkdGxIa9A6Bd/VqqYbT7zqh1GrT5/rFwCwKM70b42W5gIjWeVETq6SdcL0zXqDtY081Ws/iJWhr1+xvQg==",
|
||||
"requires": {
|
||||
"@ffmpeg-installer/darwin-arm64": "4.1.5",
|
||||
"@ffmpeg-installer/darwin-x64": "4.1.0",
|
||||
"@ffmpeg-installer/linux-arm": "4.1.3",
|
||||
"@ffmpeg-installer/linux-arm64": "4.1.4",
|
||||
"@ffmpeg-installer/linux-ia32": "4.1.0",
|
||||
"@ffmpeg-installer/linux-x64": "4.1.0",
|
||||
"@ffmpeg-installer/win32-ia32": "4.1.0",
|
||||
"@ffmpeg-installer/win32-x64": "4.1.0"
|
||||
}
|
||||
},
|
||||
"@ffmpeg-installer/linux-arm": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm/-/linux-arm-4.1.3.tgz",
|
||||
"integrity": "sha512-NDf5V6l8AfzZ8WzUGZ5mV8O/xMzRag2ETR6+TlGIsMHp81agx51cqpPItXPib/nAZYmo55Bl2L6/WOMI3A5YRg==",
|
||||
"optional": true
|
||||
},
|
||||
"@ffmpeg-installer/linux-arm64": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm64/-/linux-arm64-4.1.4.tgz",
|
||||
"integrity": "sha512-dljEqAOD0oIM6O6DxBW9US/FkvqvQwgJ2lGHOwHDDwu/pX8+V0YsDL1xqHbj1DMX/+nP9rxw7G7gcUvGspSoKg==",
|
||||
"optional": true
|
||||
},
|
||||
"@ffmpeg-installer/linux-ia32": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-ia32/-/linux-ia32-4.1.0.tgz",
|
||||
"integrity": "sha512-0LWyFQnPf+Ij9GQGD034hS6A90URNu9HCtQ5cTqo5MxOEc7Rd8gLXrJvn++UmxhU0J5RyRE9KRYstdCVUjkNOQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@ffmpeg-installer/linux-x64": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-x64/-/linux-x64-4.1.0.tgz",
|
||||
"integrity": "sha512-Y5BWhGLU/WpQjOArNIgXD3z5mxxdV8c41C+U15nsE5yF8tVcdCGet5zPs5Zy3Ta6bU7haGpIzryutqCGQA/W8A==",
|
||||
"optional": true
|
||||
},
|
||||
"@ffmpeg-installer/win32-ia32": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-ia32/-/win32-ia32-4.1.0.tgz",
|
||||
"integrity": "sha512-FV2D7RlaZv/lrtdhaQ4oETwoFUsUjlUiasiZLDxhEUPdNDWcH1OU9K1xTvqz+OXLdsmYelUDuBS/zkMOTtlUAw==",
|
||||
"optional": true
|
||||
},
|
||||
"@ffmpeg-installer/win32-x64": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-x64/-/win32-x64-4.1.0.tgz",
|
||||
"integrity": "sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==",
|
||||
"optional": true
|
||||
},
|
||||
"@miherlosev/esm": {
|
||||
"version": "3.2.26",
|
||||
"resolved": "https://registry.npmjs.org/@miherlosev/esm/-/esm-3.2.26.tgz",
|
||||
@ -6362,6 +6849,24 @@
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@types/docker-modem": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.2.tgz",
|
||||
"integrity": "sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/ssh2": "*"
|
||||
}
|
||||
},
|
||||
"@types/dockerode": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.0.tgz",
|
||||
"integrity": "sha512-3Mc0b2gnypJB8Gwmr+8UVPkwjpf4kg1gVxw8lAI4Y/EzpK50LixU1wBSPN9D+xqiw2Ubb02JO8oM0xpwzvi2mg==",
|
||||
"requires": {
|
||||
"@types/docker-modem": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/error-stack-parser": {
|
||||
"version": "1.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz",
|
||||
@ -6399,8 +6904,24 @@
|
||||
"@types/node": {
|
||||
"version": "12.20.37",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz",
|
||||
"integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA=="
|
||||
},
|
||||
"@types/ssh2": {
|
||||
"version": "0.5.49",
|
||||
"resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.49.tgz",
|
||||
"integrity": "sha512-ffxhQhJqgTzrw8NxHTgkaDtAmAj2qxCyoves7ztpRgqvzbHcZTpTcm+ATWuuCbPQzxnnF4F3SGGTLGEWTZpwqA==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/ssh2-streams": "*"
|
||||
}
|
||||
},
|
||||
"@types/ssh2-streams": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.9.tgz",
|
||||
"integrity": "sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"acorn-hammerhead": {
|
||||
"version": "0.5.0",
|
||||
@ -6498,6 +7019,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"assertion-error": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
|
||||
@ -6522,6 +7052,14 @@
|
||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
||||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"babel-plugin-dynamic-import-node": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
|
||||
@ -6600,12 +7138,45 @@
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"dev": true
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"bin-v8-flags-filter": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bin-v8-flags-filter/-/bin-v8-flags-filter-1.2.0.tgz",
|
||||
"integrity": "sha512-g8aeYkY7GhyyKRvQMBsJQZjhm2iCX3dKYvfrMpwVR8IxmUGrkpCBFoKbB9Rh0o3sTLCjU/1tFpZ4C7j3f+D+3g==",
|
||||
"dev": true
|
||||
},
|
||||
"bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@ -6659,6 +7230,16 @@
|
||||
"picocolors": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
@ -6734,6 +7315,12 @@
|
||||
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
|
||||
"dev": true
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||
"dev": true
|
||||
},
|
||||
"chrome-remote-interface": {
|
||||
"version": "0.30.1",
|
||||
"resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.30.1.tgz",
|
||||
@ -6842,6 +7429,16 @@
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"cpu-features": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.2.tgz",
|
||||
"integrity": "sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.14.1"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -7000,6 +7597,55 @@
|
||||
"path-type": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"docker-modem": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.3.tgz",
|
||||
"integrity": "sha512-Tgkn2a+yiNP9FoZgMa/D9Wk+D2Db///0KOyKSYZRJa8w4+DzKyzQMkczKSdR/adQ0x46BOpeNkoyEOKjPhCzjw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"readable-stream": "^3.5.0",
|
||||
"split-ca": "^1.0.1",
|
||||
"ssh2": "^1.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dockerode": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz",
|
||||
"integrity": "sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"docker-modem": "^3.0.0",
|
||||
"tar-fs": "~2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"tar-fs": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
|
||||
"integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.906",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.906.tgz",
|
||||
@ -7165,6 +7811,17 @@
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
|
||||
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA=="
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"dev": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@ -7357,6 +8014,12 @@
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz",
|
||||
@ -7699,6 +8362,12 @@
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
|
||||
"dev": true
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||
@ -7723,6 +8392,13 @@
|
||||
"integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
|
||||
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz",
|
||||
@ -8257,6 +8933,24 @@
|
||||
"integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
|
||||
"dev": true
|
||||
},
|
||||
"split-ca": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
|
||||
"integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=",
|
||||
"dev": true
|
||||
},
|
||||
"ssh2": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.5.0.tgz",
|
||||
"integrity": "sha512-iUmRkhH9KGeszQwDW7YyyqjsMTf4z+0o48Cp4xOwlY5LjtbIAvyd3fwnsoUZW/hXmTCRA3yt7S/Jb9uVjErVlA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asn1": "^0.2.4",
|
||||
"bcrypt-pbkdf": "^1.0.2",
|
||||
"cpu-features": "0.0.2",
|
||||
"nan": "^2.15.0"
|
||||
}
|
||||
},
|
||||
"stackframe": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz",
|
||||
@ -8316,6 +9010,32 @@
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"testcafe": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz",
|
||||
@ -8772,6 +9492,12 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||
"dev": true
|
||||
},
|
||||
"type-detect": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||
|
@ -1,8 +1,14 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"dockerode": "^3.3.1",
|
||||
"testcafe": "^1.17.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "testcafe"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||
"@types/dockerode": "^3.3.0",
|
||||
"axios": "^0.24.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,38 @@
|
||||
import {assertLogMessage} from "./utils/log";
|
||||
|
||||
const fs = require('fs')
|
||||
const fs = require('fs');
|
||||
const Docker = require('dockerode');
|
||||
import { Selector } from 'testcafe';
|
||||
import {userAlice} from "./utils/roles";
|
||||
import {login} from "./utils/roles";
|
||||
import {findContainer, rebootBack, rebootPusher, resetRedis, startContainer, stopContainer} from "./utils/containers";
|
||||
|
||||
// Note: we are also testing that we can connect if the URL contains a random query string
|
||||
fixture `Variables`
|
||||
.page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json?somerandomparam=1`;
|
||||
fixture `Reconnection`
|
||||
.page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/mousewheel.json`;
|
||||
|
||||
test("Test that connection can succeed even if WorkAdventure starts while pusher is down", async (t: TestController) => {
|
||||
// Let's stop the pusher
|
||||
const container = await findContainer('pusher');
|
||||
await stopContainer(container);
|
||||
|
||||
const errorMessage = Selector('.error-div');
|
||||
|
||||
await t
|
||||
.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/mousewheel.json')
|
||||
.expect(errorMessage.innerText).contains('Unable to connect to WorkAdventure')
|
||||
|
||||
await startContainer(container);
|
||||
|
||||
await login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/mousewheel.json');
|
||||
|
||||
t.ctx.passed = true;
|
||||
}).after(async t => {
|
||||
if (!t.ctx.passed) {
|
||||
console.log("Test failed. Browser logs:")
|
||||
console.log(await t.getBrowserConsoleMessages());
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
test("Test that variables cache in the back don't prevent setting a variable in case the map changes", async (t: TestController) => {
|
||||
// Let's start by visiting a map that DOES not have the variable.
|
||||
fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_1.json', '../maps/tests/Variables/Cache/variables_tmp.json');
|
||||
@ -18,11 +43,9 @@ test("Test that variables cache in the back don't prevent setting a variable in
|
||||
// Let's REPLACE the map by a map that has a new variable
|
||||
// At this point, the back server contains a cache of the old map (with no variables)
|
||||
fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_2.json', '../maps/tests/Variables/Cache/variables_tmp.json');
|
||||
await t.openWindow('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json');
|
||||
|
||||
await t.resizeWindow(960, 800);
|
||||
|
||||
await t.useRole(userAlice);
|
||||
await t.openWindow('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json')
|
||||
.resizeWindow(960, 800)
|
||||
.useRole(userAlice);
|
||||
//.takeScreenshot('after_switch.png');
|
||||
|
||||
// Let's check we successfully manage to save the variable value.
|
||||
@ -35,3 +58,4 @@ test("Test that variables cache in the back don't prevent setting a variable in
|
||||
console.log(await t.getBrowserConsoleMessages());
|
||||
}
|
||||
});
|
||||
*/
|
85
tests/tests/utils/containers.ts
Normal file
85
tests/tests/utils/containers.ts
Normal file
@ -0,0 +1,85 @@
|
||||
//import Docker from "dockerode";
|
||||
//import * as Dockerode from "dockerode";
|
||||
import Dockerode = require( 'dockerode')
|
||||
const util = require('util');
|
||||
const exec = util.promisify(require('child_process').exec);
|
||||
const { execSync } = require('child_process');
|
||||
const path = require("path");
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Execute Docker compose, passing the correct host directory (in case this is run from the TestCafe container)
|
||||
*/
|
||||
export function dockerCompose(command: string): void {
|
||||
let param = '';
|
||||
const projectDir = process.env.PROJECT_DIR;
|
||||
|
||||
if (!projectDir && fs.existsSync('/project')) {
|
||||
// We are probably in the docker-image AND we did not pass PROJECT_DIR env variable
|
||||
throw new Error('Incorrect docker-compose command used to fire testcafe tests. You need to add a PROJECT_DIR environment variable. Please refer to the CONTRIBUTING.md guide');
|
||||
}
|
||||
|
||||
if (projectDir) {
|
||||
const dirName = path.basename(projectDir);
|
||||
param = '--project-name '+dirName+' --project-directory '+projectDir;
|
||||
}
|
||||
|
||||
let stdout = execSync('docker-compose '+param+' '+command, {
|
||||
cwd: __dirname + '/../../../'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a container ID based on the container name.
|
||||
*/
|
||||
export async function findContainer(name: string): Promise<Dockerode.ContainerInfo> {
|
||||
const docker = new Dockerode();
|
||||
|
||||
const containers = await docker.listContainers();
|
||||
|
||||
const foundContainer = containers.find((container) => container.State === 'running' && container.Names.find((containerName) => containerName.includes(name)));
|
||||
|
||||
if (foundContainer === undefined) {
|
||||
throw new Error('Could not find a running container whose name contains "'+name+'"');
|
||||
}
|
||||
|
||||
return foundContainer;
|
||||
}
|
||||
|
||||
export async function stopContainer(container: Dockerode.ContainerInfo): Promise<void> {
|
||||
const docker = new Dockerode();
|
||||
|
||||
await docker.getContainer(container.Id).stop();
|
||||
}
|
||||
|
||||
export async function startContainer(container: Dockerode.ContainerInfo): Promise<void> {
|
||||
const docker = new Dockerode();
|
||||
|
||||
await docker.getContainer(container.Id).start();
|
||||
}
|
||||
|
||||
export async function rebootBack(): Promise<void> {
|
||||
dockerCompose('up --force-recreate -d back');
|
||||
}
|
||||
|
||||
export function rebootTraefik(): void {
|
||||
dockerCompose('up --force-recreate -d reverse-proxy');
|
||||
}
|
||||
|
||||
export async function rebootPusher(): Promise<void> {
|
||||
dockerCompose('up --force-recreate -d pusher');
|
||||
}
|
||||
|
||||
export async function resetRedis(): Promise<void> {
|
||||
dockerCompose('stop redis');
|
||||
dockerCompose('rm -f redis');
|
||||
dockerCompose('up --force-recreate -d redis');
|
||||
}
|
||||
|
||||
export function stopRedis(): void {
|
||||
dockerCompose('stop redis');
|
||||
}
|
||||
|
||||
export function startRedis(): void {
|
||||
dockerCompose('start redis');
|
||||
}
|
31
tests/tests/utils/debug.ts
Normal file
31
tests/tests/utils/debug.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import axios from "axios";
|
||||
const fs = require('fs');
|
||||
|
||||
const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN;
|
||||
|
||||
export async function getPusherDump(): Promise<any> {
|
||||
let url = 'http://pusher.workadventure.localhost/dump?token='+ADMIN_API_TOKEN;
|
||||
if (fs.existsSync('/project')) {
|
||||
// We are inside a container. Let's use a direct route
|
||||
url = 'http://pusher:8080/dump?token='+ADMIN_API_TOKEN;
|
||||
}
|
||||
|
||||
return (await axios({
|
||||
url,
|
||||
method: 'get',
|
||||
})).data;
|
||||
}
|
||||
|
||||
export async function getBackDump(): Promise<any> {
|
||||
let url = 'http://api.workadventure.localhost/dump?token='+ADMIN_API_TOKEN;
|
||||
if (fs.existsSync('/project')) {
|
||||
// We are inside a container. Let's use a direct route
|
||||
url = 'http://back:8080/dump?token='+ADMIN_API_TOKEN;
|
||||
}
|
||||
|
||||
return (await axios({
|
||||
url,
|
||||
method: 'get',
|
||||
})).data;
|
||||
}
|
||||
|
@ -1,20 +1,15 @@
|
||||
import { Role } from 'testcafe';
|
||||
|
||||
export const userAlice = Role('http://play.workadventure.localhost/', async t => {
|
||||
await t
|
||||
.typeText('input[name="loginSceneName"]', 'Alice')
|
||||
.click('button.loginSceneFormSubmit')
|
||||
.click('button.selectCharacterButtonRight')
|
||||
.click('button.selectCharacterButtonRight')
|
||||
.click('button.selectCharacterSceneFormSubmit')
|
||||
.click('button.letsgo');
|
||||
});
|
||||
export function login(t: TestController, url: string, userName: string = "Alice", characterNumber: number = 2) {
|
||||
t = t
|
||||
.navigateTo(url)
|
||||
.typeText('input[name="loginSceneName"]', userName)
|
||||
.click('button.loginSceneFormSubmit');
|
||||
|
||||
export const userBob = Role('http://play.workadventure.localhost/', async t => {
|
||||
await t
|
||||
.typeText('input[name="loginSceneName"]', 'Bob')
|
||||
.click('button.loginSceneFormSubmit')
|
||||
.click('button.selectCharacterButtonRight')
|
||||
.click('button.selectCharacterSceneFormSubmit')
|
||||
for (let i = 0; i < characterNumber; i++) {
|
||||
t = t.click('button.selectCharacterButtonRight');
|
||||
}
|
||||
|
||||
return t.click('button.selectCharacterSceneFormSubmit')
|
||||
.click('button.letsgo');
|
||||
});
|
||||
}
|
||||
|
202
tests/tests/variables.ts
Normal file
202
tests/tests/variables.ts
Normal file
@ -0,0 +1,202 @@
|
||||
import {assertLogMessage} from "./utils/log";
|
||||
|
||||
const fs = require('fs');
|
||||
const Docker = require('dockerode');
|
||||
import { Selector } from 'testcafe';
|
||||
import {login} from "./utils/roles";
|
||||
import {
|
||||
findContainer,
|
||||
rebootBack,
|
||||
rebootPusher,
|
||||
resetRedis,
|
||||
rebootTraefik,
|
||||
startContainer,
|
||||
stopContainer, stopRedis, startRedis
|
||||
} from "./utils/containers";
|
||||
import {getBackDump, getPusherDump} from "./utils/debug";
|
||||
|
||||
// Note: we are also testing that passing a random query parameter does not cause any issue.
|
||||
fixture `Variables`
|
||||
.page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1`;
|
||||
|
||||
test("Test that variables storage works", async (t: TestController) => {
|
||||
|
||||
const variableInput = Selector('#textField');
|
||||
|
||||
await resetRedis();
|
||||
|
||||
await Promise.all([
|
||||
rebootBack(),
|
||||
rebootPusher(),
|
||||
]);
|
||||
|
||||
//const mainWindow = await t.getCurrentWindow();
|
||||
|
||||
await login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1');
|
||||
|
||||
await t //.useRole(userAliceOnPage('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1'))
|
||||
.switchToIframe("#cowebsite-buffer iframe")
|
||||
.expect(variableInput.value).eql('default value')
|
||||
.selectText(variableInput)
|
||||
.pressKey('delete')
|
||||
.typeText(variableInput, 'new value')
|
||||
.pressKey('tab')
|
||||
.switchToMainWindow()
|
||||
//.switchToWindow(mainWindow)
|
||||
.wait(500)
|
||||
// reload
|
||||
.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json')
|
||||
.switchToIframe("#cowebsite-buffer iframe")
|
||||
.expect(variableInput.value).eql('new value')
|
||||
//.debug()
|
||||
.switchToMainWindow()
|
||||
//.wait(5000)
|
||||
//.switchToWindow(mainWindow)
|
||||
/*
|
||||
.wait(5000)
|
||||
//.debug()
|
||||
.switchToIframe("#cowebsite-buffer iframe")
|
||||
.expect(variableInput.value).eql('new value')
|
||||
.switchToMainWindow();*/
|
||||
|
||||
// Now, let's kill the reverse proxy to cut the connexion
|
||||
//console.log('Rebooting traefik');
|
||||
rebootTraefik();
|
||||
//console.log('Rebooting done');
|
||||
|
||||
// Maybe we should:
|
||||
// 1: stop Traefik
|
||||
// 2: detect reconnecting screen
|
||||
// 3: start Traefik again
|
||||
|
||||
|
||||
await t
|
||||
.switchToIframe("#cowebsite-buffer iframe")
|
||||
.expect(variableInput.value).eql('new value')
|
||||
|
||||
stopRedis();
|
||||
|
||||
// Redis is stopped, let's try to modify a variable.
|
||||
await t.selectText(variableInput)
|
||||
.pressKey('delete')
|
||||
.typeText(variableInput, 'value set while Redis stopped')
|
||||
.pressKey('tab')
|
||||
.switchToMainWindow()
|
||||
|
||||
startRedis();
|
||||
|
||||
// Navigate to some other map so that the pusher connection is freed.
|
||||
await t.navigateTo('http://maps.workadventure.localhost/tests/')
|
||||
.wait(3000);
|
||||
|
||||
|
||||
const backDump = await getBackDump();
|
||||
//console.log('backDump', backDump);
|
||||
for (const room of backDump) {
|
||||
if (room.roomUrl === 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') {
|
||||
throw new Error('Room still found in back');
|
||||
}
|
||||
}
|
||||
|
||||
const pusherDump = await getPusherDump();
|
||||
//console.log('pusherDump', pusherDump);
|
||||
await t.expect(pusherDump['http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json']).eql(undefined);
|
||||
|
||||
await t.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json')
|
||||
.switchToIframe("#cowebsite-buffer iframe")
|
||||
// Redis will reconnect automatically and will store the variable on reconnect!
|
||||
// So we should see the new value.
|
||||
.expect(variableInput.value).eql('value set while Redis stopped')
|
||||
.switchToMainWindow()
|
||||
|
||||
|
||||
// Now, let's try to kill / reboot the back
|
||||
await rebootBack();
|
||||
|
||||
await t.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json')
|
||||
.switchToIframe("#cowebsite-buffer iframe")
|
||||
.expect(variableInput.value).eql('value set while Redis stopped')
|
||||
.selectText(variableInput)
|
||||
.pressKey('delete')
|
||||
.typeText(variableInput, 'value set after back restart')
|
||||
.pressKey('tab')
|
||||
.switchToMainWindow()
|
||||
|
||||
.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json')
|
||||
.switchToIframe("#cowebsite-buffer iframe")
|
||||
// Redis will reconnect automatically and will store the variable on reconnect!
|
||||
// So we should see the new value.
|
||||
.expect(variableInput.value).eql('value set after back restart')
|
||||
.switchToMainWindow()
|
||||
|
||||
// Now, let's try to kill / reboot the back
|
||||
await rebootPusher();
|
||||
|
||||
await t.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json')
|
||||
.switchToIframe("#cowebsite-buffer iframe")
|
||||
.expect(variableInput.value).eql('value set after back restart')
|
||||
.selectText(variableInput)
|
||||
.pressKey('delete')
|
||||
.typeText(variableInput, 'value set after pusher restart')
|
||||
.pressKey('tab')
|
||||
.switchToMainWindow()
|
||||
|
||||
.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json')
|
||||
.switchToIframe("#cowebsite-buffer iframe")
|
||||
// Redis will reconnect automatically and will store the variable on reconnect!
|
||||
// So we should see the new value.
|
||||
.expect(variableInput.value).eql('value set after pusher restart')
|
||||
.switchToMainWindow()
|
||||
|
||||
|
||||
t.ctx.passed = true;
|
||||
}).after(async t => {
|
||||
if (!t.ctx.passed) {
|
||||
console.log("Test 'Test that variables storage works' failed. Browser logs:")
|
||||
try {
|
||||
console.log(await t.getBrowserConsoleMessages());
|
||||
} catch (e) {
|
||||
console.error('Error while fetching browser logs (maybe linked to a closed iframe?)', e);
|
||||
try {
|
||||
console.log('Logs from main window:');
|
||||
console.log(await t.switchToMainWindow().getBrowserConsoleMessages());
|
||||
} catch (e) {
|
||||
console.error('Unable to retrieve logs', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test("Test that variables cache in the back don't prevent setting a variable in case the map changes", async (t: TestController) => {
|
||||
// Let's start by visiting a map that DOES not have the variable.
|
||||
fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_1.json', '../maps/tests/Variables/Cache/variables_tmp.json');
|
||||
|
||||
//const aliceOnPageTmp = userAliceOnPage('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json');
|
||||
|
||||
await login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json', 'Alice', 2);
|
||||
|
||||
//.takeScreenshot('before_switch.png');
|
||||
|
||||
// Let's REPLACE the map by a map that has a new variable
|
||||
// At this point, the back server contains a cache of the old map (with no variables)
|
||||
fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_2.json', '../maps/tests/Variables/Cache/variables_tmp.json');
|
||||
await t.openWindow('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json')
|
||||
.resizeWindow(960, 800)
|
||||
|
||||
await login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json', 'Bob', 3);
|
||||
|
||||
//.takeScreenshot('after_switch.png');
|
||||
|
||||
// Let's check we successfully manage to save the variable value.
|
||||
await assertLogMessage(t, 'SUCCESS!');
|
||||
|
||||
t.ctx.passed = true;
|
||||
}).after(async t => {
|
||||
if (!t.ctx.passed) {
|
||||
console.log("Test 'Test that variables cache in the back don't prevent setting a variable in case the map changes' failed. Browser logs:")
|
||||
console.log(await t.getBrowserConsoleMessages());
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user