Merge branch 'develop' of github.com:thecodingmachine/workadventure into resizeSelect
@ -12,6 +12,11 @@
|
|||||||
- The emote menu can be opened by clicking on your character.
|
- The emote menu can be opened by clicking on your character.
|
||||||
- Clicking on one of its element will close the menu and play an emote above your character.
|
- Clicking on one of its element will close the menu and play an emote above your character.
|
||||||
- This emote can be seen by other players.
|
- This emote can be seen by other players.
|
||||||
|
- Player names were improved. (@Kharhamel)
|
||||||
|
- We now create a GameObject.Text instead of GameObject.BitmapText
|
||||||
|
- now use the 'Press Start 2P' font family and added an outline
|
||||||
|
- As a result, we can now allow non-standard letters like french accents or chinese characters!
|
||||||
|
|
||||||
- Mobile support has been improved
|
- Mobile support has been improved
|
||||||
- WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used
|
- WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used
|
||||||
- Mouse wheel support to zoom in / out
|
- Mouse wheel support to zoom in / out
|
||||||
|
27
front/dist/index.tmpl.html
vendored
@ -29,7 +29,6 @@
|
|||||||
|
|
||||||
|
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Press+Start+2P" rel="stylesheet">
|
|
||||||
<link href="https://unpkg.com/nes.css@2.3.0/css/nes.min.css" rel="stylesheet" />
|
<link href="https://unpkg.com/nes.css@2.3.0/css/nes.min.css" rel="stylesheet" />
|
||||||
|
|
||||||
<title>WorkAdventure</title>
|
<title>WorkAdventure</title>
|
||||||
@ -47,32 +46,6 @@
|
|||||||
</aside>
|
</aside>
|
||||||
<div id="chat-mode" class="chat-mode three-col" style="display: none;">
|
<div id="chat-mode" class="chat-mode three-col" style="display: none;">
|
||||||
</div>
|
</div>
|
||||||
<div id="activeCam" class="activeCam">
|
|
||||||
<div id="div-myCamVideo" class="video-container">
|
|
||||||
<video id="myCamVideo" autoplay muted></video>
|
|
||||||
<div id="mySoundMeter" class="sound-progress">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="btn-cam-action">
|
|
||||||
<div id="btn-monitor" class="btn-monitor">
|
|
||||||
<img id="monitor" src="resources/logos/monitor.svg">
|
|
||||||
<img id="monitor-close" src="resources/logos/monitor-close.svg">
|
|
||||||
</div>
|
|
||||||
<div id="btn-video" class="btn-video">
|
|
||||||
<img id="cinema" src="resources/logos/cinema.svg">
|
|
||||||
<img id="cinema-close" src="resources/logos/cinema-close.svg">
|
|
||||||
</div>
|
|
||||||
<div id="btn-micro" class="btn-micro">
|
|
||||||
<img id="microphone" src="resources/logos/microphone.svg">
|
|
||||||
<img id="microphone-close" src="resources/logos/microphone-close.svg">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="cowebsite" class="cowebsite hidden">
|
<div id="cowebsite" class="cowebsite hidden">
|
||||||
|
5
front/dist/resources/fonts/fonts.css
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*This file is a workaround to allow phaser to load directly this font */
|
||||||
|
@font-face {
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
src: url("/fonts/press-start-2p-latin-400-normal.woff2") format('woff2');
|
||||||
|
}
|
@ -34,6 +34,7 @@
|
|||||||
"webpack-dev-server": "^3.11.2"
|
"webpack-dev-server": "^3.11.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource/press-start-2p": "^4.3.0",
|
||||||
"@types/simple-peer": "^9.6.0",
|
"@types/simple-peer": "^9.6.0",
|
||||||
"@types/socket.io-client": "^1.4.32",
|
"@types/socket.io-client": "^1.4.32",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import MenuIcon from "./Menu/MenuIcon.svelte";
|
import MenuIcon from "./Menu/MenuIcon.svelte";
|
||||||
import {menuIconVisible} from "../Stores/MenuStore";
|
import {menuIconVisible} from "../Stores/MenuStore";
|
||||||
|
import {gameOverlayVisibilityStore} from "../Stores/MediaStore";
|
||||||
|
import CameraControls from "./CameraControls.svelte";
|
||||||
|
import MyCamera from "./MyCamera.svelte";
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
{#if $gameOverlayVisibilityStore}
|
||||||
<!-- {#if $menuIconVisible}
|
<!-- {#if $menuIconVisible}
|
||||||
<MenuIcon />
|
<MenuIcon />
|
||||||
{/if} -->
|
{/if} -->
|
||||||
|
<MyCamera></MyCamera>
|
||||||
|
<CameraControls></CameraControls>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
59
front/src/Components/CameraControls.svelte
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<script lang="typescript">
|
||||||
|
import {requestedScreenSharingState, screenSharingAvailableStore} from "../Stores/ScreenSharingStore";
|
||||||
|
import {requestedCameraState, requestedMicrophoneState} from "../Stores/MediaStore";
|
||||||
|
import monitorImg from "./images/monitor.svg";
|
||||||
|
import monitorCloseImg from "./images/monitor-close.svg";
|
||||||
|
import cinemaImg from "./images/cinema.svg";
|
||||||
|
import cinemaCloseImg from "./images/cinema-close.svg";
|
||||||
|
import microphoneImg from "./images/microphone.svg";
|
||||||
|
import microphoneCloseImg from "./images/microphone-close.svg";
|
||||||
|
|
||||||
|
function screenSharingClick(): void {
|
||||||
|
if ($requestedScreenSharingState === true) {
|
||||||
|
requestedScreenSharingState.disableScreenSharing();
|
||||||
|
} else {
|
||||||
|
requestedScreenSharingState.enableScreenSharing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cameraClick(): void {
|
||||||
|
if ($requestedCameraState === true) {
|
||||||
|
requestedCameraState.disableWebcam();
|
||||||
|
} else {
|
||||||
|
requestedCameraState.enableWebcam();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function microphoneClick(): void {
|
||||||
|
if ($requestedMicrophoneState === true) {
|
||||||
|
requestedMicrophoneState.disableMicrophone();
|
||||||
|
} else {
|
||||||
|
requestedMicrophoneState.enableMicrophone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="btn-cam-action">
|
||||||
|
<div class="btn-monitor" on:click={screenSharingClick} class:hide={!$screenSharingAvailableStore} class:enabled={$requestedScreenSharingState}>
|
||||||
|
{#if $requestedScreenSharingState}
|
||||||
|
<img src={monitorImg} alt="Start screen sharing">
|
||||||
|
{:else}
|
||||||
|
<img src={monitorCloseImg} alt="Stop screen sharing">
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="btn-video" on:click={cameraClick} class:disabled={!$requestedCameraState}>
|
||||||
|
{#if $requestedCameraState}
|
||||||
|
<img src={cinemaImg} alt="Turn on webcam">
|
||||||
|
{:else}
|
||||||
|
<img src={cinemaCloseImg} alt="Turn off webcam">
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="btn-micro" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState}>
|
||||||
|
{#if $requestedMicrophoneState}
|
||||||
|
<img src={microphoneImg} alt="Turn on microphone">
|
||||||
|
{:else}
|
||||||
|
<img src={microphoneCloseImg} alt="Turn off microphone">
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
46
front/src/Components/MyCamera.svelte
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<script lang="typescript">
|
||||||
|
import {localStreamStore} from "../Stores/MediaStore";
|
||||||
|
import SoundMeterWidget from "./SoundMeterWidget.svelte";
|
||||||
|
import {onDestroy} from "svelte";
|
||||||
|
|
||||||
|
function srcObject(node, stream) {
|
||||||
|
node.srcObject = stream;
|
||||||
|
return {
|
||||||
|
update(newStream) {
|
||||||
|
if (node.srcObject != newStream) {
|
||||||
|
node.srcObject = newStream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream : MediaStream|null;
|
||||||
|
/*$: {
|
||||||
|
if ($localStreamStore.type === 'success') {
|
||||||
|
stream = $localStreamStore.stream;
|
||||||
|
} else {
|
||||||
|
stream = null;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
const unsubscribe = localStreamStore.subscribe(value => {
|
||||||
|
if (value.type === 'success') {
|
||||||
|
stream = value.stream;
|
||||||
|
} else {
|
||||||
|
stream = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(unsubscribe);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="video-container div-myCamVideo" class:hide={!$localStreamStore.constraints.video}>
|
||||||
|
<video class="myCamVideo" use:srcObject={$localStreamStore.stream} autoplay muted></video>
|
||||||
|
<!-- {#if stream}
|
||||||
|
<SoundMeterWidget stream={stream}></SoundMeterWidget>
|
||||||
|
{/if} -->
|
||||||
|
</div>
|
||||||
|
</div>
|
36
front/src/Components/SoundMeterWidget.svelte
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<script lang="typescript">
|
||||||
|
import {SoundMeter} from "../Phaser/Components/SoundMeter";
|
||||||
|
import {onDestroy} from "svelte";
|
||||||
|
|
||||||
|
export let stream: MediaStream;
|
||||||
|
let volume = 0;
|
||||||
|
|
||||||
|
console.log('stream', stream);
|
||||||
|
|
||||||
|
if (stream.getAudioTracks().length > 0) {
|
||||||
|
const soundMeter = new SoundMeter();
|
||||||
|
soundMeter.connectToSource(stream, new AudioContext());
|
||||||
|
|
||||||
|
const timeout = setInterval(() => {
|
||||||
|
try{
|
||||||
|
volume = parseInt((soundMeter.getVolume() / 10).toFixed(0));
|
||||||
|
console.log(volume);
|
||||||
|
}catch(err){
|
||||||
|
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
clearInterval(timeout);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="sound-progress" class:active={stream?.getAudioTracks().length > 0}>
|
||||||
|
<span class:active={volume > 1}></span>
|
||||||
|
<span class:active={volume > 2}></span>
|
||||||
|
<span class:active={volume > 3}></span>
|
||||||
|
<span class:active={volume > 4}></span>
|
||||||
|
<span class:active={volume > 5}></span>
|
||||||
|
</div>
|
41
front/src/Components/images/cinema-close.svg
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 332.8 332.8" style="enable-background:new 0 0 332.8 332.8;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M330.8,171c-3.6-6.4-12-8.8-18.8-4.8l-45.6,26.4l-11.6,6.8v63.2l10.8,6.4c0.4,0,0.4,0.4,0.8,0.4l44.8,26
|
||||||
|
c2,1.6,4.8,2.4,7.6,2.4c7.6,0,13.6-6,13.6-13.6v-53.6l0.4-52.8C332.8,175.4,332.4,173,330.8,171z"/>
|
||||||
|
<path class="st0" d="M193.2,150.6c35.6,0,64.4-28.8,64.4-64.4s-28.8-64.4-64.4-64.4s-64.4,28.8-64.4,64.4
|
||||||
|
C128.8,121.8,157.6,150.6,193.2,150.6z M193.2,59.8c14.8,0,26.4,12,26.4,26.4c0,14.8-12,26.4-26.4,26.4s-26.4-12-26.4-26.4
|
||||||
|
C166.8,71.4,178.4,59.8,193.2,59.8z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<rect x="134.8" y="-45.3" transform="matrix(-0.7402 0.6723 -0.6723 -0.7402 376.0669 224.8258)" class="st0" width="19.6" height="460.7"/>
|
||||||
|
<path class="st0" d="M90.6,83.3c-0.2-2.2-1.3-8.9-6.7-14.9c-5.4-5.9-11.9-7.6-14.1-8.1C59.7,49.2,49.5,38,39.4,26.8
|
||||||
|
c24.3-9.8,52-4.4,70.2,13.6c19.9,19.7,24.7,50.8,11.5,76.4C110.9,105.6,100.8,94.5,90.6,83.3z"/>
|
||||||
|
<path class="st0" d="M10.1,51.6c9.4,10.2,18.8,20.4,28.2,30.6c-0.2,1.8-1.4,11.7,5.5,20.5c8.2,10.3,20.7,10.2,22.1,10.1
|
||||||
|
c9.2,10.3,18.5,20.6,27.7,30.8c-4.8,2.3-24.6,11.2-48.3,4.1c-6-1.8-20.7-7.3-32.1-22C-0.3,108.1-0.2,89.1,0.1,83.4
|
||||||
|
C0.8,68,6.8,56.8,10.1,51.6z"/>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M243.4,178.2c0.1,24.5,0.2,49,0.2,73.5c-30.7-33.8-61.3-67.7-92-101.5c5.9,3.9,20.9,12.4,41.6,12.4
|
||||||
|
c16,0,28.2-5.2,34.4-8.4c2.5,1.5,7,4.6,10.7,10.3C242,170,243,175.4,243.4,178.2z"/>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M211.2,311C150.8,258.7,90.4,206.5,30,154.2c6.1,3.1,18.2,8.4,34.4,8.4c18.1,0,31.5-6.5,37.5-9.9
|
||||||
|
c44.5,49,89.1,98.1,133.6,147.1c-1.8,2.1-5.3,5.5-10.6,8.1C219.2,310.6,214,311,211.2,311z"/>
|
||||||
|
<path class="st0" d="M46.8,311C36,267.7,25.2,224.3,14.4,181c0.1-3.2,0.7-11.3,6.5-18.8c3.1-4.1,6.7-6.6,9.1-8
|
||||||
|
C90.4,206.5,150.8,258.7,211.2,311C156.4,311,101.6,311,46.8,311z"/>
|
||||||
|
<path class="st0" d="M14.4,278.6L14.4,278.6c0-32.5,0-65.1,0-97.6c10.8,43.3,21.6,86.7,32.4,130c-2.6,0-12.7-0.4-21.5-8.1
|
||||||
|
C14.7,293.5,14.4,280.7,14.4,278.6z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
27
front/src/Components/images/microphone-close.svg
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<rect x="257" y="-47.9" transform="matrix(-0.7402 0.6723 -0.6723 -0.7402 643.9641 283.6469)" class="st0" width="20.4" height="628.3"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M333.6,250.3c-52.6-43.9-105.1-87.9-157.7-131.8c0-17.9,0-35.8,0-53.6c6.5-38.6,40.3-67,79.3-66.8
|
||||||
|
c38.6,0.2,71.9,28.5,78.4,66.8C333.6,126.7,333.6,188.5,333.6,250.3z"/>
|
||||||
|
<path class="st0" d="M322.6,279.9c-48.9-53.8-97.8-107.6-146.6-161.4l0,0c52.6,43.9,105.1,87.9,157.7,131.8
|
||||||
|
c-0.2,1.6-0.5,3.3-0.9,5C330.5,265.2,326.6,273.5,322.6,279.9z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st0" d="M292.5,308.1c-2.3,1.2-39.5,20.3-76.7-1c-36.4-20.8-39.4-61.2-39.6-64.1c-0.1-21-0.1-42.1-0.2-63.1
|
||||||
|
C214.8,222.6,253.6,265.3,292.5,308.1z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st0" d="M431.6,238.5c-0.9-8.4-8.5-14.4-16.6-13.5c-7.9,0.9-13.9,8.1-13.2,16.3c-0.1,13.3-2.2,34.6-12.6,57.9
|
||||||
|
c-6.3,14.2-14,25.2-20.6,33.1c6.8,7.5,13.6,14.9,20.3,22.4c9.5-10.9,23.4-29.7,32.8-56.3C430.3,273.9,431.8,252.5,431.6,238.5z"/>
|
||||||
|
<line class="st0" x1="354.5" y1="347.2" x2="374.6" y2="369.4"/>
|
||||||
|
<path class="st0" d="M338.5,359.9c6.8,7.4,13.5,14.9,20.3,22.3c-52.6,37.6-121.5,43.7-179.2,15.8c-60.3-29.1-98.9-90.7-99.3-158.2
|
||||||
|
c0-8.2,6.8-15,15-15s15,6.8,15,15c0.1,13.5,2.4,54.4,32.4,91.6c4.2,5.2,45.1,54.1,113.3,54.1C297,385.6,326.7,367.9,338.5,359.9z"/>
|
||||||
|
<rect x="241" y="409.6" class="st0" width="29.9" height="102.3"/>
|
||||||
|
<path class="st0" d="M304.2,511.9h-97.1c-8-0.4-14.3-7.1-14.3-15c0-8.1,6.7-14.9,15-15c31.7,0,63.4,0.1,95.1,0.1
|
||||||
|
c8.9-0.6,16.3,6.5,16.3,14.9C319.2,504.8,312.6,511.7,304.2,511.9z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 937 B After Width: | Height: | Size: 937 B |
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 884 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@ -9,9 +9,8 @@ export interface CharacterTexture {
|
|||||||
|
|
||||||
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
|
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
|
||||||
|
|
||||||
export function isUserNameValid(value: string): boolean {
|
export function isUserNameValid(value: unknown): boolean {
|
||||||
const regexp = new RegExp('^[A-Za-z0-9]{1,'+maxUserNameLength+'}$');
|
return typeof value === "string" && value.length > 0 && value.length < maxUserNameLength && value.indexOf(' ') === -1;
|
||||||
return regexp.test(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function areCharacterLayersValid(value: string[] | null): boolean {
|
export function areCharacterLayersValid(value: string[] | null): boolean {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {PlayerAnimationDirections, PlayerAnimationTypes} from "../Player/Animation";
|
import {PlayerAnimationDirections, PlayerAnimationTypes} from "../Player/Animation";
|
||||||
import {SpeechBubble} from "./SpeechBubble";
|
import {SpeechBubble} from "./SpeechBubble";
|
||||||
import BitmapText = Phaser.GameObjects.BitmapText;
|
import Text = Phaser.GameObjects.Text;
|
||||||
import Container = Phaser.GameObjects.Container;
|
import Container = Phaser.GameObjects.Container;
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import {TextureError} from "../../Exception/TextureError";
|
import {TextureError} from "../../Exception/TextureError";
|
||||||
@ -23,7 +23,7 @@ const interactiveRadius = 35;
|
|||||||
|
|
||||||
export abstract class Character extends Container {
|
export abstract class Character extends Container {
|
||||||
private bubble: SpeechBubble|null = null;
|
private bubble: SpeechBubble|null = null;
|
||||||
private readonly playerName: BitmapText;
|
private readonly playerName: Text;
|
||||||
public PlayerValue: string;
|
public PlayerValue: string;
|
||||||
public sprites: Map<string, Sprite>;
|
public sprites: Map<string, Sprite>;
|
||||||
private lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down;
|
private lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down;
|
||||||
@ -55,9 +55,9 @@ export abstract class Character extends Container {
|
|||||||
this.addTextures(textures, frame);
|
this.addTextures(textures, frame);
|
||||||
this.invisible = false
|
this.invisible = false
|
||||||
})
|
})
|
||||||
|
|
||||||
this.playerName = new BitmapText(scene, 0, playerNameY, 'main_font', name, 7);
|
this.playerName = new Text(scene, 0, playerNameY, name, {fontFamily: '"Press Start 2P"', fontSize: '8px', strokeThickness: 2, stroke: "gray"});
|
||||||
this.playerName.setOrigin(0.5).setCenterAlign().setDepth(DEPTH_INGAME_TEXT_INDEX);
|
this.playerName.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX);
|
||||||
this.add(this.playerName);
|
this.add(this.playerName);
|
||||||
|
|
||||||
if (this.isClickable()) {
|
if (this.isClickable()) {
|
||||||
|
@ -279,6 +279,14 @@ export class GameScene extends DirtyScene implements CenterListener {
|
|||||||
|
|
||||||
this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32});
|
this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32});
|
||||||
this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||||
|
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(this.load as any).rexWebFont({
|
||||||
|
custom: {
|
||||||
|
families: ['Press Start 2P'],
|
||||||
|
urls: ['/resources/fonts/fonts.css'],
|
||||||
|
testString: 'abcdefg'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
//this function must stay at the end of preload function
|
//this function must stay at the end of preload function
|
||||||
addLoader(this);
|
addLoader(this);
|
||||||
|
@ -41,7 +41,7 @@ export class LoginScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
pErrorElement.innerHTML = '';
|
pErrorElement.innerHTML = '';
|
||||||
if(inputElement.value && !isUserNameValid(inputElement.value)){
|
if(inputElement.value && !isUserNameValid(inputElement.value)){
|
||||||
pErrorElement.innerHTML = 'Invalid user name: only letters and numbers are allowed. No spaces.';
|
pErrorElement.innerHTML = 'Invalid user name: No spaces are allowed.';
|
||||||
}
|
}
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -8,33 +8,11 @@ import {SoundMeter} from "../Phaser/Components/SoundMeter";
|
|||||||
import {DISABLE_NOTIFICATIONS} from "../Enum/EnvironmentVariable";
|
import {DISABLE_NOTIFICATIONS} from "../Enum/EnvironmentVariable";
|
||||||
import {
|
import {
|
||||||
gameOverlayVisibilityStore, localStreamStore,
|
gameOverlayVisibilityStore, localStreamStore,
|
||||||
mediaStreamConstraintsStore,
|
|
||||||
requestedCameraState,
|
|
||||||
requestedMicrophoneState
|
|
||||||
} from "../Stores/MediaStore";
|
} from "../Stores/MediaStore";
|
||||||
import {
|
import {
|
||||||
requestedScreenSharingState,
|
|
||||||
screenSharingAvailableStore,
|
|
||||||
screenSharingLocalStreamStore
|
screenSharingLocalStreamStore
|
||||||
} from "../Stores/ScreenSharingStore";
|
} from "../Stores/ScreenSharingStore";
|
||||||
|
|
||||||
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
||||||
|
|
||||||
const videoConstraint: boolean|MediaTrackConstraints = {
|
|
||||||
width: { min: 640, ideal: 1280, max: 1920 },
|
|
||||||
height: { min: 400, ideal: 720 },
|
|
||||||
frameRate: { ideal: localUserStore.getVideoQualityValue() },
|
|
||||||
facingMode: "user",
|
|
||||||
resizeMode: 'crop-and-scale',
|
|
||||||
aspectRatio: 1.777777778
|
|
||||||
};
|
|
||||||
const audioConstraint: boolean|MediaTrackConstraints = {
|
|
||||||
//TODO: make these values configurable in the game settings menu and store them in localstorage
|
|
||||||
autoGainControl: false,
|
|
||||||
echoCancellation: true,
|
|
||||||
noiseSuppression: true
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
|
export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
|
||||||
export type StartScreenSharingCallback = (media: MediaStream) => void;
|
export type StartScreenSharingCallback = (media: MediaStream) => void;
|
||||||
export type StopScreenSharingCallback = (media: MediaStream) => void;
|
export type StopScreenSharingCallback = (media: MediaStream) => void;
|
||||||
@ -43,30 +21,16 @@ export type ShowReportCallBack = (userId: string, userName: string|undefined) =>
|
|||||||
export type HelpCameraSettingsCallBack = () => void;
|
export type HelpCameraSettingsCallBack = () => void;
|
||||||
|
|
||||||
export class MediaManager {
|
export class MediaManager {
|
||||||
localStream: MediaStream|null = null;
|
|
||||||
localScreenCapture: MediaStream|null = null;
|
|
||||||
private remoteVideo: Map<string, HTMLVideoElement> = new Map<string, HTMLVideoElement>();
|
private remoteVideo: Map<string, HTMLVideoElement> = new Map<string, HTMLVideoElement>();
|
||||||
myCamVideo: HTMLVideoElement;
|
|
||||||
cinemaClose: HTMLImageElement;
|
|
||||||
cinema: HTMLImageElement;
|
|
||||||
monitorClose: HTMLImageElement;
|
|
||||||
monitor: HTMLImageElement;
|
|
||||||
microphoneClose: HTMLImageElement;
|
|
||||||
microphone: HTMLImageElement;
|
|
||||||
webrtcInAudio: HTMLAudioElement;
|
webrtcInAudio: HTMLAudioElement;
|
||||||
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
//FIX ME SOUNDMETER: check stalability of sound meter calculation
|
||||||
//mySoundMeterElement: HTMLDivElement;
|
//mySoundMeterElement: HTMLDivElement;
|
||||||
private webrtcOutAudio: HTMLAudioElement;
|
private webrtcOutAudio: HTMLAudioElement;
|
||||||
updatedLocalStreamCallBacks : Set<UpdatedLocalStreamCallback> = new Set<UpdatedLocalStreamCallback>();
|
|
||||||
startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
|
startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
|
||||||
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
|
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
|
||||||
showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>();
|
showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>();
|
||||||
helpCameraSettingsCallBacks : Set<HelpCameraSettingsCallBack> = new Set<HelpCameraSettingsCallBack>();
|
helpCameraSettingsCallBacks : Set<HelpCameraSettingsCallBack> = new Set<HelpCameraSettingsCallBack>();
|
||||||
|
|
||||||
private microphoneBtn: HTMLDivElement;
|
|
||||||
private cinemaBtn: HTMLDivElement;
|
|
||||||
private monitorBtn: HTMLDivElement;
|
|
||||||
|
|
||||||
private focused : boolean = true;
|
private focused : boolean = true;
|
||||||
|
|
||||||
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
|
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
|
||||||
@ -80,54 +44,11 @@ export class MediaManager {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
this.myCamVideo = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('myCamVideo');
|
|
||||||
this.webrtcInAudio = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('audio-webrtc-in');
|
this.webrtcInAudio = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('audio-webrtc-in');
|
||||||
this.webrtcOutAudio = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('audio-webrtc-out');
|
this.webrtcOutAudio = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('audio-webrtc-out');
|
||||||
this.webrtcInAudio.volume = 0.2;
|
this.webrtcInAudio.volume = 0.2;
|
||||||
this.webrtcOutAudio.volume = 0.2;
|
this.webrtcOutAudio.volume = 0.2;
|
||||||
|
|
||||||
this.microphoneBtn = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('btn-micro');
|
|
||||||
this.microphoneClose = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('microphone-close');
|
|
||||||
this.microphoneClose.style.display = "none";
|
|
||||||
this.microphoneClose.addEventListener('click', (e: MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
requestedMicrophoneState.enableMicrophone();
|
|
||||||
});
|
|
||||||
this.microphone = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('microphone');
|
|
||||||
this.microphone.addEventListener('click', (e: MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
requestedMicrophoneState.disableMicrophone();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.cinemaBtn = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('btn-video');
|
|
||||||
this.cinemaClose = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('cinema-close');
|
|
||||||
this.cinemaClose.style.display = "none";
|
|
||||||
this.cinemaClose.addEventListener('click', (e: MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
requestedCameraState.enableWebcam();
|
|
||||||
});
|
|
||||||
this.cinema = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('cinema');
|
|
||||||
this.cinema.addEventListener('click', (e: MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
requestedCameraState.disableWebcam();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.monitorBtn = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('btn-monitor');
|
|
||||||
this.monitorClose = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('monitor-close');
|
|
||||||
this.monitorClose.style.display = "block";
|
|
||||||
this.monitorClose.addEventListener('click', (e: MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
//this.enableScreenSharing();
|
|
||||||
requestedScreenSharingState.enableScreenSharing();
|
|
||||||
});
|
|
||||||
this.monitor = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('monitor');
|
|
||||||
this.monitor.style.display = "none";
|
|
||||||
this.monitor.addEventListener('click', (e: MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
//this.disableScreenSharing();
|
|
||||||
requestedScreenSharingState.disableScreenSharing();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.pingCameraStatus();
|
this.pingCameraStatus();
|
||||||
|
|
||||||
//FIX ME SOUNDMETER: check stability of sound meter calculation
|
//FIX ME SOUNDMETER: check stability of sound meter calculation
|
||||||
@ -147,40 +68,8 @@ export class MediaManager {
|
|||||||
}, this.userInputManager);
|
}, this.userInputManager);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.constraints.video !== false) {
|
|
||||||
HtmlUtils.getElementByIdOrFail('div-myCamVideo').classList.remove('hide');
|
|
||||||
} else {
|
|
||||||
HtmlUtils.getElementByIdOrFail('div-myCamVideo').classList.add('hide');
|
|
||||||
}/*
|
|
||||||
if (result.constraints.audio !== false) {
|
|
||||||
this.enableMicrophoneStyle();
|
|
||||||
} else {
|
|
||||||
this.disableMicrophoneStyle();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
this.localStream = result.stream;
|
|
||||||
this.myCamVideo.srcObject = this.localStream;
|
|
||||||
|
|
||||||
// TODO: migrate all listeners to the store directly.
|
|
||||||
this.triggerUpdatedLocalStreamCallbacks(result.stream);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
requestedCameraState.subscribe((enabled) => {
|
|
||||||
if (enabled) {
|
|
||||||
this.enableCameraStyle();
|
|
||||||
} else {
|
|
||||||
this.disableCameraStyle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
requestedMicrophoneState.subscribe((enabled) => {
|
|
||||||
if (enabled) {
|
|
||||||
this.enableMicrophoneStyle();
|
|
||||||
} else {
|
|
||||||
this.disableMicrophoneStyle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//let screenSharingStream : MediaStream|null;
|
|
||||||
screenSharingLocalStreamStore.subscribe((result) => {
|
screenSharingLocalStreamStore.subscribe((result) => {
|
||||||
if (result.type === 'error') {
|
if (result.type === 'error') {
|
||||||
console.error(result.error);
|
console.error(result.error);
|
||||||
@ -191,38 +80,21 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result.stream !== null) {
|
if (result.stream !== null) {
|
||||||
this.enableScreenSharingStyle();
|
|
||||||
mediaManager.localScreenCapture = result.stream;
|
|
||||||
|
|
||||||
// TODO: migrate this out of MediaManager
|
|
||||||
this.triggerStartedScreenSharingCallbacks(result.stream);
|
|
||||||
|
|
||||||
//screenSharingStream = result.stream;
|
|
||||||
|
|
||||||
this.addScreenSharingActiveVideo('me', DivImportance.Normal);
|
this.addScreenSharingActiveVideo('me', DivImportance.Normal);
|
||||||
HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('screen-sharing-me').srcObject = result.stream;
|
HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('screen-sharing-me').srcObject = result.stream;
|
||||||
} else {
|
} else {
|
||||||
this.disableScreenSharingStyle();
|
|
||||||
this.removeActiveScreenSharingVideo('me');
|
this.removeActiveScreenSharingVideo('me');
|
||||||
|
|
||||||
// FIXME: we need the old stream that is being stopped!
|
|
||||||
if (this.localScreenCapture) {
|
|
||||||
this.triggerStoppedScreenSharingCallbacks(this.localScreenCapture);
|
|
||||||
this.localScreenCapture = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
//screenSharingStream = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
screenSharingAvailableStore.subscribe((available) => {
|
/*screenSharingAvailableStore.subscribe((available) => {
|
||||||
if (available) {
|
if (available) {
|
||||||
document.querySelector('.btn-monitor')?.classList.remove('hide');
|
document.querySelector('.btn-monitor')?.classList.remove('hide');
|
||||||
} else {
|
} else {
|
||||||
document.querySelector('.btn-monitor')?.classList.add('hide');
|
document.querySelector('.btn-monitor')?.classList.add('hide');
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateScene(){
|
public updateScene(){
|
||||||
@ -230,40 +102,6 @@ export class MediaManager {
|
|||||||
//this.updateSoudMeter();
|
//this.updateSoudMeter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void {
|
|
||||||
this.updatedLocalStreamCallBacks.add(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onStartScreenSharing(callback: StartScreenSharingCallback): void {
|
|
||||||
this.startScreenSharingCallBacks.add(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onStopScreenSharing(callback: StopScreenSharingCallback): void {
|
|
||||||
this.stopScreenSharingCallBacks.add(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeUpdateLocalStreamEventListener(callback: UpdatedLocalStreamCallback): void {
|
|
||||||
this.updatedLocalStreamCallBacks.delete(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
private triggerUpdatedLocalStreamCallbacks(stream: MediaStream|null): void {
|
|
||||||
for (const callback of this.updatedLocalStreamCallBacks) {
|
|
||||||
callback(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private triggerStartedScreenSharingCallbacks(stream: MediaStream): void {
|
|
||||||
for (const callback of this.startScreenSharingCallBacks) {
|
|
||||||
callback(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private triggerStoppedScreenSharingCallbacks(stream: MediaStream): void {
|
|
||||||
for (const callback of this.stopScreenSharingCallBacks) {
|
|
||||||
callback(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public showGameOverlay(): void {
|
public showGameOverlay(): void {
|
||||||
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
|
||||||
gameOverlay.classList.add('active');
|
gameOverlay.classList.add('active');
|
||||||
@ -290,42 +128,6 @@ export class MediaManager {
|
|||||||
gameOverlayVisibilityStore.hideGameOverlay();
|
gameOverlayVisibilityStore.hideGameOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private enableCameraStyle(){
|
|
||||||
this.cinemaClose.style.display = "none";
|
|
||||||
this.cinemaBtn.classList.remove("disabled");
|
|
||||||
this.cinema.style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
private disableCameraStyle(){
|
|
||||||
this.cinemaClose.style.display = "block";
|
|
||||||
this.cinema.style.display = "none";
|
|
||||||
this.cinemaBtn.classList.add("disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
private enableMicrophoneStyle(){
|
|
||||||
this.microphoneClose.style.display = "none";
|
|
||||||
this.microphone.style.display = "block";
|
|
||||||
this.microphoneBtn.classList.remove("disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
private disableMicrophoneStyle(){
|
|
||||||
this.microphoneClose.style.display = "block";
|
|
||||||
this.microphone.style.display = "none";
|
|
||||||
this.microphoneBtn.classList.add("disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
private enableScreenSharingStyle(){
|
|
||||||
this.monitorClose.style.display = "none";
|
|
||||||
this.monitor.style.display = "block";
|
|
||||||
this.monitorBtn.classList.add("enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
private disableScreenSharingStyle(){
|
|
||||||
this.monitorClose.style.display = "block";
|
|
||||||
this.monitor.style.display = "none";
|
|
||||||
this.monitorBtn.classList.remove("enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
addActiveVideo(user: UserSimplePeerInterface, userName: string = ""){
|
addActiveVideo(user: UserSimplePeerInterface, userName: string = ""){
|
||||||
this.webrtcInAudio.play();
|
this.webrtcInAudio.play();
|
||||||
const userId = ''+user.userId
|
const userId = ''+user.userId
|
||||||
|
@ -19,7 +19,7 @@ export class ScreenSharingPeer extends Peer {
|
|||||||
public _connected: boolean = false;
|
public _connected: boolean = false;
|
||||||
private userId: number;
|
private userId: number;
|
||||||
|
|
||||||
constructor(user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) {
|
constructor(user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection, stream: MediaStream | null) {
|
||||||
super({
|
super({
|
||||||
initiator: initiator ? initiator : false,
|
initiator: initiator ? initiator : false,
|
||||||
//reconnectTimer: 10000,
|
//reconnectTimer: 10000,
|
||||||
@ -81,7 +81,9 @@ export class ScreenSharingPeer extends Peer {
|
|||||||
this._onFinish();
|
this._onFinish();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.pushScreenSharingToRemoteUser();
|
if (stream) {
|
||||||
|
this.addStream(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendWebrtcScreenSharingSignal(data: unknown) {
|
private sendWebrtcScreenSharingSignal(data: unknown) {
|
||||||
@ -141,16 +143,6 @@ export class ScreenSharingPeer extends Peer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private pushScreenSharingToRemoteUser() {
|
|
||||||
const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture;
|
|
||||||
if(!localScreenCapture){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addStream(localScreenCapture);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
public stopPushingScreenSharingToRemoteUser(stream: MediaStream) {
|
public stopPushingScreenSharingToRemoteUser(stream: MediaStream) {
|
||||||
this.removeStream(stream);
|
this.removeStream(stream);
|
||||||
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, streamEnded: true})));
|
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, streamEnded: true})));
|
||||||
|
@ -15,7 +15,10 @@ import {connectionManager} from "../Connexion/ConnectionManager";
|
|||||||
import {GameConnexionTypes} from "../Url/UrlManager";
|
import {GameConnexionTypes} from "../Url/UrlManager";
|
||||||
import {blackListManager} from "./BlackListManager";
|
import {blackListManager} from "./BlackListManager";
|
||||||
import {get} from "svelte/store";
|
import {get} from "svelte/store";
|
||||||
import {localStreamStore, obtainedMediaConstraintStore} from "../Stores/MediaStore";
|
import {localStreamStore, LocalStreamStoreValue, obtainedMediaConstraintStore} from "../Stores/MediaStore";
|
||||||
|
import {screenSharingLocalStreamStore} from "../Stores/ScreenSharingStore";
|
||||||
|
import {DivImportance, layoutManager} from "./LayoutManager";
|
||||||
|
import {HtmlUtils} from "./HtmlUtils";
|
||||||
|
|
||||||
export interface UserSimplePeerInterface{
|
export interface UserSimplePeerInterface{
|
||||||
userId: number;
|
userId: number;
|
||||||
@ -39,9 +42,9 @@ export class SimplePeer {
|
|||||||
|
|
||||||
private PeerScreenSharingConnectionArray: Map<number, ScreenSharingPeer> = new Map<number, ScreenSharingPeer>();
|
private PeerScreenSharingConnectionArray: Map<number, ScreenSharingPeer> = new Map<number, ScreenSharingPeer>();
|
||||||
private PeerConnectionArray: Map<number, VideoPeer> = new Map<number, VideoPeer>();
|
private PeerConnectionArray: Map<number, VideoPeer> = new Map<number, VideoPeer>();
|
||||||
private readonly sendLocalVideoStreamCallback: UpdatedLocalStreamCallback;
|
|
||||||
private readonly sendLocalScreenSharingStreamCallback: StartScreenSharingCallback;
|
private readonly sendLocalScreenSharingStreamCallback: StartScreenSharingCallback;
|
||||||
private readonly stopLocalScreenSharingStreamCallback: StopScreenSharingCallback;
|
private readonly stopLocalScreenSharingStreamCallback: StopScreenSharingCallback;
|
||||||
|
private readonly unsubscribers: (() => void)[] = [];
|
||||||
private readonly peerConnectionListeners: Array<PeerConnectionListener> = new Array<PeerConnectionListener>();
|
private readonly peerConnectionListeners: Array<PeerConnectionListener> = new Array<PeerConnectionListener>();
|
||||||
private readonly userId: number;
|
private readonly userId: number;
|
||||||
private lastWebrtcUserName: string|undefined;
|
private lastWebrtcUserName: string|undefined;
|
||||||
@ -49,13 +52,32 @@ export class SimplePeer {
|
|||||||
|
|
||||||
constructor(private Connection: RoomConnection, private enableReporting: boolean, private myName: string) {
|
constructor(private Connection: RoomConnection, private enableReporting: boolean, private myName: string) {
|
||||||
// We need to go through this weird bound function pointer in order to be able to "free" this reference later.
|
// We need to go through this weird bound function pointer in order to be able to "free" this reference later.
|
||||||
this.sendLocalVideoStreamCallback = this.sendLocalVideoStream.bind(this);
|
|
||||||
this.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this);
|
this.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this);
|
||||||
this.stopLocalScreenSharingStreamCallback = this.stopLocalScreenSharingStream.bind(this);
|
this.stopLocalScreenSharingStreamCallback = this.stopLocalScreenSharingStream.bind(this);
|
||||||
|
|
||||||
mediaManager.onUpdateLocalStream(this.sendLocalVideoStreamCallback);
|
this.unsubscribers.push(localStreamStore.subscribe((streamResult) => {
|
||||||
mediaManager.onStartScreenSharing(this.sendLocalScreenSharingStreamCallback);
|
this.sendLocalVideoStream(streamResult);
|
||||||
mediaManager.onStopScreenSharing(this.stopLocalScreenSharingStreamCallback);
|
}));
|
||||||
|
|
||||||
|
let localScreenCapture: MediaStream|null = null;
|
||||||
|
|
||||||
|
this.unsubscribers.push(screenSharingLocalStreamStore.subscribe((streamResult) => {
|
||||||
|
if (streamResult.type === 'error') {
|
||||||
|
// Let's ignore screen sharing errors, we will deal with those in a different way.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamResult.stream !== null) {
|
||||||
|
localScreenCapture = streamResult.stream;
|
||||||
|
this.sendLocalScreenSharingStream(localScreenCapture);
|
||||||
|
} else {
|
||||||
|
if (localScreenCapture) {
|
||||||
|
this.stopLocalScreenSharingStream(localScreenCapture);
|
||||||
|
localScreenCapture = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
this.userId = Connection.getUserId();
|
this.userId = Connection.getUserId();
|
||||||
this.initialise();
|
this.initialise();
|
||||||
}
|
}
|
||||||
@ -106,13 +128,19 @@ export class SimplePeer {
|
|||||||
if(!user.initiator){
|
if(!user.initiator){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.createPeerConnection(user);
|
const streamResult = get(localStreamStore);
|
||||||
|
let stream : MediaStream | null = null;
|
||||||
|
if (streamResult.type === 'success' && streamResult.stream) {
|
||||||
|
stream = streamResult.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.createPeerConnection(user, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create peer connection to bind users
|
* create peer connection to bind users
|
||||||
*/
|
*/
|
||||||
private createPeerConnection(user : UserSimplePeerInterface) : VideoPeer | null {
|
private createPeerConnection(user : UserSimplePeerInterface, localStream: MediaStream | null) : VideoPeer | null {
|
||||||
const peerConnection = this.PeerConnectionArray.get(user.userId)
|
const peerConnection = this.PeerConnectionArray.get(user.userId)
|
||||||
if (peerConnection) {
|
if (peerConnection) {
|
||||||
if (peerConnection.destroyed) {
|
if (peerConnection.destroyed) {
|
||||||
@ -122,11 +150,11 @@ export class SimplePeer {
|
|||||||
if (!peerConnexionDeleted) {
|
if (!peerConnexionDeleted) {
|
||||||
throw 'Error to delete peer connection';
|
throw 'Error to delete peer connection';
|
||||||
}
|
}
|
||||||
this.createPeerConnection(user);
|
//return this.createPeerConnection(user, localStream);
|
||||||
} else {
|
} else {
|
||||||
peerConnection.toClose = false;
|
peerConnection.toClose = false;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = user.name;
|
let name = user.name;
|
||||||
@ -144,7 +172,7 @@ export class SimplePeer {
|
|||||||
this.lastWebrtcUserName = user.webRtcUser;
|
this.lastWebrtcUserName = user.webRtcUser;
|
||||||
this.lastWebrtcPassword = user.webRtcPassword;
|
this.lastWebrtcPassword = user.webRtcPassword;
|
||||||
|
|
||||||
const peer = new VideoPeer(user, user.initiator ? user.initiator : false, this.Connection);
|
const peer = new VideoPeer(user, user.initiator ? user.initiator : false, this.Connection, localStream);
|
||||||
|
|
||||||
//permit to send message
|
//permit to send message
|
||||||
mediaManager.addSendMessageCallback(user.userId,(message: string) => {
|
mediaManager.addSendMessageCallback(user.userId,(message: string) => {
|
||||||
@ -155,8 +183,9 @@ export class SimplePeer {
|
|||||||
// When a connection is established to a video stream, and if a screen sharing is taking place,
|
// When a connection is established to a video stream, and if a screen sharing is taking place,
|
||||||
// the user sharing screen should also initiate a connection to the remote user!
|
// the user sharing screen should also initiate a connection to the remote user!
|
||||||
peer.on('connect', () => {
|
peer.on('connect', () => {
|
||||||
if (mediaManager.localScreenCapture) {
|
const streamResult = get(screenSharingLocalStreamStore);
|
||||||
this.sendLocalScreenSharingStreamToUser(user.userId);
|
if (streamResult.type === 'success' && streamResult.stream !== null) {
|
||||||
|
this.sendLocalScreenSharingStreamToUser(user.userId, streamResult.stream);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -175,7 +204,7 @@ export class SimplePeer {
|
|||||||
/**
|
/**
|
||||||
* create peer connection to bind users
|
* create peer connection to bind users
|
||||||
*/
|
*/
|
||||||
private createPeerScreenSharingConnection(user : UserSimplePeerInterface) : ScreenSharingPeer | null{
|
private createPeerScreenSharingConnection(user : UserSimplePeerInterface, stream: MediaStream | null) : ScreenSharingPeer | null{
|
||||||
const peerConnection = this.PeerScreenSharingConnectionArray.get(user.userId);
|
const peerConnection = this.PeerScreenSharingConnectionArray.get(user.userId);
|
||||||
if(peerConnection){
|
if(peerConnection){
|
||||||
if(peerConnection.destroyed){
|
if(peerConnection.destroyed){
|
||||||
@ -185,7 +214,7 @@ export class SimplePeer {
|
|||||||
if(!peerConnexionDeleted){
|
if(!peerConnexionDeleted){
|
||||||
throw 'Error to delete peer connection';
|
throw 'Error to delete peer connection';
|
||||||
}
|
}
|
||||||
this.createPeerConnection(user);
|
this.createPeerConnection(user, stream);
|
||||||
}else {
|
}else {
|
||||||
peerConnection.toClose = false;
|
peerConnection.toClose = false;
|
||||||
}
|
}
|
||||||
@ -204,7 +233,7 @@ export class SimplePeer {
|
|||||||
user.webRtcPassword = this.lastWebrtcPassword;
|
user.webRtcPassword = this.lastWebrtcPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
const peer = new ScreenSharingPeer(user, user.initiator ? user.initiator : false, this.Connection);
|
const peer = new ScreenSharingPeer(user, user.initiator ? user.initiator : false, this.Connection, stream);
|
||||||
this.PeerScreenSharingConnectionArray.set(user.userId, peer);
|
this.PeerScreenSharingConnectionArray.set(user.userId, peer);
|
||||||
|
|
||||||
for (const peerConnectionListener of this.peerConnectionListeners) {
|
for (const peerConnectionListener of this.peerConnectionListeners) {
|
||||||
@ -294,7 +323,9 @@ export class SimplePeer {
|
|||||||
* Unregisters any held event handler.
|
* Unregisters any held event handler.
|
||||||
*/
|
*/
|
||||||
public unregister() {
|
public unregister() {
|
||||||
mediaManager.removeUpdateLocalStreamEventListener(this.sendLocalVideoStreamCallback);
|
for (const unsubscriber of this.unsubscribers) {
|
||||||
|
unsubscriber();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@ -302,7 +333,13 @@ export class SimplePeer {
|
|||||||
try {
|
try {
|
||||||
//if offer type, create peer connection
|
//if offer type, create peer connection
|
||||||
if(data.signal.type === "offer"){
|
if(data.signal.type === "offer"){
|
||||||
this.createPeerConnection(data);
|
const streamResult = get(localStreamStore);
|
||||||
|
let stream : MediaStream | null = null;
|
||||||
|
if (streamResult.type === 'success' && streamResult.stream) {
|
||||||
|
stream = streamResult.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.createPeerConnection(data, stream);
|
||||||
}
|
}
|
||||||
const peer = this.PeerConnectionArray.get(data.userId);
|
const peer = this.PeerConnectionArray.get(data.userId);
|
||||||
if (peer !== undefined) {
|
if (peer !== undefined) {
|
||||||
@ -318,18 +355,26 @@ export class SimplePeer {
|
|||||||
private receiveWebrtcScreenSharingSignal(data: WebRtcSignalReceivedMessageInterface) {
|
private receiveWebrtcScreenSharingSignal(data: WebRtcSignalReceivedMessageInterface) {
|
||||||
if (blackListManager.isBlackListed(data.userId)) return;
|
if (blackListManager.isBlackListed(data.userId)) return;
|
||||||
console.log("receiveWebrtcScreenSharingSignal", data);
|
console.log("receiveWebrtcScreenSharingSignal", data);
|
||||||
|
const streamResult = get(screenSharingLocalStreamStore);
|
||||||
|
let stream : MediaStream | null = null;
|
||||||
|
if (streamResult.type === 'success' && streamResult.stream !== null) {
|
||||||
|
stream = streamResult.stream;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//if offer type, create peer connection
|
//if offer type, create peer connection
|
||||||
if(data.signal.type === "offer"){
|
if(data.signal.type === "offer"){
|
||||||
this.createPeerScreenSharingConnection(data);
|
this.createPeerScreenSharingConnection(data, stream);
|
||||||
}
|
}
|
||||||
const peer = this.PeerScreenSharingConnectionArray.get(data.userId);
|
const peer = this.PeerScreenSharingConnectionArray.get(data.userId);
|
||||||
if (peer !== undefined) {
|
if (peer !== undefined) {
|
||||||
peer.signal(data.signal);
|
peer.signal(data.signal);
|
||||||
} else {
|
} else {
|
||||||
console.error('Could not find peer whose ID is "'+data.userId+'" in receiveWebrtcScreenSharingSignal');
|
console.error('Could not find peer whose ID is "'+data.userId+'" in receiveWebrtcScreenSharingSignal');
|
||||||
console.info('tentative to create new peer connexion');
|
console.info('Attempt to create new peer connexion');
|
||||||
this.sendLocalScreenSharingStreamToUser(data.userId);
|
if (stream) {
|
||||||
|
this.sendLocalScreenSharingStreamToUser(data.userId, stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`receiveWebrtcSignal => ${data.userId}`, e);
|
console.error(`receiveWebrtcSignal => ${data.userId}`, e);
|
||||||
@ -339,21 +384,19 @@ export class SimplePeer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private pushVideoToRemoteUser(userId : number) {
|
private pushVideoToRemoteUser(userId: number, streamResult: LocalStreamStoreValue) {
|
||||||
try {
|
try {
|
||||||
const PeerConnection = this.PeerConnectionArray.get(userId);
|
const PeerConnection = this.PeerConnectionArray.get(userId);
|
||||||
if (!PeerConnection) {
|
if (!PeerConnection) {
|
||||||
throw new Error('While adding media, cannot find user with ID ' + userId);
|
throw new Error('While adding media, cannot find user with ID ' + userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = get(localStreamStore);
|
PeerConnection.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...streamResult.constraints})));
|
||||||
|
|
||||||
PeerConnection.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...result.constraints})));
|
if (streamResult.type === 'error') {
|
||||||
|
|
||||||
if (result.type === 'error') {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const localStream: MediaStream | null = result.stream;
|
const localStream: MediaStream | null = streamResult.stream;
|
||||||
|
|
||||||
if(!localStream){
|
if(!localStream){
|
||||||
return;
|
return;
|
||||||
@ -370,15 +413,11 @@ export class SimplePeer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private pushScreenSharingToRemoteUser(userId : number) {
|
private pushScreenSharingToRemoteUser(userId: number, localScreenCapture: MediaStream) {
|
||||||
const PeerConnection = this.PeerScreenSharingConnectionArray.get(userId);
|
const PeerConnection = this.PeerScreenSharingConnectionArray.get(userId);
|
||||||
if (!PeerConnection) {
|
if (!PeerConnection) {
|
||||||
throw new Error('While pushing screen sharing, cannot find user with ID ' + userId);
|
throw new Error('While pushing screen sharing, cannot find user with ID ' + userId);
|
||||||
}
|
}
|
||||||
const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture;
|
|
||||||
if(!localScreenCapture){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const track of localScreenCapture.getTracks()) {
|
for (const track of localScreenCapture.getTracks()) {
|
||||||
PeerConnection.addTrack(track, localScreenCapture);
|
PeerConnection.addTrack(track, localScreenCapture);
|
||||||
@ -386,23 +425,18 @@ export class SimplePeer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendLocalVideoStream(){
|
public sendLocalVideoStream(streamResult: LocalStreamStoreValue){
|
||||||
for (const user of this.Users) {
|
for (const user of this.Users) {
|
||||||
this.pushVideoToRemoteUser(user.userId);
|
this.pushVideoToRemoteUser(user.userId, streamResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered locally when clicking on the screen sharing button
|
* Triggered locally when clicking on the screen sharing button
|
||||||
*/
|
*/
|
||||||
public sendLocalScreenSharingStream() {
|
public sendLocalScreenSharingStream(localScreenCapture: MediaStream) {
|
||||||
if (!mediaManager.localScreenCapture) {
|
|
||||||
console.error('Could not find localScreenCapture to share')
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const user of this.Users) {
|
for (const user of this.Users) {
|
||||||
this.sendLocalScreenSharingStreamToUser(user.userId);
|
this.sendLocalScreenSharingStreamToUser(user.userId, localScreenCapture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,11 +449,11 @@ export class SimplePeer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendLocalScreenSharingStreamToUser(userId: number): void {
|
private sendLocalScreenSharingStreamToUser(userId: number, localScreenCapture: MediaStream): void {
|
||||||
if (blackListManager.isBlackListed(userId)) return;
|
if (blackListManager.isBlackListed(userId)) return;
|
||||||
// If a connection already exists with user (because it is already sharing a screen with us... let's use this connection)
|
// If a connection already exists with user (because it is already sharing a screen with us... let's use this connection)
|
||||||
if (this.PeerScreenSharingConnectionArray.has(userId)) {
|
if (this.PeerScreenSharingConnectionArray.has(userId)) {
|
||||||
this.pushScreenSharingToRemoteUser(userId);
|
this.pushScreenSharingToRemoteUser(userId, localScreenCapture);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,7 +461,7 @@ export class SimplePeer {
|
|||||||
userId,
|
userId,
|
||||||
initiator: true
|
initiator: true
|
||||||
};
|
};
|
||||||
const PeerConnectionScreenSharing = this.createPeerScreenSharingConnection(screenSharingUser);
|
const PeerConnectionScreenSharing = this.createPeerScreenSharingConnection(screenSharingUser, localScreenCapture);
|
||||||
if (!PeerConnectionScreenSharing) {
|
if (!PeerConnectionScreenSharing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ export class VideoPeer extends Peer {
|
|||||||
private onBlockSubscribe: Subscription;
|
private onBlockSubscribe: Subscription;
|
||||||
private onUnBlockSubscribe: Subscription;
|
private onUnBlockSubscribe: Subscription;
|
||||||
|
|
||||||
constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) {
|
constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection, localStream: MediaStream | null) {
|
||||||
super({
|
super({
|
||||||
initiator: initiator ? initiator : false,
|
initiator: initiator ? initiator : false,
|
||||||
//reconnectTimer: 10000,
|
//reconnectTimer: 10000,
|
||||||
@ -107,7 +107,7 @@ export class VideoPeer extends Peer {
|
|||||||
this._onFinish();
|
this._onFinish();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.pushVideoToRemoteUser();
|
this.pushVideoToRemoteUser(localStream);
|
||||||
this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userId) => {
|
this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userId) => {
|
||||||
if (userId === this.userId) {
|
if (userId === this.userId) {
|
||||||
this.toggleRemoteStream(false);
|
this.toggleRemoteStream(false);
|
||||||
@ -190,9 +190,8 @@ export class VideoPeer extends Peer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private pushVideoToRemoteUser() {
|
private pushVideoToRemoteUser(localStream: MediaStream | null) {
|
||||||
try {
|
try {
|
||||||
const localStream: MediaStream | null = mediaManager.localStream;
|
|
||||||
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...get(obtainedMediaConstraintStore)})));
|
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...get(obtainedMediaConstraintStore)})));
|
||||||
|
|
||||||
if(!localStream){
|
if(!localStream){
|
||||||
|
@ -9,7 +9,7 @@ import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";
|
|||||||
import {SelectCompanionScene} from "./Phaser/Login/SelectCompanionScene";
|
import {SelectCompanionScene} from "./Phaser/Login/SelectCompanionScene";
|
||||||
import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene";
|
import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene";
|
||||||
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
|
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
|
||||||
import {ResizableScene} from "./Phaser/Login/ResizableScene";
|
import WebFontLoaderPlugin from 'phaser3-rex-plugins/plugins/webfontloader-plugin.js';
|
||||||
import {EntryScene} from "./Phaser/Login/EntryScene";
|
import {EntryScene} from "./Phaser/Login/EntryScene";
|
||||||
import {coWebsiteManager} from "./WebRtc/CoWebsiteManager";
|
import {coWebsiteManager} from "./WebRtc/CoWebsiteManager";
|
||||||
import {MenuScene} from "./Phaser/Menu/MenuScene";
|
import {MenuScene} from "./Phaser/Menu/MenuScene";
|
||||||
@ -107,6 +107,13 @@ const config: GameConfig = {
|
|||||||
roundPixels: true,
|
roundPixels: true,
|
||||||
antialias: false
|
antialias: false
|
||||||
},
|
},
|
||||||
|
plugins: {
|
||||||
|
global: [{
|
||||||
|
key: 'rexWebFontLoader',
|
||||||
|
plugin: WebFontLoaderPlugin,
|
||||||
|
start: true
|
||||||
|
}]
|
||||||
|
},
|
||||||
physics: {
|
physics: {
|
||||||
default: "arcade",
|
default: "arcade",
|
||||||
arcade: {
|
arcade: {
|
||||||
|
4
front/src/rex-plugins.d.ts
vendored
@ -7,6 +7,10 @@ declare module 'phaser3-rex-plugins/plugins/gestures-plugin.js' {
|
|||||||
const content: any; // eslint-disable-line
|
const content: any; // eslint-disable-line
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
declare module 'phaser3-rex-plugins/plugins/webfontloader-plugin.js' {
|
||||||
|
const content: any; // eslint-disable-line
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
declare module 'phaser3-rex-plugins/plugins/gestures.js' {
|
declare module 'phaser3-rex-plugins/plugins/gestures.js' {
|
||||||
export const Pinch: any; // eslint-disable-line
|
export const Pinch: any; // eslint-disable-line
|
||||||
}
|
}
|
||||||
|
1
front/style/fonts.scss
Normal file
@ -0,0 +1 @@
|
|||||||
|
@import "~@fontsource/press-start-2p/index.css";
|
@ -1,4 +1,5 @@
|
|||||||
@import "cowebsite.scss";
|
@import "cowebsite.scss";
|
||||||
@import "cowebsite-mobile.scss";
|
@import "cowebsite-mobile.scss";
|
||||||
@import "style.css";
|
@import "style.css";
|
||||||
@import "mobile-style.scss";
|
@import "mobile-style.scss";
|
||||||
|
@import "fonts.scss";
|
@ -1,9 +1,24 @@
|
|||||||
|
@media (hover: none) {
|
||||||
|
/**
|
||||||
|
* If we cannot hover over elements, let's display camera button in full.
|
||||||
|
*/
|
||||||
|
.btn-cam-action {
|
||||||
|
div {
|
||||||
|
transform: translateY(0px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 700px),
|
@media screen and (max-width: 700px),
|
||||||
screen and (max-height: 700px){
|
screen and (max-height: 700px){
|
||||||
video#myCamVideo {
|
video.myCamVideo {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.div-myCamVideo.hide {
|
||||||
|
right: -160px;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
@ -22,21 +37,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-cam-action {
|
|
||||||
min-width: 150px;
|
|
||||||
|
|
||||||
&:hover{
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
margin: 0 1%;
|
|
||||||
&:hover {
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-section {
|
.main-section {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -133,11 +133,11 @@ body .message-info.warning{
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-container#div-myCamVideo{
|
.video-container.div-myCamVideo{
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#div-myCamVideo {
|
.div-myCamVideo {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
bottom: 30px;
|
bottom: 30px;
|
||||||
@ -146,11 +146,11 @@ body .message-info.warning{
|
|||||||
transition: right 350ms;
|
transition: right 350ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
#div-myCamVideo.hide {
|
.div-myCamVideo.hide {
|
||||||
right: -20vw;
|
right: -20vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
video#myCamVideo{
|
video.myCamVideo{
|
||||||
width: 15vw;
|
width: 15vw;
|
||||||
-webkit-transform: scaleX(-1);
|
-webkit-transform: scaleX(-1);
|
||||||
transform: scaleX(-1);
|
transform: scaleX(-1);
|
||||||
@ -212,7 +212,9 @@ video#myCamVideo{
|
|||||||
/*btn animation*/
|
/*btn animation*/
|
||||||
.btn-cam-action div{
|
.btn-cam-action div{
|
||||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||||
/*position: absolute;*/
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
border: solid 0px black;
|
border: solid 0px black;
|
||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
@ -267,8 +269,6 @@ video#myCamVideo{
|
|||||||
.btn-cam-action div img{
|
.btn-cam-action div img{
|
||||||
height: 22px;
|
height: 22px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
top: calc(48px - 37px);
|
|
||||||
left: calc(48px - 41px);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,14 @@ describe("isUserNameValid()", () => {
|
|||||||
it("should not validate spaces", () => {
|
it("should not validate spaces", () => {
|
||||||
expect(isUserNameValid(' ')).toBe(false);
|
expect(isUserNameValid(' ')).toBe(false);
|
||||||
});
|
});
|
||||||
it("should not validate special characters", () => {
|
it("should validate special characters", () => {
|
||||||
expect(isUserNameValid('a&-')).toBe(false);
|
expect(isUserNameValid('%&-')).toBe(true);
|
||||||
|
});
|
||||||
|
it("should validate accents", () => {
|
||||||
|
expect(isUserNameValid('éàëè')).toBe(true);
|
||||||
|
});
|
||||||
|
it("should validate chinese characters", () => {
|
||||||
|
expect(isUserNameValid('中文鍵盤')).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -102,9 +102,17 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(ttf|eot|svg|png|gif|jpg)$/,
|
test: /\.(eot|svg|png|gif|jpg)$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
type: 'asset'
|
type: 'asset'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(woff(2)?|ttf)$/,
|
||||||
|
type: 'asset',
|
||||||
|
generator: {
|
||||||
|
filename: 'fonts/[name][ext]'
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -50,6 +50,11 @@
|
|||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
|
"@fontsource/press-start-2p@^4.3.0":
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@fontsource/press-start-2p/-/press-start-2p-4.3.0.tgz#37124387f7fbfe7792b5fc9a1906b80d9aeda4c6"
|
||||||
|
integrity sha512-gmS4070EoZp5/6NUJ+tBnvtDiSmFcR+S+ClAOJ8NGFXDWOkO12yMnyGJEJaDCNCAMX0s2TQCcmr6qWKx5ad3RQ==
|
||||||
|
|
||||||
"@nodelib/fs.scandir@2.1.4":
|
"@nodelib/fs.scandir@2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
|
||||||
|