Merge branch 'develop' of github.com:thecodingmachine/workadventure

This commit is contained in:
_Bastler
2021-11-16 10:56:33 +01:00
49 changed files with 755 additions and 489 deletions
+1 -1
View File
@@ -273,7 +273,7 @@ class IframeListener {
registerScript(scriptUrl: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
console.log("Loading map related script at ", scriptUrl);
console.info("Loading map related script at ", scriptUrl);
if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
// Using external iframe mode (
+11 -11
View File
@@ -1,6 +1,6 @@
<script lang="typescript">
import { requestedScreenSharingState, screenSharingAvailableStore } from "../Stores/ScreenSharingStore";
import { isSilentStore, requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore";
import {requestedScreenSharingState, screenSharingAvailableStore} from "../Stores/ScreenSharingStore";
import {isSilentStore, requestedCameraState, requestedMicrophoneState} from "../Stores/MediaStore";
import monitorImg from "./images/monitor.svg";
import monitorCloseImg from "./images/monitor-close.svg";
import cinemaImg from "./images/cinema.svg";
@@ -9,10 +9,10 @@
import microphoneCloseImg from "./images/microphone-close.svg";
import layoutPresentationImg from "./images/layout-presentation.svg";
import layoutChatImg from "./images/layout-chat.svg";
import { layoutModeStore } from "../Stores/StreamableCollectionStore";
import { LayoutMode } from "../WebRtc/LayoutManager";
import { peerStore } from "../Stores/PeerStore";
import { onDestroy } from "svelte";
import {layoutModeStore} from "../Stores/StreamableCollectionStore";
import {LayoutMode} from "../WebRtc/LayoutManager";
import {peerStore} from "../Stores/PeerStore";
import {onDestroy} from "svelte";
function screenSharingClick(): void {
if (isSilent) return;
@@ -60,9 +60,9 @@
<div class="btn-cam-action">
<div class="btn-layout nes-btn is-dark" on:click={switchLayoutMode} class:hide={$peerStore.size===0}>
{#if $layoutModeStore === LayoutMode.Presentation }
<img src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode">
<img src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode">
{:else}
<img src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode">
<img src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode">
{/if}
</div>
<div class="btn-micro nes-btn is-dark" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState ||
@@ -77,9 +77,9 @@
<div class="btn-video nes-btn is-dark" on:click={cameraClick} class:disabled={!$requestedCameraState ||
isSilent}>
{#if $requestedCameraState && !isSilent}
<img src={cinemaImg} alt="Turn on webcam">
<img src={cinemaImg} alt="Turn on webcam">
{:else}
<img src={cinemaCloseImg} alt="Turn off webcam">
<img src={cinemaCloseImg} alt="Turn off webcam">
{/if}
</div>
<div class="btn-monitor nes-btn is-dark" on:click={screenSharingClick} class:hide={!$screenSharingAvailableStore
@@ -91,4 +91,4 @@
{/if}
</div>
</div>
</div>
</div>
@@ -39,7 +39,7 @@
})
function onKeyDown(e: KeyboardEvent) {
function onKeyDown(e:KeyboardEvent) {
if (e.key === 'Escape') {
emoteMenuStore.closeEmoteMenu();
}
@@ -55,7 +55,7 @@
</script>
<svelte:window on:keydown={onKeyDown} />
<svelte:window on:keydown={onKeyDown}/>
<div class="emote-menu-container">
<div class="emote-menu" bind:this={emojiContainer}></div>
@@ -73,4 +73,4 @@
.emote-menu {
pointer-events: all;
}
</style>
</style>
@@ -1,10 +1,60 @@
<script lang="ts">
function goToGettingStarted() {
const sparkHost = "https://workadventu.re/getting-started";
window.open(sparkHost, "_blank");
}
function goToBuildingMap() {
const sparkHost = "https://workadventu.re/map-building/";
window.open(sparkHost, "_blank");
}
import {contactPageStore} from "../../Stores/MenuStore";
</script>
<iframe title="contact" src="{$contactPageStore}" allow="clipboard-read; clipboard-write self {$contactPageStore}" allowfullscreen></iframe>
<div class="create-map-main">
<section class="container-overflow">
<section>
<h3>Getting started</h3>
<p>
WorkAdventure allows you to create an online space to communicate spontaneously with others.
And it all starts with creating your own space. Choose from a large selection of prefabricated maps by our team.
</p>
<button type="button" class="nes-btn is-primary" on:click={goToGettingStarted}>Getting started</button>
</section>
<section>
<h3>Create your map</h3>
<p>You can also create your own custom map by following the step of the documentation.</p>
<button type="button" class="nes-btn" on:click={goToBuildingMap}>Create your map</button>
</section>
<iframe title="contact"
src="{$contactPageStore}"
allow="clipboard-read; clipboard-write self {$contactPageStore}"
allowfullscreen></iframe>
</section>
</div>
<style lang="scss">
div.create-map-main {
height: calc(100% - 56px);
text-align: center;
section {
margin-bottom: 50px;
}
section.container-overflow {
height: 100%;
margin: 0;
padding: 0;
overflow: auto;
}
}
iframe {
border: none;
height: calc(100% - 56px);
@@ -1,38 +0,0 @@
<script lang="ts">
function goToBuildingMap() {
const sparkHost = "https://wiki.bstly.de/services/partey/map";
window.open(sparkHost, "_blank");
}
</script>
<div class="create-map-main">
<section class="container-overflow">
<section>
<h3>Create your map</h3>
<p>You can also create your own custom map by following the step of the documentation.</p>
<button type="button" class="nes-btn" on:click={goToBuildingMap}>Create your map</button>
</section>
</section>
</div>
<style lang="scss">
div.create-map-main {
height: calc(100% - 56px);
text-align: center;
section {
margin-bottom: 50px;
}
section.container-overflow {
height: 100%;
margin: 0;
padding: 0;
overflow: auto;
}
}
</style>
@@ -0,0 +1,75 @@
<script lang="ts">
let HTMLShareLink: HTMLInputElement;
function copyLink() {
HTMLShareLink.select();
document.execCommand('copy');
}
async function shareLink() {
const shareData = {url: location.toString()};
try {
await navigator.share(shareData);
} catch (err) {
console.error('Error: ' + err);
copyLink();
}
}
</script>
<div class="guest-main">
<section class="container-overflow">
<section class="share-url not-mobile">
<h3>Share the link of the room !</h3>
<input type="text" readonly bind:this={HTMLShareLink} value={location.toString()}>
<button type="button" class="nes-btn is-primary" on:click={copyLink}>Copy</button>
</section>
<section class="is-mobile">
<h3>Share the link of the room !</h3>
<input type="hidden" readonly bind:this={HTMLShareLink} value={location.toString()}>
<button type="button" class="nes-btn is-primary" on:click={shareLink}>Share</button>
</section>
</section>
</div>
<style lang="scss">
div.guest-main {
height: calc(100% - 56px);
text-align: center;
section {
margin-bottom: 50px;
}
section.container-overflow {
height: 100%;
margin: 0;
padding: 0;
overflow: auto;
}
section.is-mobile {
display: none;
}
}
@media only screen and (max-width: 900px), only screen and (max-height: 600px) {
div.guest-main {
section.share-url.not-mobile {
display: none;
}
section.is-mobile {
display: block;
text-align: center;
margin-bottom: 20px;
}
section.container-overflow {
height: calc(100% - 120px);
}
}
}
</style>
+7 -7
View File
@@ -3,11 +3,11 @@
import SettingsSubMenu from "./SettingsSubMenu.svelte";
import ProfileSubMenu from "./ProfileSubMenu.svelte";
import WorldsSubMenu from "./WorldsSubMenu.svelte";
import CreateMapSubMenu from "./CreateMapSubMenu.svelte";
import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte";
import GlobalMessageSubMenu from "./GlobalMessagesSubMenu.svelte";
import ContactSubMenu from "./ContactSubMenu.svelte";
import CustomSubMenu from "./CustomSubMenu.svelte"
import GuestSubMenu from "./GuestSubMenu.svelte";
import {
checkSubMenuToShow,
customMenuIframe,
@@ -20,21 +20,21 @@
import type {Unsubscriber} from "svelte/store";
import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem";
let activeSubMenu: string = SubMenusInterface.settings;
let activeComponent: typeof SettingsSubMenu | typeof CustomSubMenu = SettingsSubMenu;
let activeSubMenu: string = SubMenusInterface.profile;
let activeComponent: typeof ProfileSubMenu | typeof CustomSubMenu = ProfileSubMenu;
let props: { url: string, allowApi: boolean };
let unsubscriberSubMenuStore: Unsubscriber;
onMount(() => {
unsubscriberSubMenuStore = subMenusStore.subscribe(() => {
if(!get(subMenusStore).includes(activeSubMenu)) {
switchMenu(SubMenusInterface.settings);
switchMenu(SubMenusInterface.profile);
}
})
checkSubMenuToShow();
switchMenu(SubMenusInterface.settings);
switchMenu(SubMenusInterface.profile);
})
onDestroy(() => {
@@ -56,8 +56,8 @@
case SubMenusInterface.worlds:
activeComponent = WorldsSubMenu;
break;
case SubMenusInterface.createMap:
activeComponent = CreateMapSubMenu;
case SubMenusInterface.invite:
activeComponent = GuestSubMenu;
break;
case SubMenusInterface.aboutRoom:
activeComponent = AboutRoomSubMenu;
+2 -4
View File
@@ -30,7 +30,6 @@
z-index: 90;
position: relative;
margin: 25px;
img {
pointer-events: auto;
width: 24px;
@@ -38,8 +37,7 @@
margin: 3px
}
}
.menuIcon img:hover {
.menuIcon img:hover{
transform: scale(1.2);
}
@@ -53,4 +51,4 @@
}
}
}
</style>
</style>
+126 -62
View File
@@ -1,104 +1,168 @@
<script lang="typescript">
import { gameManager } from "../../Phaser/Game/GameManager";
import { SelectCompanionScene, SelectCompanionSceneName } from "../../Phaser/Login/SelectCompanionScene";
import { menuIconVisiblilityStore, menuVisiblilityStore, userIsConnected } from "../../Stores/MenuStore";
import { selectCompanionSceneVisibleStore } from "../../Stores/SelectCompanionStore";
import { loginSceneVisibleStore } from "../../Stores/LoginSceneStore";
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
import { connectionManager } from "../../Connexion/ConnectionManager";
import { PROFILE_URL } from "../../Enum/EnvironmentVariable";
import { localUserStore } from "../../Connexion/LocalUserStore";
import { EnableCameraScene, EnableCameraSceneName } from "../../Phaser/Login/EnableCameraScene";
import { enableCameraSceneVisibilityStore } from "../../Stores/MediaStore";
import {gameManager} from "../../Phaser/Game/GameManager";
import {SelectCompanionScene, SelectCompanionSceneName} from "../../Phaser/Login/SelectCompanionScene";
import {menuIconVisiblilityStore, menuVisiblilityStore, userIsConnected} from "../../Stores/MenuStore";
import {selectCompanionSceneVisibleStore} from "../../Stores/SelectCompanionStore";
import {LoginScene, LoginSceneName} from "../../Phaser/Login/LoginScene";
import {loginSceneVisibleStore} from "../../Stores/LoginSceneStore";
import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore";
import {SelectCharacterScene, SelectCharacterSceneName} from "../../Phaser/Login/SelectCharacterScene";
import {connectionManager} from "../../Connexion/ConnectionManager";
import {PROFILE_URL} from "../../Enum/EnvironmentVariable";
import {localUserStore} from "../../Connexion/LocalUserStore";
import {EnableCameraScene, EnableCameraSceneName} from "../../Phaser/Login/EnableCameraScene";
import {enableCameraSceneVisibilityStore} from "../../Stores/MediaStore";
import btnProfileSubMenuCamera from "../images/btn-menu-profile-camera.svg";
import btnProfileSubMenuIdentity from "../images/btn-menu-profile-identity.svg";
import btnProfileSubMenuCompanion from "../images/btn-menu-profile-companion.svg";
import btnProfileSubMenuWoka from "../images/btn-menu-profile-woka.svg";
function disableMenuStores() {
function disableMenuStores(){
menuVisiblilityStore.set(false);
menuIconVisiblilityStore.set(false);
}
function openEditCompanionScene() {
function openEditCompanionScene(){
disableMenuStores();
selectCompanionSceneVisibleStore.set(true);
gameManager.leaveGame(SelectCompanionSceneName, new SelectCompanionScene());
gameManager.leaveGame(SelectCompanionSceneName,new SelectCompanionScene());
}
function openEditSkinScene() {
function openEditNameScene(){
disableMenuStores();
loginSceneVisibleStore.set(true);
gameManager.leaveGame(LoginSceneName,new LoginScene());
}
function openEditSkinScene(){
disableMenuStores();
selectCharacterSceneVisibleStore.set(true);
gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene());
gameManager.leaveGame(SelectCharacterSceneName,new SelectCharacterScene());
}
function logOut() {
function logOut(){
disableMenuStores();
loginSceneVisibleStore.set(true);
connectionManager.logout();
}
function getProfileUrl() {
function getProfileUrl(){
return PROFILE_URL + `?token=${localUserStore.getAuthToken()}`;
}
function openEnableCameraScene() {
function openEnableCameraScene(){
disableMenuStores();
enableCameraSceneVisibilityStore.showEnableCameraScene();
gameManager.leaveGame(EnableCameraSceneName, new EnableCameraScene());
gameManager.leaveGame(EnableCameraSceneName,new EnableCameraScene());
}
</script>
<div class="customize-main">
<section>
<button type="button" class="nes-btn" on:click|preventDefault={openEditSkinScene}>Edit Skin</button>
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>Edit Companion</button>
</section>
<section>
<button type="button" class="nes-btn" on:click|preventDefault={openEnableCameraScene}>Setup camera</button>
</section>
{#if $userIsConnected}
<section>
{#if PROFILE_URL != undefined}
<iframe title="profile" src="{getProfileUrl()}"></iframe>
<div class="submenu">
<section>
<!--
<button type="button" class="nes-btn" on:click|preventDefault={openEditNameScene}>
<img src={btnProfileSubMenuIdentity} alt="Edit your name">
<span class="btn-hover">Edit your name</span>
</button>
-->
<button type="button" class="nes-btn" on:click|preventDefault={openEditSkinScene}>
<img src={btnProfileSubMenuWoka} alt="Edit your Avatar">
<span class="btn-hover">Edit your Avatar</span>
</button>
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>
<img src={btnProfileSubMenuCompanion} alt="Change your companion">
<span class="btn-hover">Change your companion</span>
</button>
<button type="button" class="nes-btn" on:click|preventDefault={openEnableCameraScene}>
<img src={btnProfileSubMenuCamera} alt="Edit your camera">
<span class="btn-hover">Edit your camera</span>
</button>
</section>
</div>
<div class="content">
{#if $userIsConnected}
<section>
{#if PROFILE_URL != undefined}
<iframe title="profile" src="{getProfileUrl()}"></iframe>
{/if}
</section>
<section>
<button type="button" class="nes-btn" on:click|preventDefault={logOut}>Log out</button>
</section>
{:else}
<section>
<a type="button" class="nes-btn" href="/login">Sign in</a>
</section>
{/if}
</section>
<section>
<button type="button" class="nes-btn" on:click|preventDefault={logOut}>Log out</button>
</section>
{:else}
<section>
<a type="button" class="nes-btn" href="/login">Sign in</a>
</section>
{/if}
</div>
</div>
<style lang="scss">
div.customize-main {
overflow-y: auto;
height: 90%;
div.customize-main{
width: 100%;
display: inline-flex;
section {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin-bottom: 20px;
div.submenu{
height: 100%;
width: 50px;
iframe {
width: 100%;
height: 50vh;
border: none;
}
button {
height: 50px;
width: 250px;
button {
transition: all .5s ease;
text-align: left;
white-space: nowrap;
margin-bottom: 10px;
max-height: 44px;
img {
height: 26px;
width: 26px;
cursor: pointer;
}
span.btn-hover{
display: none;
font-family: "Press Start 2P";
}
&:hover{
width: auto;
span.btn-hover {
display: initial;
}
}
}
}
div.content {
width: 100%;
section {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin-bottom: 20px;
iframe {
width: 100%;
height: 50vh;
border: none;
}
button {
height: 50px;
width: 250px;
}
}
}
}
@media only screen and (max-width: 800px) {
div.customize-main section button {
width: 130px;
}
div.customize-main.content section button {
width: 130px;
}
}
</style>
+7 -7
View File
@@ -1,11 +1,11 @@
<script lang="typescript">
import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
import { localStreamStore, isSilentStore } from "../Stores/MediaStore";
import {obtainedMediaConstraintStore} from "../Stores/MediaStore";
import {localStreamStore, isSilentStore} from "../Stores/MediaStore";
import SoundMeterWidget from "./SoundMeterWidget.svelte";
import { onDestroy } from "svelte";
import { srcObject } from "./Video/utils";
import {onDestroy} from "svelte";
import {srcObject} from "./Video/utils";
let stream: MediaStream | null;
let stream : MediaStream|null;
const unsubscribe = localStreamStore.subscribe(value => {
if (value.type === 'success') {
@@ -30,7 +30,7 @@
<div>
<div class="video-container nes-container is-rounded is-dark div-myCamVideo"
class:hide={!$obtainedMediaConstraintStore.video || isSilent}>
class:hide={!$obtainedMediaConstraintStore.video || isSilent}>
{#if $localStreamStore.type === "success" && $localStreamStore.stream}
<video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline></video>
<SoundMeterWidget stream={stream}></SoundMeterWidget>
@@ -39,4 +39,4 @@
<div class="is-silent" class:hide={isSilent}>
Silent zone
</div>
</div>
</div>
@@ -0,0 +1 @@
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><g id="btn_setup_camera" data-name="btn setup camera"><circle cx="24.71" cy="13.11" r="1.46"/><path d="M8.65,23.34H40.78V2.89H31.16L28.24,0h-7L18.27,2.89H8.65ZM32,8.73h2.92v2.92H32Zm-7.31,0a4.39,4.39,0,1,1-4.38,4.38A4.39,4.39,0,0,1,24.71,8.73Z"/><path d="M2.81,44H5.73v5.84h8.76V44h5.84V46.9h2.92V44h5.84V46.9H32V44h5.84V46.9h2.92V44h5.84V41.06h-33l-3.52-3.53L6.58,41.06H2.81Z"/><path d="M2.81,32.3H8.65v2.92h2.92V32.3h5.84v2.92h2.92V32.3h5.84v2.92h2.92V32.3h5.85v5.84H43.7V32.3h2.92V29.37H42.84l-3.52-3.52-3.53,3.52h-33Z"/></g></svg>

After

Width:  |  Height:  |  Size: 629 B

@@ -0,0 +1 @@
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50"><g id="btn_setup_companion" data-name="btn setup companion"><g id="iNhyGC.tif"><image id="Layer_0" data-name="Layer 0" width="15" height="30" transform="translate(13.21 0.25) scale(1.65)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAfCAYAAADupU20AAAACXBIWXMAAAbEAAAGxAGo1xHEAAAB20lEQVQ4T51UO04DMRScRYmEhGWlApRsBcUap4VUXICSjpqaY3ACSgpOgERQKnpoNjXRhjrkANZGikRhiuDH8w8BI7l5n/H4fVxYaxFC66FnnM1eiyjoC53QoPXQjkYnkS1HUnAFWg+tUhXatsVi8Q4AUKqClBJ1PU0q8RTMZq+Fk++CU4o4oiekbjHGhCbCVtbzS/xIwGuSQ/GbNgL5VkYEWg9tWQ4ghCCb60qKJGqjS5ZSkt0YkyWhGjjZYTIASClJUfg8r4hKVWiaOYwx0WmaOZSqEKIDfEu/PDvGHYCmmUeBSlW4PDvGTdt6o00Krq8uAIDkl+UASlUoy4Fnd3EOHXc7AOzu7QN4o/l3ie79G/+G3Kkojo50svI5hB3pAOnK5xDG0TLV9ZSMqe3jft4NKuLt/Qu6qzXG40m0fcYYjMcTdFdr3N6/eL4tYDOqhwc9AMDT4wNScPbDg563XFTEfr8P+3C6MZ4/RwTct1wuqYiFtTa5A3U9RXe1xsfONtUktRNUxMXi3Rua0egExhgi5MkcRFCWA3I6IgD0Xu7jJEQghKDE8BaX6OI4vE9VCEE38r3nqx7CW2c+9yFy00oE/x1lInDTlyPi3eCgP5F/VanPM+dPfut/wScEgxZt/KHdLgAAAABJRU5ErkJggg=="/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1 @@
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><g id="btn_setup_identity" data-name="btn setup identity"><path d="M31.94,6l-4.35,6.39a3.63,3.63,0,1,1-3.07-1.7,3.71,3.71,0,0,1,.67.06l4-5.89a16,16,0,0,0-9.37,0L17,.76a1.45,1.45,0,0,0-2.4,1.64L17.09,6A16,16,0,0,0,8.56,20.15V33.69a16,16,0,0,0,31.91,0V20.15A16,16,0,0,0,31.94,6Zm-.65,32.49H17.75a1.45,1.45,0,1,1,0-2.9H31.29a1.45,1.45,0,0,1,0,2.9Zm0-5.8H17.75a1.45,1.45,0,1,1,0-2.9H31.29a1.45,1.45,0,1,1,0,2.9Zm0-5.8H17.75a1.45,1.45,0,1,1,0-2.9H31.29a1.45,1.45,0,1,1,0,2.9Z"/><path d="M34.42,2.4A1.45,1.45,0,1,0,32,.76L29.21,4.89A16.38,16.38,0,0,1,31.94,6Z"/></g></svg>

After

Width:  |  Height:  |  Size: 661 B

@@ -0,0 +1 @@
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50"><g id="btn_setup_woka" data-name="btn setup woka"><g id="NP8bMB.tif"><image id="Layer_0" data-name="Layer 0" width="23" height="29" transform="translate(4.61 0.1) scale(1.71)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAdCAYAAABBsffGAAAACXBIWXMAAAZ1AAAGdQGEn07tAAACCUlEQVRIS61WS4rDMAx9abLoxuqi4MIYSk+Q+59iBnqCUlBhDNnYmy4aMosgj+3YyRTmQReVpSdZP6eZpglrMOajqsD8aGpnANDUyIW0bTsAwPF4DGfDMAAAxvEFoO6kSG7Mx9S2XSBUSi10vPcAZkfj+Co6WJDHxCXSHN77qoOuZJATE9FCxzkH4PdW1n4vdHbxH4k6Rom4JG/bblH8hBxIo64RC+RcKZUUXLAgF2wRC9b0AvlaP7+DmGcnAq1PdYs3oPUpOKim5T+wA+YJk1aS6XsXYmftd+j3EPnWnvgrYp4kLcyPZhxf8N6HIdmCcw7e+79PaDxta60mxKXpBLLdEk/oOL6wtmPinRLbxNEHciHu+34zohhan6CUAhHher0mDpK0xMQSrTEGAJIaSKqYGUqpsH77vsfX12fQ62aCOeo8YmMMDodDUHbOgYgSGTMnN5QFxvxoQrdcLhcA633+fD6rZ2InPADQSdREBGYOT1fuxFqL/X4Pa22QMXPy5HnvYYwJ0e+AOVc5oXSJcw7OOWitQUTQWgdZvmrFXvhCWpxzSaWHYQiFk6GSnxSQiAKhDGBc+I750cgWK60AIsLtdlukSbooRs5V/bQ4n8+TpCbveeltGaT7/V7cS9WVK4WVFADpzUQueiWs7nOlFIwxi/GvyXOskm+9o1vn1ZwD6XsYp6Qmz/ED0qpw9h1b2uQAAAAASUVORK5CYII="/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+62 -57
View File
@@ -42,20 +42,11 @@ class ConnectionManager {
const nonce = localUserStore.generateNonce();
localUserStore.setAuthToken(null);
//TODO fix me to redirect this URL by pusher
if (!this._currentRoom) {
if (!this._currentRoom || !this._currentRoom.iframeAuthentication) {
loginSceneVisibleIframeStore.set(false);
return null;
}
// also allow OIDC login without admin API by using pusher
let redirectUrl : URL;
if (this._currentRoom.iframeAuthentication) {
redirectUrl = new URL(`${this._currentRoom.iframeAuthentication}`);
} else {
// need origin if PUSHER_URL is relative (in Single-Domain-Deployment)
redirectUrl = new URL(`${PUSHER_URL}/login-screen`, (!PUSHER_URL.startsWith('http:') || !PUSHER_URL.startsWith('https:')) ? window.location.origin : undefined);
}
const redirectUrl = new URL(`${this._currentRoom.iframeAuthentication}`);
redirectUrl.searchParams.append("state", state);
redirectUrl.searchParams.append("nonce", nonce);
redirectUrl.searchParams.append("playUri", this._currentRoom.key);
@@ -88,6 +79,16 @@ class ConnectionManager {
const connexionType = urlManager.getGameConnexionType();
this.connexionType = connexionType;
this._currentRoom = null;
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get("token");
if (token) {
this.authToken = token;
localUserStore.setAuthToken(token);
//token was saved, clear url
urlParams.delete("token");
}
if (connexionType === GameConnexionTypes.login) {
this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
if (this.loadOpenIDScreen() !== null) {
@@ -96,15 +97,19 @@ class ConnectionManager {
urlManager.pushRoomIdToUrl(this._currentRoom);
} else if (connexionType === GameConnexionTypes.jwt) {
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get("code");
const state = urlParams.get("state");
if (!state || !localUserStore.verifyState(state)) {
throw "Could not validate state!";
if (!token) {
const code = urlParams.get("code");
const state = urlParams.get("state");
if (!state || !localUserStore.verifyState(state)) {
throw "Could not validate state!";
}
if (!code) {
throw "No Auth code provided";
}
localUserStore.setCode(code);
}
if (!code) {
throw "No Auth code provided";
}
localUserStore.setCode(code);
this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
try {
await this.checkAuthUserConnexion();
@@ -132,11 +137,11 @@ class ConnectionManager {
this._currentRoom = await Room.createRoom(
new URL(
window.location.protocol +
"//" +
window.location.host +
roomUrl +
window.location.search +
window.location.hash
"//" +
window.location.host +
roomUrl +
window.location.search +
window.location.hash
)
);
urlManager.pushRoomIdToUrl(this._currentRoom);
@@ -173,14 +178,20 @@ class ConnectionManager {
//before set token of user we must load room and all information. For example the mandatory authentication could be require on current room
this._currentRoom = await Room.createRoom(new URL(roomPath));
//defined last room url this room path
localUserStore.setLastRoomUrl(this._currentRoom.key);
//todo: add here some kind of warning if authToken has expired.
if (!this.authToken && !this._currentRoom.authenticationMandatory) {
await this.anonymousLogin();
} else {
try {
await this.checkAuthUserConnexion();
analyticsClient.loggedWithSso();
} catch (err) {
console.error(err);
this.loadOpenIDScreen();
return Promise.reject(new Error("You will be redirect on login page"));
}
}
this.localUser = localUserStore.getLocalUser() as LocalUser; //if authToken exist in localStorage then localUser cannot be null
@@ -208,23 +219,20 @@ class ConnectionManager {
analyticsClient.identifyUser(this.localUser.uuid, this.localUser.email);
}
//clean history with new URL
window.history.pushState({}, document.title, window.location.pathname);
this.serviceWorker = new _ServiceWorker();
return Promise.resolve(this._currentRoom);
}
public async anonymousLogin(isBenchmark: boolean = false): Promise<void> {
localUserStore.setAuthToken(null);
try {
const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data);
this.localUser = new LocalUser(data.userUuid, []);
this.authToken = data.authToken;
if (!isBenchmark) {
// In benchmark, we don't have a local storage.
localUserStore.saveUser(this.localUser);
localUserStore.setAuthToken(this.authToken);
}
} catch (error) {
this.loadOpenIDScreen();
const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data);
this.localUser = new LocalUser(data.userUuid, []);
this.authToken = data.authToken;
if (!isBenchmark) {
// In benchmark, we don't have a local storage.
localUserStore.saveUser(this.localUser);
localUserStore.setAuthToken(this.authToken);
}
}
@@ -261,9 +269,9 @@ class ConnectionManager {
reject(
new Error(
"An error occurred while connecting to socket server. Retrying. Code: " +
event.code +
", Reason: " +
event.reason
event.code +
", Reason: " +
event.reason
)
);
});
@@ -293,33 +301,30 @@ class ConnectionManager {
//set connected store for menu at false
userIsConnected.set(false);
const token = localUserStore.getAuthToken();
const state = localUserStore.getState();
const code = localUserStore.getCode();
if (!state || !localUserStore.verifyState(state)) {
throw "Could not validate state!";
}
if (!code) {
throw "No Auth code provided";
}
const nonce = localUserStore.getNonce();
const token = localUserStore.getAuthToken();
const { authToken, username, userUuid } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token } }).then(
(res) => res.data
);
this.localUser = new LocalUser(userUuid, []);
this.authToken = authToken;
if (!token) {
if (!state || !localUserStore.verifyState(state)) {
throw "Could not validate state!";
}
if (!code) {
throw "No Auth code provided";
}
}
const { authToken, userUuid, textures, email, username } = await Axios.get(`${PUSHER_URL}/login-callback`, {
params: { code, nonce, token },
}).then((res) => res.data);
localUserStore.setAuthToken(authToken);
this.localUser = new LocalUser(userUuid, textures, email);
localUserStore.saveUser(this.localUser);
localUserStore.setAuthToken(this.authToken);
this.authToken = authToken;
gameManager.setPlayerName(username);
//user connected, set connected store for menu at true
userIsConnected.set(true);
if (this.connexionType === GameConnexionTypes.anonymous) {
this.connexionType = GameConnexionTypes.empty
}
}
async getWorlds() {
+13 -13
View File
@@ -120,19 +120,12 @@ class LocalUserStore {
return localStorage.getItem(fullscreenKey) === "true";
}
setLastRoomUrl(roomUrl: string | null): void {
if (roomUrl) {
localStorage.setItem(lastRoomUrl, roomUrl.toString());
caches.open(cacheAPIIndex).then((cache) => {
const stringResponse = new Response(JSON.stringify({ roomUrl }));
cache.put(`/${lastRoomUrl}`, stringResponse);
});
} else {
localStorage.removeItem(lastRoomUrl);
caches.open(cacheAPIIndex).then((cache) => {
cache.delete(`/${lastRoomUrl}`);
});
}
setLastRoomUrl(roomUrl: string): void {
localStorage.setItem(lastRoomUrl, roomUrl.toString());
caches.open(cacheAPIIndex).then((cache) => {
const stringResponse = new Response(JSON.stringify({ roomUrl }));
cache.put(`/${lastRoomUrl}`, stringResponse);
});
}
getLastRoomUrl(): string {
return (
@@ -172,8 +165,15 @@ class LocalUserStore {
verifyState(value: string): boolean {
const oldValue = localStorage.getItem(state);
if (!oldValue) {
localStorage.setItem(state, value);
return true;
}
return oldValue === value;
}
setState(value: string) {
localStorage.setItem(state, value);
}
getState(): string | null {
return localStorage.getItem(state);
}
+5 -5
View File
@@ -1,5 +1,5 @@
import Axios from "axios";
import { CONTACT_URL, PUSHER_URL } from "../Enum/EnvironmentVariable";
import { CONTACT_URL, PUSHER_URL, DISABLE_ANONYMOUS, OPID_LOGIN_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable";
import type { CharacterTexture } from "./LocalUser";
import { localUserStore } from "./LocalUserStore";
@@ -14,8 +14,8 @@ export interface RoomRedirect {
export class Room {
public readonly id: string;
public readonly isPublic: boolean;
private _authenticationMandatory: boolean = false;
private _iframeAuthentication?: string;
private _authenticationMandatory: boolean = DISABLE_ANONYMOUS as boolean;
private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER;
private _mapUrl: string | undefined;
private _textures: CharacterTexture[] | undefined;
private instance: string | undefined;
@@ -106,8 +106,8 @@ export class Room {
this._mapUrl = data.mapUrl;
this._textures = data.textures;
this._group = data.group;
this._authenticationMandatory = data.authenticationMandatory || false;
this._iframeAuthentication = data.iframeAuthentication;
this._authenticationMandatory = data.authenticationMandatory || (DISABLE_ANONYMOUS as boolean);
this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER;
this._contactPage = data.contactPage || CONTACT_URL;
return new MapDetail(data.mapUrl, data.textures);
}
+2
View File
@@ -23,6 +23,8 @@ export const CONTACT_URL = process.env.CONTACT_URL || undefined;
export const PROFILE_URL = process.env.PROFILE_URL || undefined;
export const POSTHOG_API_KEY: string = (process.env.POSTHOG_API_KEY as string) || "";
export const POSTHOG_URL = process.env.POSTHOG_URL || undefined;
export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS || false;
export const OPID_LOGIN_SCREEN_PROVIDER = process.env.OPID_LOGIN_SCREEN_PROVIDER;
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;
+11 -11
View File
@@ -66,7 +66,7 @@ export abstract class Character extends Container {
this.playAnimation(direction, moving);
})
.catch(() => {
return lazyLoadPlayerCharacterTextures(scene.load, [ "color_22", "eyes_23" ]).then((textures) => {
return lazyLoadPlayerCharacterTextures(scene.load, ["color_22", "eyes_23"]).then((textures) => {
this.addTextures(textures, frame);
this.invisible = false;
this.playAnimation(direction, moving);
@@ -77,7 +77,7 @@ export abstract class Character extends Container {
fontFamily: '"Press Start 2P"',
fontSize: "8px",
strokeThickness: 2,
stroke: "#000",
stroke: "gray",
});
this.playerName.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX);
this.add(this.playerName);
@@ -155,56 +155,56 @@ export abstract class Character extends Container {
{
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`,
frameModel: name,
frames: [ 0, 1, 2, 1 ],
frames: [0, 1, 2, 1],
frameRate: 10,
repeat: -1,
},
{
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`,
frameModel: name,
frames: [ 3, 4, 5, 4 ],
frames: [3, 4, 5, 4],
frameRate: 10,
repeat: -1,
},
{
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`,
frameModel: name,
frames: [ 6, 7, 8, 7 ],
frames: [6, 7, 8, 7],
frameRate: 10,
repeat: -1,
},
{
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`,
frameModel: name,
frames: [ 9, 10, 11, 10 ],
frames: [9, 10, 11, 10],
frameRate: 10,
repeat: -1,
},
{
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`,
frameModel: name,
frames: [ 1 ],
frames: [1],
frameRate: 10,
repeat: 1,
},
{
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`,
frameModel: name,
frames: [ 4 ],
frames: [4],
frameRate: 10,
repeat: 1,
},
{
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`,
frameModel: name,
frames: [ 7 ],
frames: [7],
frameRate: 10,
repeat: 1,
},
{
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`,
frameModel: name,
frames: [ 10 ],
frames: [10],
frameRate: 10,
repeat: 1,
},
@@ -213,7 +213,7 @@ export abstract class Character extends Container {
protected playAnimation(direction: PlayerAnimationDirections, moving: boolean): void {
if (this.invisible) return;
for (const [ texture, sprite ] of this.sprites.entries()) {
for (const [texture, sprite] of this.sprites.entries()) {
if (!sprite.anims) {
console.error("ANIMS IS NOT DEFINED!!!");
return;
+2 -5
View File
@@ -1,7 +1,7 @@
//The list of all the player textures, both the default models and the partial textures used for customization
export interface BodyResourceDescriptionListInterface {
[ key: string ]: BodyResourceDescriptionInterface;
[key: string]: BodyResourceDescriptionInterface;
}
export interface BodyResourceDescriptionInterface {
@@ -434,10 +434,7 @@ export const ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {
name: "accessory_mate_bottle",
img: "resources/customisation/character_accessories/mate_bottle1.png",
},
accessory_mask: {
name: "accessory_mask",
img: "resources/customisation/character_accessories/mask.png",
},
accessory_mask: { name: "accessory_mask", img: "resources/customisation/character_accessories/mask.png" },
wheelchair: {
name: "accessory_wheelchair",
img: "resources/customisation/character_accessories/wheelchair.png",
@@ -57,7 +57,7 @@ export class GameMapPropertiesListener {
}
});
// Open a new co-website by the property.
// Open a new co-website by the property.
this.gameMap.onEnterLayer((newLayers) => {
const handler = () => {
newLayers.forEach(layer => {
@@ -109,6 +109,11 @@ export class GameMapPropertiesListener {
return;
}
this.coWebsitesOpenByLayer.set(layer, {
coWebsite: undefined,
state: OpenCoWebsiteState.LOADING,
});
const openWebsiteFunction = () => {
coWebsiteManager.loadCoWebsite(
openWebsiteProperty as string,
+17 -18
View File
@@ -89,8 +89,6 @@ import { get } from "svelte/store";
import { contactPageStore } from "../../Stores/MenuStore";
import { GameMapProperties } from "./GameMapProperties";
export interface GameSceneInitInterface {
initPosition: PointInterface | null;
reconnecting: boolean;
@@ -271,7 +269,7 @@ export class GameScene extends DirtyScene {
// So if we are in https, we can still try to load a HTTP local resource (can be useful for testing purposes)
// See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure
const url = new URL(file.src);
const host = url.host.split(":")[ 0 ];
const host = url.host.split(":")[0];
if (
window.location.protocol === "https:" &&
file.src === this.MapUrlFile &&
@@ -326,8 +324,8 @@ export class GameScene extends DirtyScene {
//eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.load as any).rexWebFont({
custom: {
families: [ "Press Start 2P" ],
urls: [ "/resources/fonts/fonts.css" ],
families: ["Press Start 2P"],
urls: ["/resources/fonts/fonts.css"],
testString: "abcdefg",
},
});
@@ -373,7 +371,7 @@ export class GameScene extends DirtyScene {
}
}
for (const [ itemType, objectsOfType ] of this.objectsByType) {
for (const [itemType, objectsOfType] of this.objectsByType) {
// FIXME: we would ideally need for the loader to WAIT for the import to be performed, which means writing our own loader plugin.
let itemFactory: ItemFactoryInterface;
@@ -404,7 +402,7 @@ export class GameScene extends DirtyScene {
// TODO: we should pass here a factory to create sprites (maybe?)
// Do we have a state for this object?
const state = roomJoinedAnswer.items[ object.id ];
const state = roomJoinedAnswer.items[object.id];
const actionableItem = itemFactory.factory(this, object, state);
this.actionableItems.set(actionableItem.getId(), actionableItem);
@@ -634,7 +632,7 @@ export class GameScene extends DirtyScene {
}
});
Promise.all([ this.connectionAnswerPromise as Promise<unknown>, ...scriptPromises ]).then(() => {
Promise.all([this.connectionAnswerPromise as Promise<unknown>, ...scriptPromises]).then(() => {
this.scene.wake();
});
}
@@ -722,8 +720,8 @@ export class GameScene extends DirtyScene {
if (item === undefined) {
console.warn(
'Received an event about object "' +
message.itemId +
'" but cannot find this item on the map.'
message.itemId +
'" but cannot find this item on the map.'
);
return;
}
@@ -931,8 +929,8 @@ export class GameScene extends DirtyScene {
} else {
console.error(
"Error while opening a popup. Cannot find an object on the map with name '" +
openPopupEvent.targetObject +
"'. The first parameter of WA.openPopup() must be the name of a rectangle object in your map."
openPopupEvent.targetObject +
"'. The first parameter of WA.openPopup() must be the name of a rectangle object in your map."
);
return;
}
@@ -943,7 +941,8 @@ export class GameScene extends DirtyScene {
html += `<input id="popupinput-${openPopupEvent.popupId}" class="nes-input" />`
}
html += `</div>`;
const buttonContainer = `<div class="buttonContainer"</div>`;
const buttonContainer = '<div class="buttonContainer"</div>';
html += buttonContainer;
let id = 0;
for (const button of openPopupEvent.buttons) {
@@ -1190,7 +1189,7 @@ export class GameScene extends DirtyScene {
const jsonTilesetDir = eventTileset.url.substr(0, eventTileset.url.lastIndexOf("/"));
//Initialise the firstgid to 1 because if there is no tileset in the tilemap, the firstgid will be 1
let newFirstgid = 1;
const lastTileset = this.mapFile.tilesets[ this.mapFile.tilesets.length - 1 ];
const lastTileset = this.mapFile.tilesets[this.mapFile.tilesets.length - 1];
if (lastTileset) {
//If there is at least one tileset in the tilemap then calculate the firstgid of the new tileset
newFirstgid = lastTileset.firstgid + lastTileset.tilecount;
@@ -1290,14 +1289,14 @@ export class GameScene extends DirtyScene {
if (phaserLayers === []) {
console.warn(
'Could not find layer with name that contains "' +
layerName +
'" when calling WA.hideLayer / WA.showLayer'
layerName +
'" when calling WA.hideLayer / WA.showLayer'
);
return;
}
for (let i = 0; i < phaserLayers.length; i++) {
phaserLayers[ i ].setVisible(visible);
phaserLayers[ i ].setCollisionByProperty({ collides: true }, visible);
phaserLayers[i].setVisible(visible);
phaserLayers[i].setCollisionByProperty({ collides: true }, visible);
}
}
this.markDirty();
+5 -5
View File
@@ -17,7 +17,7 @@ export class LoginScene extends ResizableScene {
this.name = gameManager.getPlayerName() || "";
}
preload() { }
preload() {}
create() {
loginSceneVisibleIframeStore.set(false);
@@ -28,9 +28,9 @@ export class LoginScene extends ResizableScene {
gameManager.currentStartedRoom.authenticationMandatory
) {
connectionManager.loadOpenIDScreen();
} else {
loginSceneVisibleStore.set(true);
loginSceneVisibleIframeStore.set(true);
}
loginSceneVisibleStore.set(true);
}
public login(name: string): void {
@@ -43,7 +43,7 @@ export class LoginScene extends ResizableScene {
loginSceneVisibleStore.set(false);
}
update(time: number, delta: number): void { }
update(time: number, delta: number): void {}
public onResize(): void { }
public onResize(): void {}
}
+3
View File
@@ -67,6 +67,9 @@ function createChatMessagesStore() {
});
},
addPersonnalMessage(text: string) {
//post message iframe listener
iframeListener.sendUserInputChat(text);
newChatMessageStore.set(text);
update((list) => {
const lastMessage = list[list.length - 1];
+5 -5
View File
@@ -35,21 +35,21 @@ export enum SubMenusInterface {
settings = "Settings",
profile = "Profile",
worlds = "Worlds",
createMap = "Create a Map",
aboutRoom = "About the Room",
invite = "Invite",
aboutRoom = "Credit",
globalMessages = "Global Messages",
contact = "Contact",
}
function createSubMenusStore() {
const { subscribe, update } = writable<string[]>([
SubMenusInterface.settings,
SubMenusInterface.profile,
SubMenusInterface.worlds,
SubMenusInterface.createMap,
SubMenusInterface.aboutRoom,
SubMenusInterface.globalMessages,
SubMenusInterface.contact,
SubMenusInterface.settings,
SubMenusInterface.invite,
SubMenusInterface.aboutRoom,
]);
return {
+5
View File
@@ -517,6 +517,7 @@ class CoWebsiteManager {
};
this.coWebsites.push(coWebsite);
// this.cowebsiteSubIconsDom.appendChild(icon);
const onTimeoutPromise = new Promise<void>((resolve) => {
setTimeout(() => resolve(), 2000);
@@ -564,6 +565,10 @@ class CoWebsiteManager {
this.fire();
}
if (coWebsite) {
iframeListener.unregisterIframe(coWebsite.iframe);
}
this.removeCoWebsiteFromStack(coWebsite);
resolve();
})
+1 -1
View File
@@ -147,7 +147,7 @@ const wa = {
* @deprecated Use WA.ui.openPopup instead
*/
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[], popupClass : string, input : boolean): Popup {
console.warn('Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead');
console.warn("Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead");
return ui.openPopup(targetObject, message, buttons, popupClass, input);
},
/**