Merge branch 'develop' of github.com:thecodingmachine/workadventure into metadataScriptingApi
This commit is contained in:
commit
2ee62c9e9e
@ -10,6 +10,8 @@ START_ROOM_URL=/_/global/maps.workadventure.localhost/Floor0/floor0.json
|
||||
# If you are using Coturn, this is the value of the "static-auth-secret" parameter in your coturn config file.
|
||||
# Keep empty if you are sharing hard coded / clear text credentials.
|
||||
TURN_STATIC_AUTH_SECRET=
|
||||
DISABLE_NOTIFICATIONS=true
|
||||
SKIP_RENDER_OPTIMIZATIONS=false
|
||||
|
||||
# The email address used by Let's encrypt to send renewal warnings (compulsory)
|
||||
ACME_EMAIL=
|
||||
|
30
.github/workflows/build-and-deploy.yml
vendored
30
.github/workflows/build-and-deploy.yml
vendored
@ -2,7 +2,7 @@ name: Build, push and deploy Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branch: [master]
|
||||
branches: [master]
|
||||
release:
|
||||
types: [created]
|
||||
pull_request:
|
||||
@ -16,7 +16,7 @@ env:
|
||||
jobs:
|
||||
|
||||
build-front:
|
||||
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -36,11 +36,11 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: thecodingmachine/workadventure-front
|
||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
|
||||
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||
add_git_labels: true
|
||||
|
||||
build-back:
|
||||
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -59,11 +59,11 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: thecodingmachine/workadventure-back
|
||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
|
||||
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||
add_git_labels: true
|
||||
|
||||
build-pusher:
|
||||
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -82,11 +82,11 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: thecodingmachine/workadventure-pusher
|
||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
|
||||
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||
add_git_labels: true
|
||||
|
||||
build-uploader:
|
||||
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -105,11 +105,11 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: thecodingmachine/workadventure-uploader
|
||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
|
||||
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||
add_git_labels: true
|
||||
|
||||
build-maps:
|
||||
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -129,7 +129,7 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: thecodingmachine/workadventure-maps
|
||||
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
|
||||
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||
add_git_labels: true
|
||||
|
||||
deeploy:
|
||||
@ -140,7 +140,7 @@ jobs:
|
||||
- build-maps
|
||||
- build-uploader
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -158,13 +158,13 @@ jobs:
|
||||
JITSI_URL: ${{ secrets.JITSI_URL }}
|
||||
SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }}
|
||||
TURN_STATIC_AUTH_SECRET: ${{ secrets.TURN_STATIC_AUTH_SECRET }}
|
||||
DEPLOY_REF: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || 'master' }}
|
||||
DEPLOY_REF: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||
with:
|
||||
namespace: workadventure-${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || 'master' }}
|
||||
namespace: workadventure-${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
|
||||
|
||||
- name: Add a comment in PR
|
||||
uses: unsplash/comment-on-pr@v1.2.0
|
||||
if: ${{ github.event.pull_request }}
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
2
.github/workflows/continuous_integration.yml
vendored
2
.github/workflows/continuous_integration.yml
vendored
@ -49,7 +49,7 @@ jobs:
|
||||
- name: "Build"
|
||||
run: yarn run build
|
||||
env:
|
||||
API_URL: "localhost:8080"
|
||||
PUSHER_URL: "//localhost:8080"
|
||||
working-directory: "front"
|
||||
|
||||
- name: "Lint"
|
||||
|
@ -2,7 +2,7 @@
|
||||
local env = std.extVar("env"),
|
||||
local namespace = env.DEPLOY_REF,
|
||||
local tag = namespace,
|
||||
local url = if namespace == "master" then "workadventu.re" else namespace+".test.workadventu.re",
|
||||
local url = namespace+".test.workadventu.re",
|
||||
// develop branch does not use admin because of issue with SSL certificate of admin as of now.
|
||||
local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://"+url else null,
|
||||
"$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json",
|
||||
@ -25,10 +25,7 @@
|
||||
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
|
||||
} + (if adminUrl != null then {
|
||||
"ADMIN_API_URL": adminUrl,
|
||||
} else {}) + if namespace != "master" then {
|
||||
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
|
||||
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
|
||||
}
|
||||
} else {})
|
||||
},
|
||||
"back2": {
|
||||
"image": "thecodingmachine/workadventure-back:"+tag,
|
||||
@ -47,10 +44,7 @@
|
||||
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
|
||||
} + (if adminUrl != null then {
|
||||
"ADMIN_API_URL": adminUrl,
|
||||
} else {}) + if namespace != "master" then {
|
||||
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
|
||||
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
|
||||
}
|
||||
} else {})
|
||||
},
|
||||
"pusher": {
|
||||
"replicas": 2,
|
||||
@ -69,10 +63,7 @@
|
||||
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
|
||||
} + (if adminUrl != null then {
|
||||
"ADMIN_API_URL": adminUrl,
|
||||
} else {}) + if namespace != "master" then {
|
||||
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
|
||||
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
|
||||
}
|
||||
} else {})
|
||||
},
|
||||
"front": {
|
||||
"image": "thecodingmachine/workadventure-front:"+tag,
|
||||
|
@ -33,6 +33,8 @@ services:
|
||||
STARTUP_COMMAND_1: ./templater.sh
|
||||
STARTUP_COMMAND_2: yarn install
|
||||
TURN_SERVER: "turn:localhost:3478,turns:localhost:5349"
|
||||
DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS"
|
||||
SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS"
|
||||
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
|
||||
# Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container
|
||||
TURN_USER: ""
|
||||
|
@ -33,6 +33,8 @@ services:
|
||||
STARTUP_COMMAND_2: yarn install
|
||||
STUN_SERVER: "stun:stun.l.google.com:19302"
|
||||
TURN_SERVER: "turn:coturn.workadventure.localhost:3478,turns:coturn.workadventure.localhost:5349"
|
||||
DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS"
|
||||
SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS"
|
||||
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
|
||||
# Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container
|
||||
TURN_USER: ""
|
||||
|
@ -25,6 +25,15 @@
|
||||
],
|
||||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-explicit-any": "error"
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
|
||||
// TODO: remove those ignored rules and write a stronger code!
|
||||
"@typescript-eslint/no-floating-promises": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/restrict-plus-operands": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||
"@typescript-eslint/restrict-template-expressions": "off"
|
||||
}
|
||||
}
|
||||
|
2
front/dist/index.tmpl.html
vendored
2
front/dist/index.tmpl.html
vendored
@ -38,6 +38,8 @@
|
||||
<div class="main-container" id="main-container">
|
||||
<!-- Create the editor container -->
|
||||
<div id="game" class="game">
|
||||
<div id="svelte-overlay">
|
||||
</div>
|
||||
<div id="game-overlay" class="game-overlay">
|
||||
<div id="main-section" class="main-section">
|
||||
</div>
|
||||
|
BIN
front/dist/resources/logos/logo-WA-min.png
vendored
Normal file
BIN
front/dist/resources/logos/logo-WA-min.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -4,25 +4,34 @@
|
||||
"main": "index.js",
|
||||
"license": "SEE LICENSE IN LICENSE.txt",
|
||||
"devDependencies": {
|
||||
"@tsconfig/svelte": "^1.0.10",
|
||||
"@types/google-protobuf": "^3.7.3",
|
||||
"@types/jasmine": "^3.5.10",
|
||||
"@types/mini-css-extract-plugin": "^1.4.3",
|
||||
"@types/node": "^15.3.0",
|
||||
"@types/quill": "^1.3.7",
|
||||
"@typescript-eslint/eslint-plugin": "^2.26.0",
|
||||
"@typescript-eslint/parser": "^2.26.0",
|
||||
"css-loader": "^5.1.3",
|
||||
"eslint": "^6.8.0",
|
||||
"html-webpack-plugin": "^4.3.0",
|
||||
"@types/webpack-dev-server": "^3.11.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
||||
"@typescript-eslint/parser": "^4.23.0",
|
||||
"css-loader": "^5.2.4",
|
||||
"eslint": "^7.26.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.2.9",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"jasmine": "^3.5.0",
|
||||
"mini-css-extract-plugin": "^1.3.9",
|
||||
"sass": "^1.32.8",
|
||||
"sass-loader": "10.1.1",
|
||||
"ts-loader": "^6.2.2",
|
||||
"ts-node": "^8.10.2",
|
||||
"typescript": "^3.8.3",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.10.3",
|
||||
"webpack-merge": "^4.2.2"
|
||||
"mini-css-extract-plugin": "^1.6.0",
|
||||
"node-polyfill-webpack-plugin": "^1.1.2",
|
||||
"sass": "^1.32.12",
|
||||
"sass-loader": "^11.1.0",
|
||||
"svelte": "^3.38.2",
|
||||
"svelte-loader": "^3.1.1",
|
||||
"svelte-preprocess": "^4.7.3",
|
||||
"ts-loader": "^9.1.2",
|
||||
"ts-node": "^9.1.1",
|
||||
"tsconfig-paths": "^3.9.0",
|
||||
"typescript": "^4.2.4",
|
||||
"webpack": "^5.37.0",
|
||||
"webpack-cli": "^4.7.0",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/simple-peer": "^9.6.0",
|
||||
@ -36,13 +45,12 @@
|
||||
"quill": "1.3.6",
|
||||
"rxjs": "^6.6.3",
|
||||
"simple-peer": "^9.6.2",
|
||||
"socket.io-client": "^2.3.0",
|
||||
"webpack-require-http": "^0.4.3"
|
||||
"socket.io-client": "^2.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --open",
|
||||
"build": "webpack --config webpack.prod.js",
|
||||
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
|
||||
"start": "TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" webpack serve --open",
|
||||
"build": "TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack",
|
||||
"test": "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"
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {HtmlUtils} from "../WebRtc/HtmlUtils";
|
||||
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
|
||||
import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
|
||||
import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService";
|
||||
|
||||
export const CLASS_CONSOLE_MESSAGE = 'main-console';
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {HtmlUtils} from "./../WebRtc/HtmlUtils";
|
||||
import {AUDIO_TYPE, MESSAGE_TYPE} from "./ConsoleGlobalMessageManager";
|
||||
import {PUSHER_URL, UPLOADER_URL} from "../Enum/EnvironmentVariable";
|
||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
|
||||
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
|
||||
|
||||
export class GlobalMessageManager {
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {TypeMessageInterface} from "./UserMessageManager";
|
||||
import type {TypeMessageInterface} from "./UserMessageManager";
|
||||
import {HtmlUtils} from "../WebRtc/HtmlUtils";
|
||||
|
||||
let modalTimeOut : NodeJS.Timeout;
|
||||
|
@ -1,16 +1,16 @@
|
||||
|
||||
|
||||
import { ButtonClickedEvent } from './ButtonClickedEvent';
|
||||
import { ChatEvent } from './ChatEvent';
|
||||
import { ClosePopupEvent } from './ClosePopupEvent';
|
||||
import { EnterLeaveEvent } from './EnterLeaveEvent';
|
||||
import { GoToPageEvent } from './GoToPageEvent';
|
||||
import { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
|
||||
import { OpenPopupEvent } from './OpenPopupEvent';
|
||||
import { OpenTabEvent } from './OpenTabEvent';
|
||||
import { UserInputChatEvent } from './UserInputChatEvent';
|
||||
import { LayerEvent } from './LayerEvent';
|
||||
import { SetPropertyEvent } from "./setPropertyEvent";
|
||||
import type { ButtonClickedEvent } from './ButtonClickedEvent';
|
||||
import type { ChatEvent } from './ChatEvent';
|
||||
import type { ClosePopupEvent } from './ClosePopupEvent';
|
||||
import type { EnterLeaveEvent } from './EnterLeaveEvent';
|
||||
import type { GoToPageEvent } from './GoToPageEvent';
|
||||
import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
|
||||
import type { OpenPopupEvent } from './OpenPopupEvent';
|
||||
import type { OpenTabEvent } from './OpenTabEvent';
|
||||
import type { UserInputChatEvent } from './UserInputChatEvent';
|
||||
import type { LayerEvent } from './LayerEvent';
|
||||
import type { SetPropertyEvent } from "./setPropertyEvent";
|
||||
|
||||
|
||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||
|
@ -1,17 +1,16 @@
|
||||
import { Subject } from "rxjs";
|
||||
import { ChatEvent, isChatEvent } from "./Events/ChatEvent";
|
||||
import * as crypto from "crypto";
|
||||
import { HtmlUtils } from "../WebRtc/HtmlUtils";
|
||||
import { EnterLeaveEvent } from "./Events/EnterLeaveEvent";
|
||||
import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent";
|
||||
import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent";
|
||||
import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent";
|
||||
import { ButtonClickedEvent } from "./Events/ButtonClickedEvent";
|
||||
import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent";
|
||||
import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent";
|
||||
import { scriptUtils } from "./ScriptUtils";
|
||||
import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent";
|
||||
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent";
|
||||
import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent";
|
||||
import { UserInputChatEvent } from "./Events/UserInputChatEvent";
|
||||
import type { UserInputChatEvent } from "./Events/UserInputChatEvent";
|
||||
import { isLayerEvent, LayerEvent } from "./Events/LayerEvent";
|
||||
import { isSetPropertyEvent, SetPropertyEvent} from "./Events/setPropertyEvent";
|
||||
|
||||
@ -193,7 +192,7 @@ class IframeListener {
|
||||
}
|
||||
|
||||
private getIFrameId(scriptUrl: string): string {
|
||||
return 'script' + crypto.createHash('md5').update(scriptUrl).digest("hex");
|
||||
return 'script' + btoa(scriptUrl);
|
||||
}
|
||||
|
||||
unregisterScript(scriptUrl: string): void {
|
||||
|
11
front/src/Components/App.svelte
Normal file
11
front/src/Components/App.svelte
Normal file
@ -0,0 +1,11 @@
|
||||
<script lang="typescript">
|
||||
import MenuIcon from "./Menu/MenuIcon.svelte";
|
||||
import {menuIconVisible} from "../Stores/MenuStore";
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<!-- {#if $menuIconVisible}
|
||||
<MenuIcon />
|
||||
{/if} -->
|
||||
</div>
|
33
front/src/Components/Menu/MenuIcon.svelte
Normal file
33
front/src/Components/Menu/MenuIcon.svelte
Normal file
@ -0,0 +1,33 @@
|
||||
<script lang="typescript">
|
||||
|
||||
</script>
|
||||
|
||||
<main class="menuIcon">
|
||||
<section>
|
||||
<button>
|
||||
<img src="/static/images/menu.svg" alt="Open menu">
|
||||
</button>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
.menuIcon button {
|
||||
background-color: black;
|
||||
color: white;
|
||||
border-radius: 7px;
|
||||
padding: 2px 8px;
|
||||
img {
|
||||
width: 14px;
|
||||
padding-top: 0;
|
||||
/*cursor: url('/resources/logos/cursor_pointer.png'), pointer;*/
|
||||
}
|
||||
}
|
||||
.menuIcon section {
|
||||
margin: 10px;
|
||||
}
|
||||
@media only screen and (max-height: 700px) {
|
||||
.menuIcon section {
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
import {Subject} from "rxjs";
|
||||
import {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb";
|
||||
import type {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb";
|
||||
|
||||
export enum AdminMessageEventTypes {
|
||||
admin = 'message',
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Axios from "axios";
|
||||
import {PUSHER_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable";
|
||||
import {RoomConnection} from "./RoomConnection";
|
||||
import {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
|
||||
import type {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
|
||||
import {GameConnexionTypes, urlManager} from "../Url/UrlManager";
|
||||
import {localUserStore} from "./LocalUserStore";
|
||||
import {LocalUser} from "./LocalUser";
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {PlayerAnimationDirections} from "../Phaser/Player/Animation";
|
||||
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
||||
import {SignalData} from "simple-peer";
|
||||
import {RoomConnection} from "./RoomConnection";
|
||||
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
||||
import type {SignalData} from "simple-peer";
|
||||
import type {RoomConnection} from "./RoomConnection";
|
||||
import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
||||
|
||||
export enum EventMessage{
|
||||
CONNECT = "connect",
|
||||
|
@ -31,7 +31,7 @@ import {
|
||||
BanUserMessage
|
||||
} from "../Messages/generated/messages_pb"
|
||||
|
||||
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
||||
import type {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
||||
import Direction = PositionMessage.Direction;
|
||||
import {ProtobufClientUtils} from "../Network/ProtobufClientUtils";
|
||||
import {
|
||||
@ -42,7 +42,7 @@ import {
|
||||
ViewportInterface, WebRtcDisconnectMessageInterface,
|
||||
WebRtcSignalReceivedMessageInterface,
|
||||
} from "./ConnexionModels";
|
||||
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
||||
import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
||||
import {adminMessagesService} from "./AdminMessagesService";
|
||||
import {worldFullMessageStream} from "./WorldFullMessageStream";
|
||||
import {worldFullWarningStream} from "./WorldFullWarningStream";
|
||||
|
@ -1,11 +1,11 @@
|
||||
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true";
|
||||
const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.workadventure.localhost/Floor0/floor0.json';
|
||||
// For compatibility reasons with older versions, API_URL is the old host name of PUSHER_URL
|
||||
const PUSHER_URL = process.env.PUSHER_URL || (process.env.API_URL ? '//'+process.env.API_URL : "//pusher.workadventure.localhost");
|
||||
const PUSHER_URL = process.env.PUSHER_URL || '//pusher.workadventure.localhost';
|
||||
const UPLOADER_URL = process.env.UPLOADER_URL || '//uploader.workadventure.localhost';
|
||||
const ADMIN_URL = process.env.ADMIN_URL || "//workadventure.localhost";
|
||||
const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302";
|
||||
const TURN_SERVER: string = process.env.TURN_SERVER || "";
|
||||
const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true";
|
||||
const DISABLE_NOTIFICATIONS: boolean = process.env.DISABLE_NOTIFICATIONS == "true";
|
||||
const TURN_USER: string = process.env.TURN_USER || '';
|
||||
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || '';
|
||||
const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL;
|
||||
@ -20,9 +20,10 @@ export const isMobile = ():boolean => ( ( window.innerWidth <= 800 ) || ( window
|
||||
export {
|
||||
DEBUG_MODE,
|
||||
START_ROOM_URL,
|
||||
SKIP_RENDER_OPTIMIZATIONS,
|
||||
DISABLE_NOTIFICATIONS,
|
||||
PUSHER_URL,
|
||||
UPLOADER_URL,
|
||||
ADMIN_URL,
|
||||
POSITION_DELAY,
|
||||
MAX_EXTRAPOLATION_TIME,
|
||||
STUN_SERVER,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {PositionMessage} from "../Messages/generated/messages_pb";
|
||||
import Direction = PositionMessage.Direction;
|
||||
import {PointInterface} from "../Connexion/ConnexionModels";
|
||||
import type {PointInterface} from "../Connexion/ConnexionModels";
|
||||
|
||||
export class ProtobufClientUtils {
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import { lazyLoadCompanionResource } from "./CompanionTexturesLoadingManager";
|
||||
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
|
||||
|
||||
export interface CompanionStatus {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js';
|
||||
import ScaleManager = Phaser.Scale.ScaleManager;
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
|
||||
//the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free
|
||||
|
@ -17,7 +17,6 @@ export class SoundMeter {
|
||||
}
|
||||
|
||||
private init(context: AudioContext) {
|
||||
if (this.context === undefined) {
|
||||
this.context = context;
|
||||
this.analyser = this.context.createAnalyser();
|
||||
|
||||
@ -25,7 +24,6 @@ export class SoundMeter {
|
||||
const bufferLength = this.analyser.fftSize;
|
||||
this.dataArray = new Uint8Array(bufferLength);
|
||||
}
|
||||
}
|
||||
|
||||
public connectToSource(stream: MediaStream, context: AudioContext): void
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import {Scene} from "phaser";
|
||||
import type {Scene} from "phaser";
|
||||
import GameObject = Phaser.GameObjects.GameObject;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
import {ITiledMapObject} from "../Map/ITiledMap";
|
||||
import Text = Phaser.GameObjects.Text;
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import TextStyle = Phaser.GameObjects.TextStyle;
|
||||
import type {ITiledMapObject} from "../Map/ITiledMap";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
|
||||
export class TextUtils {
|
||||
public static createTextFromITiledMapObject(scene: GameScene, object: ITiledMapObject): void {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import TextureManager = Phaser.Textures.TextureManager;
|
||||
import {CharacterTexture} from "../../Connexion/LocalUser";
|
||||
import type {CharacterTexture} from "../../Connexion/LocalUser";
|
||||
import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures";
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import {PointInterface} from "../../Connexion/ConnexionModels";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
import type {PointInterface} from "../../Connexion/ConnexionModels";
|
||||
import {Character} from "../Entity/Character";
|
||||
import {PlayerAnimationDirections} from "../Player/Animation";
|
||||
import type {PlayerAnimationDirections} from "../Player/Animation";
|
||||
|
||||
/**
|
||||
* Class representing the sprite of a remote player (a player that plays on another computer)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Scene = Phaser.Scene;
|
||||
import {Character} from "./Character";
|
||||
import type {Character} from "./Character";
|
||||
|
||||
//todo: improve this WIP
|
||||
export class SpeechBubble {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {PointInterface} from "../../Connexion/ConnexionModels";
|
||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import type {PointInterface} from "../../Connexion/ConnexionModels";
|
||||
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
|
||||
export interface AddPlayerInterface {
|
||||
userId: number;
|
||||
|
@ -2,6 +2,8 @@ import {ResizableScene} from "../Login/ResizableScene";
|
||||
import GameObject = Phaser.GameObjects.GameObject;
|
||||
import Events = Phaser.Scenes.Events;
|
||||
import AnimationEvents = Phaser.Animations.Events;
|
||||
import StructEvents = Phaser.Structs.Events;
|
||||
import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable";
|
||||
|
||||
/**
|
||||
* A scene that can track its dirty/pristine state.
|
||||
@ -18,17 +20,16 @@ export abstract class DirtyScene extends ResizableScene {
|
||||
* Note: this does not work with animations from sprites inside containers.
|
||||
*/
|
||||
protected trackDirtyAnims(): void {
|
||||
if (this.isAlreadyTracking) {
|
||||
if (this.isAlreadyTracking || SKIP_RENDER_OPTIMIZATIONS) {
|
||||
return;
|
||||
}
|
||||
this.isAlreadyTracking = true;
|
||||
const trackAnimationFunction = this.trackAnimation.bind(this);
|
||||
this.events.on(Events.ADDED_TO_SCENE, (gameObject: GameObject) => {
|
||||
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_ADD, (gameObject: GameObject) => {
|
||||
this.objectListChanged = true;
|
||||
gameObject.on(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
|
||||
});
|
||||
|
||||
this.events.on(Events.REMOVED_FROM_SCENE, (gameObject: GameObject) => {
|
||||
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_REMOVE, (gameObject: GameObject) => {
|
||||
this.objectListChanged = true;
|
||||
gameObject.removeListener(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
|
||||
});
|
||||
|
@ -1,3 +1,8 @@
|
||||
import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable";
|
||||
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
import {ResizableScene} from "../Login/ResizableScene";
|
||||
|
||||
const Events = Phaser.Core.Events;
|
||||
|
||||
/**
|
||||
@ -5,8 +10,27 @@ const Events = Phaser.Core.Events;
|
||||
* It comes with an optimization to skip rendering.
|
||||
*
|
||||
* Beware, the "step" function might vary in future versions of Phaser.
|
||||
*
|
||||
* It also automatically calls "onResize" on any scenes extending ResizableScene.
|
||||
*/
|
||||
export class Game extends Phaser.Game {
|
||||
|
||||
private _isDirty = false;
|
||||
|
||||
|
||||
constructor(GameConfig: Phaser.Types.Core.GameConfig) {
|
||||
super(GameConfig);
|
||||
|
||||
window.addEventListener('resize', (event) => {
|
||||
// Let's trigger the onResize method of any active scene that is a ResizableScene
|
||||
for (const scene of this.scene.getScenes(true)) {
|
||||
if (scene instanceof ResizableScene) {
|
||||
scene.onResize(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public step(time: number, delta: number)
|
||||
{
|
||||
// @ts-ignore
|
||||
@ -35,7 +59,7 @@ export class Game extends Phaser.Game {
|
||||
eventEmitter.emit(Events.POST_STEP, time, delta);
|
||||
|
||||
// This "if" is the changed introduced by the new "Game" class to avoid rendering unnecessarily.
|
||||
if (this.isDirty()) {
|
||||
if (SKIP_RENDER_OPTIMIZATIONS || this.isDirty()) {
|
||||
const renderer = this.renderer;
|
||||
|
||||
// Run the Pre-render (clearing the canvas, setting background colors, etc)
|
||||
@ -62,6 +86,11 @@ export class Game extends Phaser.Game {
|
||||
}
|
||||
|
||||
private isDirty(): boolean {
|
||||
if (this._isDirty) {
|
||||
this._isDirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Loop through the scenes in forward order
|
||||
for (let i = 0; i < this.scene.scenes.length; i++)
|
||||
{
|
||||
@ -85,4 +114,11 @@ export class Game extends Phaser.Game {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the game as needing to be redrawn.
|
||||
*/
|
||||
public markDirty(): void {
|
||||
this._isDirty = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {GameScene} from "./GameScene";
|
||||
import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||
import {Room} from "../../Connexion/Room";
|
||||
import type {Room} from "../../Connexion/Room";
|
||||
import {MenuScene, MenuSceneName} from "../Menu/MenuScene";
|
||||
import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene";
|
||||
import {LoginSceneName} from "../Login/LoginScene";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {ITiledMap, ITiledMapLayer, ITiledMapTileLayer} from "../Map/ITiledMap";
|
||||
import type {ITiledMap, ITiledMapLayer, ITiledMapTileLayer} from "../Map/ITiledMap";
|
||||
import { flattenGroupLayersMap } from "../Map/LayersFlattener";
|
||||
|
||||
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {gameManager, HasMovedEvent} from "./GameManager";
|
||||
import {
|
||||
import type {
|
||||
GroupCreatedUpdatedMessageInterface,
|
||||
MessageUserJoined,
|
||||
MessageUserMovedInterface,
|
||||
@ -16,7 +16,7 @@ import {
|
||||
MAX_PER_GROUP,
|
||||
POSITION_DELAY,
|
||||
} from "../../Enum/EnvironmentVariable";
|
||||
import {
|
||||
import type {
|
||||
ITiledMap,
|
||||
ITiledMapLayer,
|
||||
ITiledMapLayerProperty,
|
||||
@ -25,7 +25,7 @@ import {
|
||||
ITiledMapTileLayer,
|
||||
ITiledTileSet
|
||||
} from "../Map/ITiledMap";
|
||||
import {AddPlayerInterface} from "./AddPlayerInterface";
|
||||
import type {AddPlayerInterface} from "./AddPlayerInterface";
|
||||
import {PlayerAnimationDirections} from "../Player/Animation";
|
||||
import {PlayerMovement} from "./PlayerMovement";
|
||||
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
|
||||
@ -49,13 +49,13 @@ import {
|
||||
import {GameMap} from "./GameMap";
|
||||
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
||||
import {ActionableItem} from "../Items/ActionableItem";
|
||||
import type {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
||||
import type {ActionableItem} from "../Items/ActionableItem";
|
||||
import {UserInputManager} from "../UserInput/UserInputManager";
|
||||
import {UserMovedMessage} from "../../Messages/generated/messages_pb";
|
||||
import type {UserMovedMessage} from "../../Messages/generated/messages_pb";
|
||||
import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils";
|
||||
import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||
import {RoomConnection} from "../../Connexion/RoomConnection";
|
||||
import type {RoomConnection} from "../../Connexion/RoomConnection";
|
||||
import {GlobalMessageManager} from "../../Administration/GlobalMessageManager";
|
||||
import {userMessageManager} from "../../Administration/UserMessageManager";
|
||||
import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager";
|
||||
@ -80,7 +80,7 @@ import CanvasTexture = Phaser.Textures.CanvasTexture;
|
||||
import GameObject = Phaser.GameObjects.GameObject;
|
||||
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
||||
import DOMElement = Phaser.GameObjects.DOMElement;
|
||||
import {Subscription} from "rxjs";
|
||||
import type {Subscription} from "rxjs";
|
||||
import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream";
|
||||
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
|
||||
import RenderTexture = Phaser.GameObjects.RenderTexture;
|
||||
@ -152,7 +152,7 @@ export class GameScene extends DirtyScene implements CenterListener {
|
||||
private GlobalMessageManager!: GlobalMessageManager;
|
||||
public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager;
|
||||
private connectionAnswerPromise: Promise<RoomJoinedMessageInterface>;
|
||||
private connectionAnswerPromiseResolve!: (value?: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
|
||||
private connectionAnswerPromiseResolve!: (value: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
|
||||
// A promise that will resolve when the "create" method is called (signaling loading is ended)
|
||||
private createPromise: Promise<void>;
|
||||
private createPromiseResolve!: (value?: void | PromiseLike<void>) => void;
|
||||
@ -187,6 +187,9 @@ export class GameScene extends DirtyScene implements CenterListener {
|
||||
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
|
||||
private originalMapUrl: string|undefined;
|
||||
private pinchManager: PinchManager|undefined;
|
||||
private physicsEnabled: boolean = true;
|
||||
private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time.
|
||||
private onVisibilityChangeCallback: () => void;
|
||||
|
||||
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
|
||||
super({
|
||||
@ -202,10 +205,11 @@ export class GameScene extends DirtyScene implements CenterListener {
|
||||
|
||||
this.createPromise = new Promise<void>((resolve, reject): void => {
|
||||
this.createPromiseResolve = resolve;
|
||||
})
|
||||
});
|
||||
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
||||
this.connectionAnswerPromiseResolve = resolve;
|
||||
});
|
||||
this.onVisibilityChangeCallback = this.onVisibilityChange.bind(this);
|
||||
}
|
||||
|
||||
//hook preload scene
|
||||
@ -495,6 +499,8 @@ export class GameScene extends DirtyScene implements CenterListener {
|
||||
if (!this.room.isDisconnected()) {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
document.addEventListener('visibilitychange', this.onVisibilityChangeCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -616,6 +622,7 @@ export class GameScene extends DirtyScene implements CenterListener {
|
||||
self.chatModeSprite.setVisible(false);
|
||||
self.openChatIcon.setVisible(false);
|
||||
audioManager.restoreVolume();
|
||||
self.onVisibilityChange();
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -916,6 +923,8 @@ ${escapedMessage}
|
||||
}
|
||||
|
||||
private onMapExit(exitKey: string) {
|
||||
if (this.mapTransitioning) return;
|
||||
this.mapTransitioning = true;
|
||||
const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance);
|
||||
if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey);
|
||||
urlManager.pushStartLayerNameToUrl(hash);
|
||||
@ -933,6 +942,7 @@ ${escapedMessage}
|
||||
this.initPositionFromLayerName(hash || defaultStartLayerName);
|
||||
this.CurrentPlayer.x = this.startX;
|
||||
this.CurrentPlayer.y = this.startY;
|
||||
setTimeout(() => this.mapTransitioning = false, 500);
|
||||
}
|
||||
}
|
||||
|
||||
@ -958,6 +968,8 @@ ${escapedMessage}
|
||||
for(const iframeEvents of this.iframeSubscriptionList){
|
||||
iframeEvents.unsubscribe();
|
||||
}
|
||||
|
||||
document.removeEventListener('visibilitychange', this.onVisibilityChangeCallback);
|
||||
}
|
||||
|
||||
private removeAllRemotePlayers(): void {
|
||||
@ -1107,6 +1119,7 @@ ${escapedMessage}
|
||||
|
||||
createCollisionWithPlayer() {
|
||||
this.physics.disableUpdate();
|
||||
this.physicsEnabled = false;
|
||||
//add collision layer
|
||||
for (const Layer of this.gameMap.flatLayers) {
|
||||
if (Layer.type == "tilelayer") {
|
||||
@ -1251,11 +1264,14 @@ ${escapedMessage}
|
||||
this.CurrentPlayer.moveUser(delta);
|
||||
if (this.CurrentPlayer.isMoving()) {
|
||||
this.dirty = true;
|
||||
if (!this.physicsEnabled) {
|
||||
this.physics.enableUpdate();
|
||||
} else {
|
||||
this.physics.disableUpdate();
|
||||
this.physicsEnabled = true;
|
||||
}
|
||||
} else if (this.physicsEnabled) {
|
||||
this.physics.disableUpdate();
|
||||
this.physicsEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
// Let's handle all events
|
||||
while (this.pendingEvents.length !== 0) {
|
||||
@ -1523,6 +1539,8 @@ ${escapedMessage}
|
||||
mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => {
|
||||
this.stopJitsi();
|
||||
});
|
||||
|
||||
this.onVisibilityChange();
|
||||
}
|
||||
|
||||
public stopJitsi(): void {
|
||||
@ -1531,6 +1549,7 @@ ${escapedMessage}
|
||||
mediaManager.showGameOverlay();
|
||||
|
||||
mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi');
|
||||
this.onVisibilityChange();
|
||||
}
|
||||
|
||||
//todo: put this into an 'orchestrator' scene (EntryScene?)
|
||||
@ -1570,4 +1589,20 @@ ${escapedMessage}
|
||||
waScaleManager.zoomModifier *= zoomFactor;
|
||||
this.updateCameraOffset();
|
||||
}
|
||||
|
||||
private onVisibilityChange(): void {
|
||||
// If the overlay is not displayed, we are in Jitsi. We don't need the webcam.
|
||||
if (!mediaManager.isGameOverlayVisible()) {
|
||||
mediaManager.blurCamera();
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.visibilityState === 'visible') {
|
||||
mediaManager.focusCamera();
|
||||
} else {
|
||||
if (this.simplePeer.getNbConnections() === 0) {
|
||||
mediaManager.blurCamera();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {HasMovedEvent} from "./GameManager";
|
||||
import type {HasMovedEvent} from "./GameManager";
|
||||
import {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable";
|
||||
import {PositionInterface} from "../../Connexion/ConnexionModels";
|
||||
import type {PositionInterface} from "../../Connexion/ConnexionModels";
|
||||
|
||||
export class PlayerMovement {
|
||||
public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) {
|
||||
|
@ -2,8 +2,8 @@
|
||||
* This class is in charge of computing the position of all players.
|
||||
* Player movement is delayed by 200ms so position depends on ticks.
|
||||
*/
|
||||
import {PlayerMovement} from "./PlayerMovement";
|
||||
import {HasMovedEvent} from "./GameManager";
|
||||
import type {PlayerMovement} from "./PlayerMovement";
|
||||
import type {HasMovedEvent} from "./GameManager";
|
||||
|
||||
export class PlayersPositionInterpolator {
|
||||
playerMovements: Map<number, PlayerMovement> = new Map<number, PlayerMovement>();
|
||||
|
@ -4,7 +4,7 @@
|
||||
*/
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import {OutlinePipeline} from "../Shaders/OutlinePipeline";
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
|
||||
type EventCallback = (state: unknown, parameters: unknown) => void;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as Phaser from 'phaser';
|
||||
import {Scene} from "phaser";
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import {ITiledMapObject} from "../../Map/ITiledMap";
|
||||
import {ItemFactoryInterface} from "../ItemFactoryInterface";
|
||||
import {GameScene} from "../../Game/GameScene";
|
||||
import type {ITiledMapObject} from "../../Map/ITiledMap";
|
||||
import type {ItemFactoryInterface} from "../ItemFactoryInterface";
|
||||
import type {GameScene} from "../../Game/GameScene";
|
||||
import {ActionableItem} from "../ActionableItem";
|
||||
import * as tg from "generic-type-guard";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
import type {ITiledMapObject} from "../Map/ITiledMap";
|
||||
import type {ActionableItem} from "./ActionableItem";
|
||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import {ITiledMapObject} from "../Map/ITiledMap";
|
||||
import {ActionableItem} from "./ActionableItem";
|
||||
|
||||
export interface ItemFactoryInterface {
|
||||
preload: (loader: LoaderPlugin) => void;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {ResizableScene} from "./ResizableScene";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import {CharacterTexture} from "../../Connexion/LocalUser";
|
||||
import type {CharacterTexture} from "../../Connexion/LocalUser";
|
||||
|
||||
export abstract class AbstractCharacterScene extends ResizableScene {
|
||||
|
||||
|
@ -6,7 +6,7 @@ import Container = Phaser.GameObjects.Container;
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {addLoader} from "../Components/Loader";
|
||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||
import { MenuScene } from "../Menu/MenuScene";
|
||||
|
@ -252,7 +252,6 @@ export class EnableCameraScene extends ResizableScene {
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
|
||||
mediaManager.updateScene();
|
||||
|
||||
this.centerXDomElement(this.enableCameraSceneElement, 300);
|
||||
}
|
||||
|
@ -1,18 +1,4 @@
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||
import {CustomizeSceneName} from "./CustomizeScene";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import {MenuScene} from "../Menu/MenuScene";
|
||||
import { SelectCharacterScene, SelectCharacterSceneName } from "./SelectCharacterScene";
|
||||
import { SelectCharacterScene } from "./SelectCharacterScene";
|
||||
|
||||
export class SelectCharacterMobileScene extends SelectCharacterScene {
|
||||
|
||||
|
@ -1,18 +1,16 @@
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||
import {CustomizeSceneName} from "./CustomizeScene";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import {addLoader} from "../Components/Loader";
|
||||
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import {MenuScene} from "../Menu/MenuScene";
|
||||
import { SelectCharacterMobileScene } from "./SelectCharacterMobileScene";
|
||||
|
||||
//todo: put this constants in a dedicated file
|
||||
export const SelectCharacterSceneName = "SelectCharacterScene";
|
||||
|
@ -5,7 +5,7 @@ import { gameManager} from "../Game/GameManager";
|
||||
import { ResizableScene } from "./ResizableScene";
|
||||
import { EnableCameraSceneName } from "./EnableCameraScene";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures";
|
||||
import type { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures";
|
||||
import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {ITiledMap, ITiledMapLayer} from "./ITiledMap";
|
||||
import type {ITiledMap, ITiledMapLayer} from "./ITiledMap";
|
||||
|
||||
/**
|
||||
* Flatten the grouped layers
|
||||
*/
|
||||
export function flattenGroupLayersMap(map: ITiledMap) {
|
||||
let flatLayers: ITiledMapLayer[] = [];
|
||||
const flatLayers: ITiledMapLayer[] = [];
|
||||
flattenGroupLayers(map.layers, '', flatLayers);
|
||||
return flatLayers;
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ export class HelpCameraSettingsScene extends DirtyScene {
|
||||
|
||||
public onResize(ev: UIEvent): void {
|
||||
super.onResize(ev);
|
||||
if (this.helpCameraSettingsOpened) {
|
||||
const middleX = this.getMiddleX();
|
||||
const middleY = this.getMiddleY();
|
||||
this.tweens.add({
|
||||
@ -118,6 +119,8 @@ export class HelpCameraSettingsScene extends DirtyScene {
|
||||
duration: 1000,
|
||||
ease: 'Power3'
|
||||
});
|
||||
this.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private getMiddleX() : number{
|
||||
|
@ -9,6 +9,7 @@ import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||
import {GameConnexionTypes} from "../../Url/UrlManager";
|
||||
import {WarningContainer, warningContainerHtml, warningContainerKey} from "../Components/WarningContainer";
|
||||
import {worldFullWarningStream} from "../../Connexion/WorldFullWarningStream";
|
||||
import {menuIconVisible} from "../../Stores/MenuStore";
|
||||
|
||||
export const MenuSceneName = 'MenuScene';
|
||||
const gameMenuKey = 'gameMenu';
|
||||
@ -53,6 +54,7 @@ export class MenuScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
create() {
|
||||
menuIconVisible.set(true);
|
||||
this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey);
|
||||
this.menuElement.setOrigin(0);
|
||||
MenuScene.revealMenusAfterInit(this.menuElement, 'gameMenu');
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {PlayerAnimationDirections} from "./Animation";
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
|
||||
import {Character} from "../Entity/Character";
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import ScaleManager = Phaser.Scale.ScaleManager;
|
||||
|
||||
interface Size {
|
||||
width: number;
|
||||
@ -13,8 +12,7 @@ export class HdpiManager {
|
||||
* @param minRecommendedGamePixelsNumber The minimum number of pixels we want to display "by default" to the user
|
||||
* @param absoluteMinPixelNumber The very minimum of game pixels to display. Below, we forbid zooming more
|
||||
*/
|
||||
public constructor(private minRecommendedGamePixelsNumber: number, private absoluteMinPixelNumber: number) {
|
||||
}
|
||||
public constructor(private minRecommendedGamePixelsNumber: number, private absoluteMinPixelNumber: number) {}
|
||||
|
||||
/**
|
||||
* Returns the optimal size in "game pixels" based on the screen size in "real pixels".
|
||||
@ -36,16 +34,12 @@ export class HdpiManager {
|
||||
};
|
||||
}
|
||||
|
||||
let i = 1;
|
||||
|
||||
while (realPixelNumber > this.minRecommendedGamePixelsNumber * i * i) {
|
||||
i++;
|
||||
}
|
||||
const optimalZoomLevel = this.getOptimalZoomLevel(realPixelNumber);
|
||||
|
||||
// Has the canvas more pixels than the screen? This is forbidden
|
||||
if ((i - 1) * this._zoomModifier < 1) {
|
||||
if (optimalZoomLevel * this._zoomModifier < 1) {
|
||||
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
|
||||
this._zoomModifier = 1 / (i - 1);
|
||||
this._zoomModifier = 1 / optimalZoomLevel;
|
||||
|
||||
return {
|
||||
game: {
|
||||
@ -59,8 +53,8 @@ export class HdpiManager {
|
||||
}
|
||||
}
|
||||
|
||||
const gameWidth = Math.ceil(realPixelScreenSize.width / (i - 1) / this._zoomModifier);
|
||||
const gameHeight = Math.ceil(realPixelScreenSize.height / (i - 1) / this._zoomModifier);
|
||||
const gameWidth = Math.ceil(realPixelScreenSize.width / optimalZoomLevel / this._zoomModifier);
|
||||
const gameHeight = Math.ceil(realPixelScreenSize.height / optimalZoomLevel / this._zoomModifier);
|
||||
|
||||
// Let's ensure we display a minimum of pixels, even if crazily zoomed in.
|
||||
if (gameWidth * gameHeight < this.absoluteMinPixelNumber) {
|
||||
@ -68,7 +62,7 @@ export class HdpiManager {
|
||||
const minGameWidth = Math.sqrt(this.absoluteMinPixelNumber * realPixelScreenSize.width / realPixelScreenSize.height);
|
||||
|
||||
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
|
||||
this._zoomModifier = realPixelScreenSize.width / minGameWidth / (i - 1);
|
||||
this._zoomModifier = realPixelScreenSize.width / minGameWidth / optimalZoomLevel;
|
||||
|
||||
return {
|
||||
game: {
|
||||
@ -89,12 +83,24 @@ export class HdpiManager {
|
||||
height: gameHeight,
|
||||
},
|
||||
real: {
|
||||
width: Math.ceil(realPixelScreenSize.width / (i - 1)) * (i - 1),
|
||||
height: Math.ceil(realPixelScreenSize.height / (i - 1)) * (i - 1),
|
||||
width: Math.ceil(realPixelScreenSize.width / optimalZoomLevel) * optimalZoomLevel,
|
||||
height: Math.ceil(realPixelScreenSize.height / optimalZoomLevel) * optimalZoomLevel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We only accept integer but we make an exception for 1.5
|
||||
*/
|
||||
private getOptimalZoomLevel(realPixelNumber: number): number {
|
||||
const result = Math.sqrt(realPixelNumber / this.minRecommendedGamePixelsNumber);
|
||||
if (1.5 <= result && result < 2) {
|
||||
return 1.5
|
||||
} else {
|
||||
return Math.floor(result);
|
||||
}
|
||||
}
|
||||
|
||||
public get zoomModifier(): number {
|
||||
return this._zoomModifier;
|
||||
}
|
||||
|
@ -1,18 +1,21 @@
|
||||
import {HdpiManager} from "./HdpiManager";
|
||||
import ScaleManager = Phaser.Scale.ScaleManager;
|
||||
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
||||
import type {Game} from "../Game/Game";
|
||||
|
||||
|
||||
class WaScaleManager {
|
||||
private hdpiManager: HdpiManager;
|
||||
private scaleManager!: ScaleManager;
|
||||
private game!: Game;
|
||||
|
||||
public constructor(private minGamePixelsNumber: number, private absoluteMinPixelNumber: number) {
|
||||
this.hdpiManager = new HdpiManager(minGamePixelsNumber, absoluteMinPixelNumber);
|
||||
}
|
||||
|
||||
public setScaleManager(scaleManager: ScaleManager) {
|
||||
this.scaleManager = scaleManager;
|
||||
public setGame(game: Game): void {
|
||||
this.scaleManager = game.scale;
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
public applyNewSize() {
|
||||
@ -32,6 +35,8 @@ class WaScaleManager {
|
||||
const style = this.scaleManager.canvas.style;
|
||||
style.width = Math.ceil(realSize.width / devicePixelRatio) + 'px';
|
||||
style.height = Math.ceil(realSize.height / devicePixelRatio) + 'px';
|
||||
|
||||
this.game.markDirty();
|
||||
}
|
||||
|
||||
public get zoomModifier(): number {
|
||||
@ -42,6 +47,7 @@ class WaScaleManager {
|
||||
this.hdpiManager.zoomModifier = zoomModifier;
|
||||
this.applyNewSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const waScaleManager = new WaScaleManager(640*480, 196*196);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Direction } from "../../types";
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import type { Direction } from "../../types";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {MobileJoystick} from "../Components/MobileJoystick";
|
||||
|
||||
@ -173,7 +173,7 @@ export class UserInputManager {
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.joystick.destroy();
|
||||
this.joystick?.destroy();
|
||||
}
|
||||
|
||||
private initMouseWheel() {
|
||||
|
3
front/src/Stores/MenuStore.ts
Normal file
3
front/src/Stores/MenuStore.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { derived, writable, Writable } from "svelte/store";
|
||||
|
||||
export const menuIconVisible = writable(false);
|
@ -1,4 +1,4 @@
|
||||
import {Room} from "../Connexion/Room";
|
||||
import type {Room} from "../Connexion/Room";
|
||||
|
||||
export enum GameConnexionTypes {
|
||||
anonymous=1,
|
||||
|
@ -137,14 +137,14 @@ class CoWebsiteManager {
|
||||
if (allowPolicy) {
|
||||
iframe.allow = allowPolicy;
|
||||
}
|
||||
const onloadPromise = new Promise((resolve) => {
|
||||
const onloadPromise = new Promise<void>((resolve) => {
|
||||
iframe.onload = () => resolve();
|
||||
});
|
||||
if (allowApi) {
|
||||
iframeListener.registerIframe(iframe);
|
||||
}
|
||||
this.cowebsiteMainDom.appendChild(iframe);
|
||||
const onTimeoutPromise = new Promise((resolve) => {
|
||||
const onTimeoutPromise = new Promise<void>((resolve) => {
|
||||
setTimeout(() => resolve(), 2000);
|
||||
});
|
||||
this.currentOperationPromise = this.currentOperationPromise.then(() =>Promise.race([onloadPromise, onTimeoutPromise])).then(() => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {HtmlUtils} from "./HtmlUtils";
|
||||
import {mediaManager, ReportCallback, ShowReportCallBack} from "./MediaManager";
|
||||
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||
import type {ShowReportCallBack} from "./MediaManager";
|
||||
import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||
import {connectionManager} from "../Connexion/ConnectionManager";
|
||||
import {GameConnexionTypes} from "../Url/UrlManager";
|
||||
import {iframeListener} from "../Api/IframeListener";
|
||||
|
@ -10,9 +10,10 @@ interface jitsiConfigInterface {
|
||||
}
|
||||
|
||||
const getDefaultConfig = () : jitsiConfigInterface => {
|
||||
const constraints = mediaManager.getConstraintRequestedByUser();
|
||||
return {
|
||||
startWithAudioMuted: !mediaManager.constraintsMedia.audio,
|
||||
startWithVideoMuted: mediaManager.constraintsMedia.video === false,
|
||||
startWithAudioMuted: !constraints.audio,
|
||||
startWithVideoMuted: constraints.video === false,
|
||||
prejoinPageEnabled: false
|
||||
}
|
||||
}
|
||||
@ -71,7 +72,7 @@ class JitsiFactory {
|
||||
private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
private audioCallback = this.onAudioChange.bind(this);
|
||||
private videoCallback = this.onVideoChange.bind(this);
|
||||
private previousConfigMeet? : jitsiConfigInterface;
|
||||
private previousConfigMeet! : jitsiConfigInterface;
|
||||
private jitsiScriptLoaded: boolean = false;
|
||||
|
||||
/**
|
||||
@ -136,32 +137,24 @@ class JitsiFactory {
|
||||
|
||||
//restore previous config
|
||||
if(this.previousConfigMeet?.startWithAudioMuted){
|
||||
mediaManager.disableMicrophone();
|
||||
await mediaManager.disableMicrophone();
|
||||
}else{
|
||||
mediaManager.enableMicrophone();
|
||||
await mediaManager.enableMicrophone();
|
||||
}
|
||||
|
||||
if(this.previousConfigMeet?.startWithVideoMuted){
|
||||
mediaManager.disableCamera();
|
||||
await mediaManager.disableCamera();
|
||||
}else{
|
||||
mediaManager.enableCamera();
|
||||
await mediaManager.enableCamera();
|
||||
}
|
||||
}
|
||||
|
||||
private onAudioChange({muted}: {muted: boolean}): void {
|
||||
if (muted && mediaManager.constraintsMedia.audio === true) {
|
||||
mediaManager.disableMicrophone();
|
||||
} else if(!muted && mediaManager.constraintsMedia.audio === false) {
|
||||
mediaManager.enableMicrophone();
|
||||
}
|
||||
this.previousConfigMeet.startWithAudioMuted = muted;
|
||||
}
|
||||
|
||||
private onVideoChange({muted}: {muted: boolean}): void {
|
||||
if (muted && mediaManager.constraintsMedia.video !== false) {
|
||||
mediaManager.disableCamera();
|
||||
} else if(!muted && mediaManager.constraintsMedia.video === false) {
|
||||
mediaManager.enableCamera();
|
||||
}
|
||||
this.previousConfigMeet.startWithVideoMuted = muted;
|
||||
}
|
||||
|
||||
private async loadJitsiScript(domain: string): Promise<void> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { UserInputManager } from "../Phaser/UserInput/UserInputManager";
|
||||
import type { UserInputManager } from "../Phaser/UserInput/UserInputManager";
|
||||
import {HtmlUtils} from "./HtmlUtils";
|
||||
|
||||
export enum LayoutMode {
|
||||
|
@ -1,10 +1,11 @@
|
||||
import {DivImportance, layoutManager} from "./LayoutManager";
|
||||
import {HtmlUtils} from "./HtmlUtils";
|
||||
import {discussionManager, SendMessageCallback} from "./DiscussionManager";
|
||||
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||
import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||
import {localUserStore} from "../Connexion/LocalUserStore";
|
||||
import {UserSimplePeerInterface} from "./SimplePeer";
|
||||
import type {UserSimplePeerInterface} from "./SimplePeer";
|
||||
import {SoundMeter} from "../Phaser/Components/SoundMeter";
|
||||
import {DISABLE_NOTIFICATIONS} from "../Enum/EnvironmentVariable";
|
||||
|
||||
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
@ -20,7 +21,7 @@ const audioConstraint: boolean|MediaTrackConstraints = {
|
||||
//TODO: make these values configurable in the game settings menu and store them in localstorage
|
||||
autoGainControl: false,
|
||||
echoCancellation: true,
|
||||
noiseSuppression: false
|
||||
noiseSuppression: true
|
||||
};
|
||||
|
||||
export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
|
||||
@ -43,7 +44,8 @@ export class MediaManager {
|
||||
microphoneClose: HTMLImageElement;
|
||||
microphone: HTMLImageElement;
|
||||
webrtcInAudio: HTMLAudioElement;
|
||||
mySoundMeterElement: HTMLDivElement;
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
//mySoundMeterElement: HTMLDivElement;
|
||||
private webrtcOutAudio: HTMLAudioElement;
|
||||
constraintsMedia : MediaStreamConstraints = {
|
||||
audio: audioConstraint,
|
||||
@ -62,18 +64,16 @@ export class MediaManager {
|
||||
private previousConstraint : MediaStreamConstraints;
|
||||
private focused : boolean = true;
|
||||
|
||||
private lastUpdateScene : Date = new Date();
|
||||
private setTimeOutlastUpdateScene? : NodeJS.Timeout;
|
||||
|
||||
private hasCamera = true;
|
||||
|
||||
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
|
||||
|
||||
private userInputManager?: UserInputManager;
|
||||
|
||||
private mySoundMeter?: SoundMeter|null;
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
/*private mySoundMeter?: SoundMeter|null;
|
||||
private soundMeters: Map<string, SoundMeter> = new Map<string, SoundMeter>();
|
||||
private soundMeterElements: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>();
|
||||
private soundMeterElements: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>();*/
|
||||
|
||||
constructor() {
|
||||
|
||||
@ -132,17 +132,19 @@ export class MediaManager {
|
||||
this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
|
||||
this.pingCameraStatus();
|
||||
|
||||
this.checkActiveUser(); //todo: desactivated in case of bug
|
||||
|
||||
this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter'));
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
/*this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter'));
|
||||
this.mySoundMeterElement.childNodes.forEach((value: ChildNode, index) => {
|
||||
this.mySoundMeterElement.children.item(index)?.classList.remove('active');
|
||||
});
|
||||
});*/
|
||||
|
||||
//Check of ask notification navigator permission
|
||||
this.getNotification();
|
||||
}
|
||||
|
||||
public updateScene(){
|
||||
this.lastUpdateScene = new Date();
|
||||
this.updateSoudMeter();
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
//this.updateSoudMeter();
|
||||
}
|
||||
|
||||
public blurCamera() {
|
||||
@ -154,6 +156,13 @@ export class MediaManager {
|
||||
this.disableCamera();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the constraint that the user wants (independently of the visibility / jitsi state...)
|
||||
*/
|
||||
public getConstraintRequestedByUser(): MediaStreamConstraints {
|
||||
return this.previousConstraint ?? this.constraintsMedia;
|
||||
}
|
||||
|
||||
public focusCamera() {
|
||||
if(this.focused){
|
||||
return;
|
||||
@ -196,7 +205,7 @@ export class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
public showGameOverlay(){
|
||||
public showGameOverlay(): void {
|
||||
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
||||
gameOverlay.classList.add('active');
|
||||
|
||||
@ -207,7 +216,7 @@ export class MediaManager {
|
||||
buttonCloseFrame.removeEventListener('click', functionTrigger);
|
||||
}
|
||||
|
||||
public hideGameOverlay(){
|
||||
public hideGameOverlay(): void {
|
||||
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
||||
gameOverlay.classList.remove('active');
|
||||
|
||||
@ -218,6 +227,11 @@ export class MediaManager {
|
||||
buttonCloseFrame.addEventListener('click', functionTrigger);
|
||||
}
|
||||
|
||||
public isGameOverlayVisible(): boolean {
|
||||
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
||||
return gameOverlay.classList.contains('active');
|
||||
}
|
||||
|
||||
public updateCameraQuality(value: number) {
|
||||
this.enableCameraStyle();
|
||||
const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint));
|
||||
@ -229,29 +243,32 @@ export class MediaManager {
|
||||
});
|
||||
}
|
||||
|
||||
public enableCamera() {
|
||||
public async enableCamera() {
|
||||
this.constraintsMedia.video = videoConstraint;
|
||||
|
||||
this.getCamera().then((stream: MediaStream) => {
|
||||
try {
|
||||
const stream = await this.getCamera()
|
||||
//TODO show error message tooltip upper of camera button
|
||||
//TODO message : please check camera permission of your navigator
|
||||
if(stream.getVideoTracks().length === 0) {
|
||||
throw Error('Video track is empty, please check camera permission of your navigator')
|
||||
throw new Error('Video track is empty, please check camera permission of your navigator')
|
||||
}
|
||||
this.enableCameraStyle();
|
||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||
}).catch((err) => {
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
this.disableCameraStyle();
|
||||
this.stopCamera();
|
||||
|
||||
layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => {
|
||||
this.showHelpCameraSettingsCallBack();
|
||||
}, this.userInputManager);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async disableCamera() {
|
||||
this.disableCameraStyle();
|
||||
this.stopCamera();
|
||||
|
||||
if (this.constraintsMedia.audio !== false) {
|
||||
const stream = await this.getCamera();
|
||||
@ -261,25 +278,27 @@ export class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
public enableMicrophone() {
|
||||
public async enableMicrophone() {
|
||||
this.constraintsMedia.audio = audioConstraint;
|
||||
|
||||
this.getCamera().then((stream) => {
|
||||
try {
|
||||
const stream = await this.getCamera();
|
||||
|
||||
//TODO show error message tooltip upper of camera button
|
||||
//TODO message : please check microphone permission of your navigator
|
||||
if(stream.getAudioTracks().length === 0) {
|
||||
if (stream.getAudioTracks().length === 0) {
|
||||
throw Error('Audio track is empty, please check microphone permission of your navigator')
|
||||
}
|
||||
this.enableMicrophoneStyle();
|
||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||
}).catch((err) => {
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
this.disableMicrophoneStyle();
|
||||
|
||||
layoutManager.addInformation('warning', 'Microphone access denied. Click here and check navigators permissions.', () => {
|
||||
this.showHelpCameraSettingsCallBack();
|
||||
}, this.userInputManager);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async disableMicrophone() {
|
||||
@ -324,7 +343,6 @@ export class MediaManager {
|
||||
this.cinemaBtn.classList.add("disabled");
|
||||
this.constraintsMedia.video = false;
|
||||
this.myCamVideo.srcObject = null;
|
||||
this.stopCamera();
|
||||
}
|
||||
|
||||
private enableMicrophoneStyle(){
|
||||
@ -435,6 +453,8 @@ export class MediaManager {
|
||||
return this.getLocalStream().catch((err) => {
|
||||
console.info('Error get camera, trying with video option at null =>', err);
|
||||
this.disableCameraStyle();
|
||||
this.stopCamera();
|
||||
|
||||
return this.getLocalStream().then((stream : MediaStream) => {
|
||||
this.hasCamera = false;
|
||||
return stream;
|
||||
@ -457,12 +477,12 @@ export class MediaManager {
|
||||
this.localStream = stream;
|
||||
this.myCamVideo.srcObject = this.localStream;
|
||||
|
||||
//init sound meter
|
||||
this.mySoundMeter = null;
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
/*this.mySoundMeter = null;
|
||||
if(this.constraintsMedia.audio){
|
||||
this.mySoundMeter = new SoundMeter();
|
||||
this.mySoundMeter.connectToSource(stream, new AudioContext());
|
||||
}
|
||||
}*/
|
||||
return stream;
|
||||
}).catch((err: Error) => {
|
||||
throw err;
|
||||
@ -489,7 +509,7 @@ export class MediaManager {
|
||||
track.stop();
|
||||
}
|
||||
}
|
||||
this.mySoundMeter?.stop();
|
||||
//this.mySoundMeter?.stop();
|
||||
}
|
||||
|
||||
setCamera(id: string): Promise<MediaStream> {
|
||||
@ -632,11 +652,12 @@ export class MediaManager {
|
||||
}
|
||||
remoteVideo.srcObject = stream;
|
||||
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
//sound metter
|
||||
const soundMeter = new SoundMeter();
|
||||
/*const soundMeter = new SoundMeter();
|
||||
soundMeter.connectToSource(stream, new AudioContext());
|
||||
this.soundMeters.set(userId, soundMeter);
|
||||
this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail<HTMLImageElement>('soundMeter-'+userId));
|
||||
this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail<HTMLImageElement>('soundMeter-'+userId));*/
|
||||
}
|
||||
addStreamRemoteScreenSharing(userId: string, stream : MediaStream){
|
||||
// In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet
|
||||
@ -652,9 +673,10 @@ export class MediaManager {
|
||||
layoutManager.remove(userId);
|
||||
this.remoteVideo.delete(userId);
|
||||
|
||||
this.soundMeters.get(userId)?.stop();
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
/*this.soundMeters.get(userId)?.stop();
|
||||
this.soundMeters.delete(userId);
|
||||
this.soundMeterElements.delete(userId);
|
||||
this.soundMeterElements.delete(userId);*/
|
||||
|
||||
//permit to remove user in discussion part
|
||||
this.removeParticipant(userId);
|
||||
@ -776,22 +798,6 @@ export class MediaManager {
|
||||
this.userInputManager = userInputManager;
|
||||
discussionManager.setUserInputManager(userInputManager);
|
||||
}
|
||||
//check if user is active
|
||||
private checkActiveUser(){
|
||||
if(this.setTimeOutlastUpdateScene){
|
||||
clearTimeout(this.setTimeOutlastUpdateScene);
|
||||
}
|
||||
this.setTimeOutlastUpdateScene = setTimeout(() => {
|
||||
const now = new Date();
|
||||
//if last update is more of 10 sec
|
||||
if( (now.getTime() - this.lastUpdateScene.getTime()) > 10000) {
|
||||
this.blurCamera();
|
||||
}else{
|
||||
this.focusCamera();
|
||||
}
|
||||
this.checkActiveUser();
|
||||
}, this.focused ? 10000 : 1000);
|
||||
}
|
||||
|
||||
public setShowReportModalCallBacks(callback: ShowReportCallBack){
|
||||
this.showReportModalCallBacks.add(callback);
|
||||
@ -807,7 +813,8 @@ export class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
updateSoudMeter(){
|
||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||
/*updateSoudMeter(){
|
||||
try{
|
||||
const volume = parseInt(((this.mySoundMeter ? this.mySoundMeter.getVolume() : 0) / 10).toFixed(0));
|
||||
this.setVolumeSoundMeter(volume, this.mySoundMeterElement);
|
||||
@ -824,7 +831,7 @@ export class MediaManager {
|
||||
}catch(err){
|
||||
//console.error(err);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
private setVolumeSoundMeter(volume: number, element: HTMLDivElement){
|
||||
if(volume <= 0 && !element.classList.contains('active')){
|
||||
@ -847,6 +854,32 @@ export class MediaManager {
|
||||
elementChildre.classList.add('active');
|
||||
});
|
||||
}
|
||||
|
||||
public getNotification(){
|
||||
//Get notification
|
||||
if (!DISABLE_NOTIFICATIONS && window.Notification && Notification.permission !== "granted") {
|
||||
Notification.requestPermission().catch((err) => {
|
||||
console.error(`Notification permission error`, err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public createNotification(userName: string){
|
||||
if(this.focused){
|
||||
return;
|
||||
}
|
||||
if (window.Notification && Notification.permission === "granted") {
|
||||
const title = 'WorkAdventure';
|
||||
const options = {
|
||||
body: `Hi! ${userName} wants to discuss with you, don't be afraid!`,
|
||||
icon: '/resources/logos/logo-WA-min.png',
|
||||
image: '/resources/logos/logo-WA-min.png',
|
||||
badge: '/resources/logos/logo-WA-min.png',
|
||||
};
|
||||
new Notification(title, options);
|
||||
//new Notification(`Hi! ${userName} wants to discuss with you, don't be afraid!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const mediaManager = new MediaManager();
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as SimplePeerNamespace from "simple-peer";
|
||||
import type * as SimplePeerNamespace from "simple-peer";
|
||||
import {mediaManager} from "./MediaManager";
|
||||
import {STUN_SERVER, TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable";
|
||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer";
|
||||
import {UserSimplePeerInterface} from "./SimplePeer";
|
||||
import type {UserSimplePeerInterface} from "./SimplePeer";
|
||||
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {
|
||||
import type {
|
||||
WebRtcDisconnectMessageInterface,
|
||||
WebRtcSignalReceivedMessageInterface,
|
||||
} from "../Connexion/ConnexionModels";
|
||||
@ -10,7 +10,7 @@ import {
|
||||
} from "./MediaManager";
|
||||
import {ScreenSharingPeer} from "./ScreenSharingPeer";
|
||||
import {MESSAGE_TYPE_BLOCKED, MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer";
|
||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import {connectionManager} from "../Connexion/ConnectionManager";
|
||||
import {GameConnexionTypes} from "../Url/UrlManager";
|
||||
import {blackListManager} from "./BlackListManager";
|
||||
@ -158,6 +158,11 @@ export class SimplePeer {
|
||||
this.sendLocalScreenSharingStreamToUser(user.userId);
|
||||
}
|
||||
});
|
||||
|
||||
//Create a notification for first user in circle discussion
|
||||
if(this.PeerConnectionArray.size === 0){
|
||||
mediaManager.createNotification(user.name??'');
|
||||
}
|
||||
this.PeerConnectionArray.set(user.userId, peer);
|
||||
|
||||
for (const peerConnectionListener of this.peerConnectionListeners) {
|
||||
|
@ -1,10 +1,10 @@
|
||||
import * as SimplePeerNamespace from "simple-peer";
|
||||
import type * as SimplePeerNamespace from "simple-peer";
|
||||
import {mediaManager} from "./MediaManager";
|
||||
import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable";
|
||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import {blackListManager} from "./BlackListManager";
|
||||
import {Subscription} from "rxjs";
|
||||
import {UserSimplePeerInterface} from "./SimplePeer";
|
||||
import type {Subscription} from "rxjs";
|
||||
import type {UserSimplePeerInterface} from "./SimplePeer";
|
||||
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { ChatEvent } from "./Api/Events/ChatEvent";
|
||||
import type { ChatEvent } from "./Api/Events/ChatEvent";
|
||||
import { isIframeResponseEventWrapper } from "./Api/Events/IframeEvent";
|
||||
import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent";
|
||||
import { Subject } from "rxjs";
|
||||
import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent";
|
||||
import { OpenPopupEvent } from "./Api/Events/OpenPopupEvent";
|
||||
import type { OpenPopupEvent } from "./Api/Events/OpenPopupEvent";
|
||||
import { isButtonClickedEvent } from "./Api/Events/ButtonClickedEvent";
|
||||
import { ClosePopupEvent } from "./Api/Events/ClosePopupEvent";
|
||||
import { OpenTabEvent } from "./Api/Events/OpenTabEvent";
|
||||
import { GoToPageEvent } from "./Api/Events/GoToPageEvent";
|
||||
import { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent";
|
||||
import { LayerEvent } from "./Api/Events/LayerEvent";
|
||||
import { SetPropertyEvent } from "./Api/Events/setPropertyEvent";
|
||||
import type { ClosePopupEvent } from "./Api/Events/ClosePopupEvent";
|
||||
import type { OpenTabEvent } from "./Api/Events/OpenTabEvent";
|
||||
import type { GoToPageEvent } from "./Api/Events/GoToPageEvent";
|
||||
import type { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent";
|
||||
import type { LayerEvent } from "./Api/Events/LayerEvent";
|
||||
import type { SetPropertyEvent } from "./Api/Events/setPropertyEvent";
|
||||
|
||||
interface WorkAdventureApi {
|
||||
sendChatMessage(message: string, author: string): void;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'phaser';
|
||||
import GameConfig = Phaser.Types.Core.GameConfig;
|
||||
import "../dist/resources/style/index.scss";
|
||||
import "../style/index.scss";
|
||||
|
||||
import {DEBUG_MODE, isMobile} from "./Enum/EnvironmentVariable";
|
||||
import {LoginScene} from "./Phaser/Login/LoginScene";
|
||||
@ -21,6 +21,8 @@ import { SelectCharacterMobileScene } from './Phaser/Login/SelectCharacterMobile
|
||||
import {HdpiManager} from "./Phaser/Services/HdpiManager";
|
||||
import {waScaleManager} from "./Phaser/Services/WaScaleManager";
|
||||
import {Game} from "./Phaser/Game/Game";
|
||||
import App from './Components/App.svelte';
|
||||
import {HtmlUtils} from "./WebRtc/HtmlUtils";
|
||||
|
||||
const {width, height} = coWebsiteManager.getGameSize();
|
||||
|
||||
@ -127,19 +129,12 @@ const config: GameConfig = {
|
||||
//const game = new Phaser.Game(config);
|
||||
const game = new Game(config);
|
||||
|
||||
waScaleManager.setScaleManager(game.scale);
|
||||
waScaleManager.setGame(game);
|
||||
|
||||
window.addEventListener('resize', function (event) {
|
||||
coWebsiteManager.resetStyle();
|
||||
|
||||
waScaleManager.applyNewSize();
|
||||
|
||||
// Let's trigger the onResize method of any active scene that is a ResizableScene
|
||||
for (const scene of game.scene.getScenes(true)) {
|
||||
if (scene instanceof ResizableScene) {
|
||||
scene.onResize(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
coWebsiteManager.onResize.subscribe(() => {
|
||||
@ -147,3 +142,10 @@ coWebsiteManager.onResize.subscribe(() => {
|
||||
});
|
||||
|
||||
iframeListener.init();
|
||||
|
||||
const app = new App({
|
||||
target: HtmlUtils.getElementByIdOrFail('svelte-overlay'),
|
||||
props: { },
|
||||
})
|
||||
|
||||
export default app
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Phaser from "phaser";
|
||||
import type Phaser from "phaser";
|
||||
|
||||
export type CursorKey = {
|
||||
isDown: boolean
|
||||
|
@ -32,7 +32,7 @@
|
||||
position: absolute;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
|
||||
img {
|
||||
height: 25px;
|
Before Width: | Height: | Size: 979 B After Width: | Height: | Size: 979 B |
Before Width: | Height: | Size: 937 B After Width: | Height: | Size: 937 B |
@ -1,9 +1,9 @@
|
||||
*{
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
cursor: url('/resources/logos/cursor_normal.png'), auto;
|
||||
cursor: url('./images/cursor_normal.png'), auto;
|
||||
}
|
||||
* a, button, select{
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
body{
|
||||
overflow: hidden;
|
||||
@ -39,7 +39,7 @@ body .message-info.warning{
|
||||
position: relative;
|
||||
transition: all 0.2s ease;
|
||||
background-color: #00000099;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
.video-container i{
|
||||
position: absolute;
|
||||
@ -75,7 +75,7 @@ body .message-info.warning{
|
||||
|
||||
.video-container button.report{
|
||||
display: block;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
background: none;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border: none;
|
||||
@ -108,7 +108,7 @@ body .message-info.warning{
|
||||
left: 5px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
@ -118,7 +118,7 @@ body .message-info.warning{
|
||||
left: 36px;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
.video-container img.active {
|
||||
display: block !important;
|
||||
@ -126,7 +126,7 @@ body .message-info.warning{
|
||||
|
||||
.video-container video{
|
||||
height: 100%;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
|
||||
.video-container video:focus{
|
||||
@ -206,7 +206,7 @@ video#myCamVideo{
|
||||
}
|
||||
/*btn animation*/
|
||||
.btn-cam-action div{
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
/*position: absolute;*/
|
||||
border: solid 0px black;
|
||||
width: 44px;
|
||||
@ -260,7 +260,7 @@ video#myCamVideo{
|
||||
top: calc(48px - 37px);
|
||||
left: calc(48px - 41px);
|
||||
position: relative;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
|
||||
/* Spinner */
|
||||
@ -572,7 +572,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
margin: 2%;
|
||||
flex-basis: 96%;
|
||||
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
pointer-events: auto;
|
||||
/*flex-shrink: 2;*/
|
||||
}
|
||||
@ -590,7 +590,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
.sidebar > div {
|
||||
margin: 2%;
|
||||
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
border-radius: 15px 15px 15px 15px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
@ -600,7 +600,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
}
|
||||
|
||||
.sidebar > div video {
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
|
||||
/* Let's make sure videos are vertically centered if they need to be cropped */
|
||||
@ -625,7 +625,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
margin: 1%;
|
||||
max-height: 96%;
|
||||
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
|
||||
.chat-mode > div:hover {
|
||||
@ -715,7 +715,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
margin-top: 6px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
padding: 0 5px;
|
||||
transition: all .5s ease;
|
||||
transform: rotateY(0);
|
||||
@ -739,7 +739,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
|
||||
.main-console div.console:hover,
|
||||
.message-container div.clear:hover {
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
top: calc(100% + 5px);
|
||||
transform: scale(1.2) translateY(3px);
|
||||
}
|
||||
@ -772,7 +772,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
transition: all .2s ease;
|
||||
}
|
||||
.main-console .btn-action .btn:hover{
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
background-color: #ffda01;
|
||||
color: black;
|
||||
border: 1px solid black;
|
||||
@ -787,7 +787,7 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
|
||||
.main-console .menu span {
|
||||
margin: 20px;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
|
||||
.main-console .menu span.active {
|
||||
@ -821,10 +821,10 @@ input[type=range]:focus::-ms-fill-upper {
|
||||
}
|
||||
.main-console section div.upload label img{
|
||||
height: 150px;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
.main-console section div.upload label img{
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
|
||||
|
||||
@ -917,7 +917,7 @@ div.modal-report-user{
|
||||
right: 0;
|
||||
left: auto;
|
||||
top: 0;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin: 10px;
|
||||
@ -936,7 +936,7 @@ div.modal-report-user{
|
||||
transition: all .2s ease;
|
||||
}
|
||||
.modal-report-user button:hover{
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
background-color: #ffda01;
|
||||
color: black;
|
||||
border: 1px solid black;
|
||||
@ -979,7 +979,7 @@ div.modal-report-user{
|
||||
}
|
||||
.discussion .active-btn{
|
||||
display: none;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
background-color: #2d2d2dba;
|
||||
@ -1008,7 +1008,7 @@ div.modal-report-user{
|
||||
right: 10px;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
.discussion .close-btn img{
|
||||
height: 15px;
|
||||
@ -1033,7 +1033,7 @@ div.modal-report-user{
|
||||
background-color: #ffffff69;
|
||||
padding: 5px;
|
||||
border-radius: 15px;
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
|
||||
.discussion .participants .participant:hover{
|
||||
@ -1066,7 +1066,7 @@ div.modal-report-user{
|
||||
}
|
||||
|
||||
.discussion .participants .participant button.report-btn{
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
position: absolute;
|
||||
background-color: #2d2d2dba;
|
||||
right: 34px;
|
||||
@ -1176,7 +1176,7 @@ div.action.danger{
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
div.action p.action-body{
|
||||
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
padding: 10px;
|
||||
background-color: #2d2d2dba;
|
||||
color: #fff;
|
||||
@ -1225,3 +1225,11 @@ div.action.danger p.action-body{
|
||||
50% {bottom: 30px;}
|
||||
100% {bottom: 40px;}
|
||||
}
|
||||
|
||||
|
||||
#svelte-overlay {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import "jasmine";
|
||||
import {Room} from "../../../src/Connexion/Room";
|
||||
import {flattenGroupLayersMap} from "../../../src/Phaser/Map/LayersFlattener";
|
||||
import {ITiledMapLayer} from "../../../src/Phaser/Map/ITiledMap";
|
||||
import type {ITiledMapLayer} from "../../../src/Phaser/Map/ITiledMap";
|
||||
|
||||
describe("Layers flattener", () => {
|
||||
it("should iterate maps with no group", () => {
|
||||
|
@ -50,6 +50,6 @@ describe("Test HdpiManager", () => {
|
||||
const result = hdpiManager.getOptimalGameSize({ width: 1280, height: 768 });
|
||||
expect(result.game.width).toEqual(1280);
|
||||
expect(result.game.height).toEqual(768);
|
||||
expect(hdpiManager.zoomModifier).toEqual(1);
|
||||
expect(hdpiManager.zoomModifier).toEqual(2 / 3);
|
||||
});
|
||||
});
|
||||
|
9
front/tsconfig-for-jasmine.json
Normal file
9
front/tsconfig-for-jasmine.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["./src/**/*", "./tests/**/*"],
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"lib": ["es2015","dom"],
|
||||
"types": ["svelte", "node"]
|
||||
},
|
||||
}
|
7
front/tsconfig-for-webpack.json
Normal file
7
front/tsconfig-for-webpack.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
@ -1,16 +1,22 @@
|
||||
{
|
||||
// "include": ["src/**/*", "webpack.config.ts"],
|
||||
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"sourceMap": true,
|
||||
"moduleResolution": "node",
|
||||
"module": "CommonJS",
|
||||
"target": "ES2015",
|
||||
//"module": "CommonJS",
|
||||
"module": "ESNext",
|
||||
"target": "ES2017",
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
|
||||
"importsNotUsedAsValues": "error",
|
||||
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
"strictNullChecks": true, /* Enable strict null checks. */
|
||||
|
@ -1,88 +0,0 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
'main': './src/index.ts',
|
||||
'iframe_api': './src/iframe_api.ts'
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
devServer: {
|
||||
contentBase: './dist',
|
||||
host: '0.0.0.0',
|
||||
sockPort: 80,
|
||||
disableHostCheck: true,
|
||||
historyApiFallback: {
|
||||
rewrites: [
|
||||
{ from: /^_\/.*$/, to: '/index.html' }
|
||||
],
|
||||
disableDotRule: true
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [MiniCssExtractPlugin.loader, 'css-loader?url=false', 'sass-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: [ '.tsx', '.ts', '.js' ],
|
||||
},
|
||||
output: {
|
||||
filename: (pathData) => {
|
||||
// Add a content hash only for the main bundle.
|
||||
// We want the iframe_api.js file to keep its name as it will be referenced from outside iframes.
|
||||
return pathData.chunk.name === 'main' ? 'js/[name].[contenthash].js': '[name].js';
|
||||
},
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
publicPath: '/'
|
||||
},
|
||||
externals:[
|
||||
require('webpack-require-http')
|
||||
],
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({filename: 'style.[contenthash].css'}),
|
||||
new HtmlWebpackPlugin(
|
||||
{
|
||||
template: './dist/index.tmpl.html.tmp',
|
||||
minify: {
|
||||
collapseWhitespace: true,
|
||||
keepClosingSlash: true,
|
||||
removeComments: false,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
useShortDoctype: true
|
||||
},
|
||||
chunks: ['main']
|
||||
}
|
||||
),
|
||||
new webpack.ProvidePlugin({
|
||||
Phaser: 'phaser'
|
||||
}),
|
||||
new webpack.EnvironmentPlugin({
|
||||
'API_URL': null,
|
||||
'PUSHER_URL': undefined,
|
||||
'UPLOADER_URL': null,
|
||||
'ADMIN_URL': null,
|
||||
'DEBUG_MODE': null,
|
||||
'STUN_SERVER': null,
|
||||
'TURN_SERVER': null,
|
||||
'TURN_USER': null,
|
||||
'TURN_PASSWORD': null,
|
||||
'JITSI_URL': null,
|
||||
'JITSI_PRIVATE_MODE': null,
|
||||
'START_ROOM_URL': null
|
||||
})
|
||||
],
|
||||
|
||||
};
|
174
front/webpack.config.ts
Normal file
174
front/webpack.config.ts
Normal file
@ -0,0 +1,174 @@
|
||||
import type {Configuration} from "webpack";
|
||||
import type WebpackDevServer from "webpack-dev-server";
|
||||
import path from 'path';
|
||||
import webpack from 'webpack';
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
|
||||
import NodePolyfillPlugin from 'node-polyfill-webpack-plugin';
|
||||
|
||||
const mode = process.env.NODE_ENV ?? 'development';
|
||||
const isProduction = mode === 'production';
|
||||
const isDevelopment = !isProduction;
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
'main': './src/index.ts',
|
||||
'iframe_api': './src/iframe_api.ts'
|
||||
},
|
||||
mode: mode,
|
||||
devtool: isDevelopment ? 'inline-source-map' : 'source-map',
|
||||
devServer: {
|
||||
contentBase: './dist',
|
||||
host: '0.0.0.0',
|
||||
sockPort: 80,
|
||||
disableHostCheck: true,
|
||||
historyApiFallback: {
|
||||
rewrites: [
|
||||
{ from: /^_\/.*$/, to: '/index.html' }
|
||||
],
|
||||
disableDotRule: true
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
//use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
transpileOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader, {
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
//url: false,
|
||||
sourceMap: true
|
||||
}
|
||||
}, 'sass-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
//url: false,
|
||||
sourceMap: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(html|svelte)$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'svelte-loader',
|
||||
options: {
|
||||
compilerOptions: {
|
||||
// Dev mode must be enabled for HMR to work!
|
||||
dev: isDevelopment
|
||||
},
|
||||
emitCss: isProduction,
|
||||
hotReload: isDevelopment,
|
||||
hotOptions: {
|
||||
// List of options and defaults: https://www.npmjs.com/package/svelte-loader-hot#usage
|
||||
noPreserveState: false,
|
||||
optimistic: true,
|
||||
},
|
||||
preprocess: sveltePreprocess({
|
||||
scss: true,
|
||||
sass: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Required to prevent errors from Svelte on Webpack 5+, omit on Webpack 4
|
||||
// See: https://github.com/sveltejs/svelte-loader#usage
|
||||
{
|
||||
test: /node_modules\/svelte\/.*\.mjs$/,
|
||||
resolve: {
|
||||
fullySpecified: false
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|svg|png|gif|jpg)$/,
|
||||
exclude: /node_modules/,
|
||||
type: 'asset'
|
||||
}
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
svelte: path.resolve('node_modules', 'svelte')
|
||||
},
|
||||
extensions: [ '.tsx', '.ts', '.js', '.svelte' ],
|
||||
mainFields: ['svelte', 'browser', 'module', 'main']
|
||||
},
|
||||
output: {
|
||||
filename: (pathData) => {
|
||||
// Add a content hash only for the main bundle.
|
||||
// We want the iframe_api.js file to keep its name as it will be referenced from outside iframes.
|
||||
return pathData.chunk?.name === 'main' ? 'js/[name].[contenthash].js': '[name].js';
|
||||
},
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
publicPath: '/'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
eslint: {
|
||||
files: './src/**/*.ts'
|
||||
}
|
||||
}),
|
||||
new MiniCssExtractPlugin({filename: '[name].[contenthash].css'}),
|
||||
new HtmlWebpackPlugin(
|
||||
{
|
||||
template: './dist/index.tmpl.html.tmp',
|
||||
minify: {
|
||||
collapseWhitespace: true,
|
||||
keepClosingSlash: true,
|
||||
removeComments: false,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
useShortDoctype: true
|
||||
},
|
||||
chunks: ['main']
|
||||
}
|
||||
),
|
||||
new webpack.ProvidePlugin({
|
||||
Phaser: 'phaser'
|
||||
}),
|
||||
new NodePolyfillPlugin(),
|
||||
new webpack.EnvironmentPlugin({
|
||||
'API_URL': null,
|
||||
'SKIP_RENDER_OPTIMIZATIONS': false,
|
||||
'DISABLE_NOTIFICATIONS': false,
|
||||
'PUSHER_URL': undefined,
|
||||
'UPLOADER_URL': null,
|
||||
'ADMIN_URL': null,
|
||||
'DEBUG_MODE': null,
|
||||
'STUN_SERVER': null,
|
||||
'TURN_SERVER': null,
|
||||
'TURN_USER': null,
|
||||
'TURN_PASSWORD': null,
|
||||
'JITSI_URL': null,
|
||||
'JITSI_PRIVATE_MODE': null,
|
||||
'START_ROOM_URL': null,
|
||||
'MAX_USERNAME_LENGTH': 8,
|
||||
'MAX_PER_GROUP': 4
|
||||
})
|
||||
],
|
||||
|
||||
} as Configuration & WebpackDevServer.Configuration;
|
@ -1,7 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.config.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production',
|
||||
devtool: 'source-map'
|
||||
});
|
2684
front/yarn.lock
2684
front/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user