Merge branch 'develop' of github.com:thecodingmachine/workadventure into improve_logging
This commit is contained in:
commit
a1107bd20e
71 changed files with 10373 additions and 520 deletions
|
@ -4,7 +4,7 @@ declare let window: any;
|
|||
|
||||
class AnalyticsClient {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private posthogPromise: Promise<any>;
|
||||
private posthogPromise: Promise<any> | undefined;
|
||||
|
||||
constructor() {
|
||||
if (POSTHOG_API_KEY && POSTHOG_URL) {
|
||||
|
@ -14,65 +14,68 @@ class AnalyticsClient {
|
|||
window.posthog = posthog;
|
||||
return posthog;
|
||||
});
|
||||
} else {
|
||||
this.posthogPromise = Promise.reject();
|
||||
}
|
||||
}
|
||||
|
||||
identifyUser(uuid: string, email: string | null) {
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.identify(uuid, { uuid, email, wa: true });
|
||||
})
|
||||
.catch();
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.identify(uuid, { uuid, email, wa: true });
|
||||
});
|
||||
}
|
||||
|
||||
loggedWithSso() {
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-logged-sso");
|
||||
})
|
||||
.catch();
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.capture("wa-logged-sso");
|
||||
});
|
||||
}
|
||||
|
||||
loggedWithToken() {
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-logged-token");
|
||||
})
|
||||
.catch();
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.capture("wa-logged-token");
|
||||
});
|
||||
}
|
||||
|
||||
enteredRoom(roomId: string, roomGroup: string | null) {
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("$pageView", { roomId, roomGroup });
|
||||
})
|
||||
.catch();
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.capture("$pageView", { roomId, roomGroup });
|
||||
posthog.capture("enteredRoom");
|
||||
});
|
||||
}
|
||||
|
||||
openedMenu() {
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-opened-menu");
|
||||
})
|
||||
.catch();
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.capture("wa-opened-menu");
|
||||
});
|
||||
}
|
||||
|
||||
launchEmote(emote: string) {
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-emote-launch", { emote });
|
||||
})
|
||||
.catch();
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.capture("wa-emote-launch", { emote });
|
||||
});
|
||||
}
|
||||
|
||||
enteredJitsi(roomName: string, roomId: string) {
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-entered-jitsi", { roomName, roomId });
|
||||
})
|
||||
.catch();
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.capture("wa-entered-jitsi", { roomName, roomId });
|
||||
});
|
||||
}
|
||||
|
||||
validationName() {
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.capture("wa-name-validation");
|
||||
});
|
||||
}
|
||||
|
||||
validationWoka(scene: string) {
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.capture("wa-woka-validation", { scene });
|
||||
});
|
||||
}
|
||||
|
||||
validationVideo() {
|
||||
this.posthogPromise?.then((posthog) => {
|
||||
posthog.capture("wa-video-validation");
|
||||
});
|
||||
}
|
||||
}
|
||||
export const analyticsClient = new AnalyticsClient();
|
||||
|
|
|
@ -49,7 +49,7 @@ class IframeListener {
|
|||
public readonly openTabStream = this._openTabStream.asObservable();
|
||||
|
||||
private readonly _loadPageStream: Subject<string> = new Subject();
|
||||
public readonly loadPageStream = this._loadPageStream.asObservable()
|
||||
public readonly loadPageStream = this._loadPageStream.asObservable();
|
||||
|
||||
private readonly _disablePlayerControlStream: Subject<void> = new Subject();
|
||||
public readonly disablePlayerControlStream = this._disablePlayerControlStream.asObservable();
|
||||
|
@ -269,7 +269,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 (
|
||||
|
|
|
@ -18,13 +18,18 @@
|
|||
'--font': 'Press Start 2P'
|
||||
},
|
||||
emojisPerRow: isMobile() ? 6 : 8,
|
||||
autoFocusSearch: false
|
||||
autoFocusSearch: false,
|
||||
style: 'twemoji',
|
||||
});
|
||||
//the timeout is here to prevent the menu from flashing
|
||||
setTimeout(() => picker.showPicker(emojiContainer), 100);
|
||||
|
||||
picker.on("emoji", (selection) => {
|
||||
emoteStore.set(selection.emoji);
|
||||
emoteStore.set({
|
||||
unicode: selection.emoji,
|
||||
url: selection.url,
|
||||
name: selection.name
|
||||
});
|
||||
});
|
||||
|
||||
picker.on("hidden", () => {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
let gameScene = gameManager.getCurrentGameScene();
|
||||
|
||||
let HTMLShareLink: HTMLInputElement;
|
||||
let expandedMapCopyright = false;
|
||||
let expandedTilesetCopyright = false;
|
||||
|
||||
|
@ -38,35 +37,9 @@
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
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="about-room-main">
|
||||
<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>
|
||||
<h2>Information on the map</h2>
|
||||
<section class="container-overflow">
|
||||
<h3>{mapName}</h3>
|
||||
|
@ -93,24 +66,6 @@
|
|||
div.about-room-main {
|
||||
height: calc(100% - 56px);
|
||||
|
||||
section.share-url {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
input {
|
||||
width: 85%;
|
||||
border-radius: 32px;
|
||||
padding: 3px;
|
||||
}
|
||||
input::selection {
|
||||
background-color: #209cee;
|
||||
}
|
||||
}
|
||||
|
||||
section.is-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
h2, h3 {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
@ -126,21 +81,11 @@
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
|
||||
div.about-room-main {
|
||||
section.share-url.not-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section.is-mobile {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
section.container-overflow {
|
||||
height: calc(100% - 120px);
|
||||
}
|
||||
|
|
|
@ -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,51 +0,0 @@
|
|||
<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");
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<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>
|
||||
</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>
|
75
front/src/Components/Menu/GuestSubMenu.svelte
Normal file
75
front/src/Components/Menu/GuestSubMenu.svelte
Normal file
|
@ -0,0 +1,75 @@
|
|||
<script lang="ts">
|
||||
function copyLink() {
|
||||
const input: HTMLInputElement = document.getElementById('input-share-link') as HTMLInputElement;
|
||||
input.focus();
|
||||
input.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 id="input-share-link" value={location.toString()}>
|
||||
<button type="button" class="nes-btn is-primary" on:click={copyLink}>Copy</button>
|
||||
</section>
|
||||
<section class="is-mobile">
|
||||
<h3>Share the link of the room !</h3>
|
||||
<input type="hidden" readonly id="input-share-link" value={location.toString()}>
|
||||
<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>
|
|
@ -2,11 +2,11 @@
|
|||
import {fly} from "svelte/transition";
|
||||
import SettingsSubMenu from "./SettingsSubMenu.svelte";
|
||||
import ProfileSubMenu from "./ProfileSubMenu.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,
|
||||
|
@ -19,21 +19,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(() => {
|
||||
|
@ -52,8 +52,8 @@
|
|||
case SubMenusInterface.profile:
|
||||
activeComponent = ProfileSubMenu;
|
||||
break;
|
||||
case SubMenusInterface.createMap:
|
||||
activeComponent = CreateMapSubMenu;
|
||||
case SubMenusInterface.invite:
|
||||
activeComponent = GuestSubMenu;
|
||||
break;
|
||||
case SubMenusInterface.aboutRoom:
|
||||
activeComponent = AboutRoomSubMenu;
|
||||
|
|
|
@ -11,15 +11,9 @@
|
|||
function showChat(){
|
||||
chatVisibilityStore.set(true);
|
||||
}
|
||||
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Tab") {
|
||||
showMenu();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown}/>
|
||||
<svelte:window/>
|
||||
|
||||
<main class="menuIcon">
|
||||
<img src={logoWA} alt="open menu" class="nes-pointer" on:click|preventDefault={showMenu}>
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
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(){
|
||||
|
@ -55,54 +59,107 @@
|
|||
</script>
|
||||
|
||||
<div class="customize-main">
|
||||
{#if $userIsConnected}
|
||||
<div class="submenu">
|
||||
<section>
|
||||
{#if PROFILE_URL != undefined}
|
||||
<iframe title="profile" src="{getProfileUrl()}"></iframe>
|
||||
{/if}
|
||||
<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 WOKA">
|
||||
<span class="btn-hover">Edit your WOKA</span>
|
||||
</button>
|
||||
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>
|
||||
<img src={btnProfileSubMenuCompanion} alt="Edit your companion">
|
||||
<span class="btn-hover">Edit 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>
|
||||
<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>
|
||||
<button type="button" class="nes-btn" on:click|preventDefault={openEditNameScene}>Edit Name</button>
|
||||
<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>
|
||||
</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}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.customize-main{
|
||||
section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
|
||||
iframe{
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
border: none;
|
||||
}
|
||||
div.submenu{
|
||||
height: 100%;
|
||||
width: 50px;
|
||||
|
||||
button {
|
||||
height: 50px;
|
||||
width: 250px;
|
||||
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 {
|
||||
div.customize-main.content section button {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
|
|
1
front/src/Components/images/btn-menu-profile-camera.svg
Normal file
1
front/src/Components/images/btn-menu-profile-camera.svg
Normal file
|
@ -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 |
1
front/src/Components/images/btn-menu-profile-woka.svg
Normal file
1
front/src/Components/images/btn-menu-profile-woka.svg
Normal file
|
@ -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 |
|
@ -41,7 +41,6 @@ class ConnectionManager {
|
|||
const nonce = localUserStore.generateNonce();
|
||||
localUserStore.setAuthToken(null);
|
||||
|
||||
//TODO fix me to redirect this URL by pusher
|
||||
if (!this._currentRoom || !this._currentRoom.iframeAuthentication) {
|
||||
loginSceneVisibleIframeStore.set(false);
|
||||
return null;
|
||||
|
@ -79,6 +78,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) {
|
||||
|
@ -87,15 +96,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();
|
||||
|
@ -164,14 +177,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
|
||||
|
@ -199,13 +218,15 @@ 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> {
|
||||
const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data);
|
||||
this.localUser = new LocalUser(data.userUuid, []);
|
||||
this.localUser = new LocalUser(data.userUuid, [], data.email);
|
||||
this.authToken = data.authToken;
|
||||
if (!isBenchmark) {
|
||||
// In benchmark, we don't have a local storage.
|
||||
|
@ -279,20 +300,25 @@ 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 } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token } }).then(
|
||||
(res) => res.data
|
||||
);
|
||||
|
||||
if (!token) {
|
||||
if (!state || !localUserStore.verifyState(state)) {
|
||||
throw "Could not validate state!";
|
||||
}
|
||||
if (!code) {
|
||||
throw "No Auth code provided";
|
||||
}
|
||||
}
|
||||
const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, {
|
||||
params: { code, nonce, token, playUri: this.currentRoom?.key },
|
||||
}).then((res) => res.data);
|
||||
localUserStore.setAuthToken(authToken);
|
||||
this.localUser = new LocalUser(userUuid, textures, email);
|
||||
localUserStore.saveUser(this.localUser);
|
||||
this.authToken = authToken;
|
||||
|
||||
//user connected, set connected store for menu at true
|
||||
|
|
|
@ -122,10 +122,12 @@ class LocalUserStore {
|
|||
|
||||
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);
|
||||
});
|
||||
if ("caches" in window) {
|
||||
caches.open(cacheAPIIndex).then((cache) => {
|
||||
const stringResponse = new Response(JSON.stringify({ roomUrl }));
|
||||
cache.put(`/${lastRoomUrl}`, stringResponse);
|
||||
});
|
||||
}
|
||||
}
|
||||
getLastRoomUrl(): string {
|
||||
return (
|
||||
|
@ -133,6 +135,9 @@ class LocalUserStore {
|
|||
);
|
||||
}
|
||||
getLastRoomUrlCacheApi(): Promise<string | undefined> {
|
||||
if (!("caches" in window)) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
return caches.open(cacheAPIIndex).then((cache) => {
|
||||
return cache.match(`/${lastRoomUrl}`).then((res) => {
|
||||
return res?.json().then((data) => {
|
||||
|
@ -165,8 +170,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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER;
|
||||
private _mapUrl: string | undefined;
|
||||
private _textures: CharacterTexture[] | undefined;
|
||||
private instance: string | undefined;
|
||||
|
@ -89,27 +89,37 @@ export class Room {
|
|||
}
|
||||
|
||||
private async getMapDetail(): Promise<MapDetail | RoomRedirect> {
|
||||
const result = await Axios.get(`${PUSHER_URL}/map`, {
|
||||
params: {
|
||||
playUri: this.roomUrl.toString(),
|
||||
authToken: localUserStore.getAuthToken(),
|
||||
},
|
||||
});
|
||||
try {
|
||||
const result = await Axios.get(`${PUSHER_URL}/map`, {
|
||||
params: {
|
||||
playUri: this.roomUrl.toString(),
|
||||
authToken: localUserStore.getAuthToken(),
|
||||
},
|
||||
});
|
||||
|
||||
const data = result.data;
|
||||
if (data.redirectUrl) {
|
||||
return {
|
||||
redirectUrl: data.redirectUrl as string,
|
||||
};
|
||||
const data = result.data;
|
||||
if (data.redirectUrl) {
|
||||
return {
|
||||
redirectUrl: data.redirectUrl as string,
|
||||
};
|
||||
}
|
||||
console.log("Map ", this.id, " resolves to URL ", data.mapUrl);
|
||||
this._mapUrl = data.mapUrl;
|
||||
this._textures = data.textures;
|
||||
this._group = data.group;
|
||||
this._authenticationMandatory = data.authenticationMandatory || DISABLE_ANONYMOUS;
|
||||
this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER;
|
||||
this._contactPage = data.contactPage || CONTACT_URL;
|
||||
return new MapDetail(data.mapUrl, data.textures);
|
||||
} catch (e) {
|
||||
console.error("Error => getMapDetail", e, e.response);
|
||||
//TODO fix me and manage Error class
|
||||
if (e.response?.data === "Token decrypted error") {
|
||||
localUserStore.setAuthToken(null);
|
||||
window.location.assign("/login");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
console.log("Map ", this.id, " resolves to URL ", data.mapUrl);
|
||||
this._mapUrl = data.mapUrl;
|
||||
this._textures = data.textures;
|
||||
this._group = data.group;
|
||||
this._authenticationMandatory = data.authenticationMandatory || false;
|
||||
this._iframeAuthentication = data.iframeAuthentication;
|
||||
this._contactPage = data.contactPage || CONTACT_URL;
|
||||
return new MapDetail(data.mapUrl, data.textures);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,7 @@ const START_ROOM_URL: string =
|
|||
const PUSHER_URL = process.env.PUSHER_URL || "//pusher.workadventure.localhost";
|
||||
export const ADMIN_URL = process.env.ADMIN_URL || "//workadventu.re";
|
||||
const UPLOADER_URL = process.env.UPLOADER_URL || "//uploader.workadventure.localhost";
|
||||
const ICON_URL = process.env.ICON_URL || "//icon.workadventure.localhost";
|
||||
const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302";
|
||||
const TURN_SERVER: string = process.env.TURN_SERVER || "";
|
||||
const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true";
|
||||
|
@ -22,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: boolean = process.env.DISABLE_ANONYMOUS === "true";
|
||||
export const OPID_LOGIN_SCREEN_PROVIDER = process.env.OPID_LOGIN_SCREEN_PROVIDER;
|
||||
|
||||
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;
|
||||
|
||||
|
@ -32,6 +35,7 @@ export {
|
|||
DISABLE_NOTIFICATIONS,
|
||||
PUSHER_URL,
|
||||
UPLOADER_URL,
|
||||
ICON_URL,
|
||||
POSITION_DELAY,
|
||||
MAX_EXTRAPOLATION_TIME,
|
||||
STUN_SERVER,
|
||||
|
|
|
@ -6,73 +6,124 @@ const TextName: string = "Loading...";
|
|||
const LogoResource: string = "static/images/logo.png";
|
||||
const LogoFrame: ImageFrameConfig = { frameWidth: 310, frameHeight: 60 };
|
||||
|
||||
export const addLoader = (scene: Phaser.Scene): void => {
|
||||
// If there is nothing to load, do not display the loader.
|
||||
if (scene.load.list.entries.length === 0) {
|
||||
return;
|
||||
}
|
||||
let loadingText: Phaser.GameObjects.Text | null = null;
|
||||
const loadingBarWidth: number = Math.floor(scene.game.renderer.width / 3);
|
||||
const loadingBarHeight: number = 16;
|
||||
const padding: number = 5;
|
||||
const loadingBarHeight: number = 16;
|
||||
const padding: number = 5;
|
||||
|
||||
const promiseLoadLogoTexture = new Promise<Phaser.GameObjects.Image>((res) => {
|
||||
if (scene.load.textureManager.exists(LogoNameIndex)) {
|
||||
return res(
|
||||
scene.add.image(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 150, LogoNameIndex)
|
||||
);
|
||||
} else {
|
||||
//add loading if logo image is not ready
|
||||
loadingText = scene.add.text(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 50, TextName);
|
||||
export class Loader {
|
||||
private progressContainer!: Phaser.GameObjects.Graphics;
|
||||
private progress!: Phaser.GameObjects.Graphics;
|
||||
private progressAmount: number = 0;
|
||||
private logo: Phaser.GameObjects.Image | undefined;
|
||||
private loadingText: Phaser.GameObjects.Text | null = null;
|
||||
|
||||
public constructor(private scene: Phaser.Scene) {}
|
||||
|
||||
public addLoader(): void {
|
||||
// If there is nothing to load, do not display the loader.
|
||||
if (this.scene.load.list.entries.length === 0) {
|
||||
return;
|
||||
}
|
||||
scene.load.spritesheet(LogoNameIndex, LogoResource, LogoFrame);
|
||||
scene.load.once(`filecomplete-spritesheet-${LogoNameIndex}`, () => {
|
||||
if (loadingText) {
|
||||
loadingText.destroy();
|
||||
|
||||
const loadingBarWidth: number = Math.floor(this.scene.game.renderer.width / 3);
|
||||
|
||||
const promiseLoadLogoTexture = new Promise<Phaser.GameObjects.Image>((res) => {
|
||||
if (this.scene.load.textureManager.exists(LogoNameIndex)) {
|
||||
return res(
|
||||
(this.logo = this.scene.add.image(
|
||||
this.scene.game.renderer.width / 2,
|
||||
this.scene.game.renderer.height / 2 - 150,
|
||||
LogoNameIndex
|
||||
))
|
||||
);
|
||||
} else {
|
||||
//add loading if logo image is not ready
|
||||
this.loadingText = this.scene.add.text(
|
||||
this.scene.game.renderer.width / 2,
|
||||
this.scene.game.renderer.height / 2 - 50,
|
||||
TextName
|
||||
);
|
||||
}
|
||||
return res(
|
||||
scene.add.image(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 150, LogoNameIndex)
|
||||
);
|
||||
this.scene.load.spritesheet(LogoNameIndex, LogoResource, LogoFrame);
|
||||
this.scene.load.once(`filecomplete-spritesheet-${LogoNameIndex}`, () => {
|
||||
if (this.loadingText) {
|
||||
this.loadingText.destroy();
|
||||
}
|
||||
return res(
|
||||
(this.logo = this.scene.add.image(
|
||||
this.scene.game.renderer.width / 2,
|
||||
this.scene.game.renderer.height / 2 - 150,
|
||||
LogoNameIndex
|
||||
))
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const progressContainer = scene.add.graphics();
|
||||
const progress = scene.add.graphics();
|
||||
progressContainer.fillStyle(0x444444, 0.8);
|
||||
progressContainer.fillRect(
|
||||
(scene.game.renderer.width - loadingBarWidth) / 2 - padding,
|
||||
scene.game.renderer.height / 2 + 50 - padding,
|
||||
loadingBarWidth + padding * 2,
|
||||
loadingBarHeight + padding * 2
|
||||
);
|
||||
this.progressContainer = this.scene.add.graphics();
|
||||
this.progress = this.scene.add.graphics();
|
||||
this.progressContainer.fillStyle(0x444444, 0.8);
|
||||
|
||||
scene.load.on("progress", (value: number) => {
|
||||
progress.clear();
|
||||
progress.fillStyle(0xbbbbbb, 1);
|
||||
progress.fillRect(
|
||||
(scene.game.renderer.width - loadingBarWidth) / 2,
|
||||
scene.game.renderer.height / 2 + 50,
|
||||
loadingBarWidth * value,
|
||||
this.resize();
|
||||
|
||||
this.scene.load.on("progress", (value: number) => {
|
||||
this.progressAmount = value;
|
||||
this.drawProgress();
|
||||
});
|
||||
this.scene.load.on("complete", () => {
|
||||
if (this.loadingText) {
|
||||
this.loadingText.destroy();
|
||||
}
|
||||
promiseLoadLogoTexture.then((resLoadingImage: Phaser.GameObjects.Image) => {
|
||||
resLoadingImage.destroy();
|
||||
});
|
||||
this.progress.destroy();
|
||||
this.progressContainer.destroy();
|
||||
if (this.scene instanceof DirtyScene) {
|
||||
this.scene.markDirty();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public removeLoader(): void {
|
||||
if (this.scene.load.textureManager.exists(LogoNameIndex)) {
|
||||
this.scene.load.textureManager.remove(LogoNameIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public resize(): void {
|
||||
const loadingBarWidth: number = Math.floor(this.scene.game.renderer.width / 3);
|
||||
|
||||
this.progressContainer.clear();
|
||||
this.progressContainer.fillStyle(0x444444, 0.8);
|
||||
this.progressContainer.fillRect(
|
||||
(this.scene.game.renderer.width - loadingBarWidth) / 2 - padding,
|
||||
this.scene.game.renderer.height / 2 + 50 - padding,
|
||||
loadingBarWidth + padding * 2,
|
||||
loadingBarHeight + padding * 2
|
||||
);
|
||||
|
||||
this.drawProgress();
|
||||
|
||||
if (this.loadingText) {
|
||||
this.loadingText.x = this.scene.game.renderer.width / 2;
|
||||
this.loadingText.y = this.scene.game.renderer.height / 2 - 50;
|
||||
}
|
||||
|
||||
if (this.logo) {
|
||||
this.logo.x = this.scene.game.renderer.width / 2;
|
||||
this.logo.y = this.scene.game.renderer.height / 2 - 150;
|
||||
}
|
||||
}
|
||||
|
||||
private drawProgress() {
|
||||
const loadingBarWidth: number = Math.floor(this.scene.game.renderer.width / 3);
|
||||
|
||||
this.progress.clear();
|
||||
this.progress.fillStyle(0xbbbbbb, 1);
|
||||
this.progress.fillRect(
|
||||
(this.scene.game.renderer.width - loadingBarWidth) / 2,
|
||||
this.scene.game.renderer.height / 2 + 50,
|
||||
loadingBarWidth * this.progressAmount,
|
||||
loadingBarHeight
|
||||
);
|
||||
});
|
||||
scene.load.on("complete", () => {
|
||||
if (loadingText) {
|
||||
loadingText.destroy();
|
||||
}
|
||||
promiseLoadLogoTexture.then((resLoadingImage: Phaser.GameObjects.Image) => {
|
||||
resLoadingImage.destroy();
|
||||
});
|
||||
progress.destroy();
|
||||
progressContainer.destroy();
|
||||
if (scene instanceof DirtyScene) {
|
||||
scene.markDirty();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const removeLoader = (scene: Phaser.Scene): void => {
|
||||
if (scene.load.textureManager.exists(LogoNameIndex)) {
|
||||
scene.load.textureManager.remove(LogoNameIndex);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { SpeechBubble } from "./SpeechBubble";
|
|||
import Text = Phaser.GameObjects.Text;
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import DOMElement = Phaser.GameObjects.DOMElement;
|
||||
import { TextureError } from "../../Exception/TextureError";
|
||||
import { Companion } from "../Companion/Companion";
|
||||
import type { GameScene } from "../Game/GameScene";
|
||||
|
@ -33,7 +34,7 @@ export abstract class Character extends Container {
|
|||
//private teleportation: Sprite;
|
||||
private invisible: boolean;
|
||||
public companion?: Companion;
|
||||
private emote: Phaser.GameObjects.Text | null = null;
|
||||
private emote: Phaser.GameObjects.DOMElement | null = null;
|
||||
private emoteTween: Phaser.Tweens.Tween | null = null;
|
||||
scene: GameScene;
|
||||
|
||||
|
@ -300,8 +301,9 @@ export abstract class Character extends Container {
|
|||
playEmote(emote: string) {
|
||||
this.cancelPreviousEmote();
|
||||
const emoteY = -45;
|
||||
this.playerName.setVisible(false);
|
||||
this.emote = new Text(this.scene, -10, 0, emote, { fontSize: "20px" });
|
||||
const image = new Image(16, 16);
|
||||
image.src = emote;
|
||||
this.emote = new DOMElement(this.scene, -1, 0, image, "z-index:10;");
|
||||
this.emote.setAlpha(0);
|
||||
this.add(this.emote);
|
||||
this.createStartTransition(emoteY);
|
||||
|
|
|
@ -162,6 +162,7 @@ export class EmbeddedWebsiteManager {
|
|||
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.src = absoluteUrl;
|
||||
iframe.tabIndex = -1;
|
||||
iframe.style.width = embeddedWebsiteEvent.position.width + "px";
|
||||
iframe.style.height = embeddedWebsiteEvent.position.height + "px";
|
||||
iframe.style.margin = "0";
|
||||
|
|
|
@ -15,10 +15,7 @@ import type {
|
|||
import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
import { Queue } from "queue-typescript";
|
||||
import {
|
||||
Box,
|
||||
ON_ACTION_TRIGGER_BUTTON,
|
||||
} from "../../WebRtc/LayoutManager";
|
||||
import { Box, ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
|
||||
import { CoWebsite, coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
|
||||
import type { UserMovedMessage } from "../../Messages/generated/messages_pb";
|
||||
import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils";
|
||||
|
@ -31,7 +28,7 @@ import { localUserStore } from "../../Connexion/LocalUserStore";
|
|||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||
import { mediaManager } from "../../WebRtc/MediaManager";
|
||||
import { SimplePeer } from "../../WebRtc/SimplePeer";
|
||||
import { addLoader, removeLoader } from "../Components/Loader";
|
||||
import { Loader } from "../Components/Loader";
|
||||
import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager";
|
||||
import { RemotePlayer } from "../Entity/RemotePlayer";
|
||||
import type { ActionableItem } from "../Items/ActionableItem";
|
||||
|
@ -91,6 +88,7 @@ import { analyticsClient } from "../../Administration/AnalyticsClient";
|
|||
import { get } from "svelte/store";
|
||||
import { contactPageStore } from "../../Stores/MenuStore";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile;
|
||||
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
|
@ -205,6 +203,7 @@ export class GameScene extends DirtyScene {
|
|||
private sharedVariablesManager!: SharedVariablesManager;
|
||||
private objectsByType = new Map<string, ITiledMapObject[]>();
|
||||
private embeddedWebsiteManager!: EmbeddedWebsiteManager;
|
||||
private loader: Loader;
|
||||
|
||||
constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) {
|
||||
super({
|
||||
|
@ -223,6 +222,7 @@ export class GameScene extends DirtyScene {
|
|||
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
||||
this.connectionAnswerPromiseResolve = resolve;
|
||||
});
|
||||
this.loader = new Loader(this);
|
||||
}
|
||||
|
||||
//hook preload scene
|
||||
|
@ -296,9 +296,10 @@ export class GameScene extends DirtyScene {
|
|||
}
|
||||
|
||||
//once preloading is over, we don't want loading errors to crash the game, so we need to disable this behavior after preloading.
|
||||
if (this.preloading) {
|
||||
//if SpriteSheetFile (WOKA file) don't display error and give an access for user
|
||||
if (this.preloading && !(file instanceof SpriteSheetFile)) {
|
||||
//remove loader in progress
|
||||
removeLoader(this);
|
||||
this.loader.removeLoader();
|
||||
|
||||
//display an error scene
|
||||
this.scene.start(ErrorSceneName, {
|
||||
|
@ -332,7 +333,7 @@ export class GameScene extends DirtyScene {
|
|||
});
|
||||
|
||||
//this function must stay at the end of preload function
|
||||
addLoader(this);
|
||||
this.loader.addLoader();
|
||||
}
|
||||
|
||||
// FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving.
|
||||
|
@ -497,7 +498,10 @@ export class GameScene extends DirtyScene {
|
|||
object.properties,
|
||||
'in the "' + object.name + '" object of type "website"'
|
||||
);
|
||||
const allowApi = PropertyUtils.findBooleanProperty(GameMapProperties.ALLOW_API, object.properties);
|
||||
const allowApi = PropertyUtils.findBooleanProperty(
|
||||
GameMapProperties.ALLOW_API,
|
||||
object.properties
|
||||
);
|
||||
|
||||
// TODO: add a "allow" property to iframe
|
||||
this.embeddedWebsiteManager.createEmbeddedWebsite(
|
||||
|
@ -614,10 +618,10 @@ export class GameScene extends DirtyScene {
|
|||
oldPeerNumber = newPeerNumber;
|
||||
});
|
||||
|
||||
this.emoteUnsubscribe = emoteStore.subscribe((emoteKey) => {
|
||||
if (emoteKey) {
|
||||
this.CurrentPlayer?.playEmote(emoteKey);
|
||||
this.connection?.emitEmoteEvent(emoteKey);
|
||||
this.emoteUnsubscribe = emoteStore.subscribe((emote) => {
|
||||
if (emote) {
|
||||
this.CurrentPlayer?.playEmote(emote.url);
|
||||
this.connection?.emitEmoteEvent(emote.url);
|
||||
emoteStore.set(null);
|
||||
}
|
||||
});
|
||||
|
@ -763,14 +767,14 @@ export class GameScene extends DirtyScene {
|
|||
this.gameMap.setPosition(this.CurrentPlayer.x, this.CurrentPlayer.y);
|
||||
|
||||
// Init layer change listener
|
||||
this.gameMap.onEnterLayer(layers => {
|
||||
layers.forEach(layer => {
|
||||
this.gameMap.onEnterLayer((layers) => {
|
||||
layers.forEach((layer) => {
|
||||
iframeListener.sendEnterLayerEvent(layer.name);
|
||||
});
|
||||
});
|
||||
|
||||
this.gameMap.onLeaveLayer(layers => {
|
||||
layers.forEach(layer => {
|
||||
this.gameMap.onLeaveLayer((layers) => {
|
||||
layers.forEach((layer) => {
|
||||
iframeListener.sendLeaveLayerEvent(layer.name);
|
||||
});
|
||||
});
|
||||
|
@ -1830,6 +1834,8 @@ ${escapedMessage}
|
|||
right: camera.scrollX + camera.width,
|
||||
bottom: camera.scrollY + camera.height,
|
||||
});
|
||||
|
||||
this.loader.resize();
|
||||
}
|
||||
private getObjectLayerData(objectName: string): ITiledMapObject | undefined {
|
||||
for (const layer of this.mapFile.layers) {
|
||||
|
@ -1868,7 +1874,8 @@ ${escapedMessage}
|
|||
public startJitsi(roomName: string, jwt?: string): void {
|
||||
const allProps = this.gameMap.getCurrentProperties();
|
||||
const jitsiConfig = this.safeParseJSONstring(
|
||||
allProps.get(GameMapProperties.JITSI_CONFIG) as string | undefined, GameMapProperties.JITSI_CONFIG
|
||||
allProps.get(GameMapProperties.JITSI_CONFIG) as string | undefined,
|
||||
GameMapProperties.JITSI_CONFIG
|
||||
);
|
||||
const jitsiInterfaceConfig = this.safeParseJSONstring(
|
||||
allProps.get(GameMapProperties.JITSI_INTERFACE_CONFIG) as string | undefined,
|
||||
|
|
|
@ -4,7 +4,7 @@ import { loadAllLayers } from "../Entity/PlayerTexturesLoadingManager";
|
|||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import { gameManager } from "../Game/GameManager";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { addLoader } from "../Components/Loader";
|
||||
import { Loader } from "../Components/Loader";
|
||||
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
|
||||
import { AbstractCharacterScene } from "./AbstractCharacterScene";
|
||||
import { areCharacterLayersValid } from "../../Connexion/LocalUser";
|
||||
|
@ -14,6 +14,7 @@ import { waScaleManager } from "../Services/WaScaleManager";
|
|||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
import { CustomizedCharacter } from "../Entity/CustomizedCharacter";
|
||||
import { get } from "svelte/store";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
|
||||
export const CustomizeSceneName = "CustomizeScene";
|
||||
|
||||
|
@ -29,10 +30,13 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||
private moveHorizontally: number = 0;
|
||||
private moveVertically: number = 0;
|
||||
|
||||
private loader: Loader;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: CustomizeSceneName,
|
||||
});
|
||||
this.loader = new Loader(this);
|
||||
}
|
||||
|
||||
preload() {
|
||||
|
@ -54,7 +58,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||
this.lazyloadingAttempt = false;
|
||||
|
||||
//this function must stay at the end of preload function
|
||||
addLoader(this);
|
||||
this.loader.addLoader();
|
||||
}
|
||||
|
||||
create() {
|
||||
|
@ -278,6 +282,8 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||
return;
|
||||
}
|
||||
|
||||
analyticsClient.validationWoka("CustomizeWoka");
|
||||
|
||||
gameManager.setCharacterLayers(layers);
|
||||
this.scene.sleep(CustomizeSceneName);
|
||||
waScaleManager.restoreZoom();
|
||||
|
|
|
@ -2,6 +2,7 @@ import { gameManager } from "../Game/GameManager";
|
|||
import { ResizableScene } from "./ResizableScene";
|
||||
import { enableCameraSceneVisibilityStore } from "../../Stores/MediaStore";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
|
||||
export const EnableCameraSceneName = "EnableCameraScene";
|
||||
|
||||
|
@ -27,6 +28,8 @@ export class EnableCameraScene extends ResizableScene {
|
|||
update(time: number, delta: number): void {}
|
||||
|
||||
public login(): void {
|
||||
analyticsClient.validationVideo();
|
||||
|
||||
enableCameraSceneVisibilityStore.hideEnableCameraScene();
|
||||
|
||||
this.scene.sleep(EnableCameraSceneName);
|
||||
|
|
|
@ -4,6 +4,7 @@ import { loginSceneVisibleIframeStore, loginSceneVisibleStore } from "../../Stor
|
|||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { connectionManager } from "../../Connexion/ConnectionManager";
|
||||
import { gameManager } from "../Game/GameManager";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
|
||||
export const LoginSceneName = "LoginScene";
|
||||
|
||||
|
@ -34,6 +35,8 @@ export class LoginScene extends ResizableScene {
|
|||
}
|
||||
|
||||
public login(name: string): void {
|
||||
analyticsClient.validationName();
|
||||
|
||||
name = name.trim();
|
||||
gameManager.setPlayerName(name);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { EnableCameraSceneName } from "./EnableCameraScene";
|
|||
import { CustomizeSceneName } from "./CustomizeScene";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { loadAllDefaultModels } from "../Entity/PlayerTexturesLoadingManager";
|
||||
import { addLoader } from "../Components/Loader";
|
||||
import { Loader } from "../Components/Loader";
|
||||
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
|
||||
import { AbstractCharacterScene } from "./AbstractCharacterScene";
|
||||
import { areCharacterLayersValid } from "../../Connexion/LocalUser";
|
||||
|
@ -13,6 +13,7 @@ import { PinchManager } from "../UserInput/PinchManager";
|
|||
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
|
||||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
|
||||
//todo: put this constants in a dedicated file
|
||||
export const SelectCharacterSceneName = "SelectCharacterScene";
|
||||
|
@ -30,11 +31,13 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||
protected pointerTimer: number = 0;
|
||||
|
||||
protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
|
||||
private loader: Loader;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: SelectCharacterSceneName,
|
||||
});
|
||||
this.loader = new Loader(this);
|
||||
}
|
||||
|
||||
preload() {
|
||||
|
@ -48,7 +51,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||
this.lazyloadingAttempt = false;
|
||||
|
||||
//this function must stay at the end of preload function
|
||||
addLoader(this);
|
||||
this.loader.addLoader();
|
||||
}
|
||||
|
||||
create() {
|
||||
|
@ -98,6 +101,9 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||
if (!this.selectedPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
analyticsClient.validationWoka("SelectWoka");
|
||||
|
||||
this.scene.stop(SelectCharacterSceneName);
|
||||
waScaleManager.restoreZoom();
|
||||
gameManager.setCharacterLayers([this.selectedPlayer.texture.key]);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { addLoader } from "../Components/Loader";
|
||||
import { Loader } from "../Components/Loader";
|
||||
import { gameManager } from "../Game/GameManager";
|
||||
import { ResizableScene } from "./ResizableScene";
|
||||
import { EnableCameraSceneName } from "./EnableCameraScene";
|
||||
|
@ -22,11 +22,13 @@ export class SelectCompanionScene extends ResizableScene {
|
|||
private currentCompanion = 0;
|
||||
private pointerClicked: boolean = false;
|
||||
private pointerTimer: number = 0;
|
||||
private loader: Loader;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: SelectCompanionSceneName,
|
||||
});
|
||||
this.loader = new Loader(this);
|
||||
}
|
||||
|
||||
preload() {
|
||||
|
@ -35,7 +37,7 @@ export class SelectCompanionScene extends ResizableScene {
|
|||
});
|
||||
|
||||
//this function must stay at the end of preload function
|
||||
addLoader(this);
|
||||
this.loader.addLoader();
|
||||
}
|
||||
|
||||
create() {
|
||||
|
|
|
@ -3,8 +3,6 @@ import type { GameScene } from "../Game/GameScene";
|
|||
import { UserInputEvent, UserInputManager } from "../UserInput/UserInputManager";
|
||||
import { Character } from "../Entity/Character";
|
||||
import { userMovingStore } from "../../Stores/GameStore";
|
||||
import { get } from "svelte/store";
|
||||
import { emoteMenuStore } from "../../Stores/EmoteStore";
|
||||
|
||||
export const hasMovedEventName = "hasMoved";
|
||||
export const requestEmoteEventName = "requestEmote";
|
||||
|
@ -68,10 +66,24 @@ export class Player extends Character {
|
|||
} else if (this.wasMoving && moving) {
|
||||
// slow joystick movement
|
||||
this.move(0, 0);
|
||||
this.emit(hasMovedEventName, { moving, direction: this.previousDirection, x: this.x, y: this.y, oldX: x, oldY: y });
|
||||
this.emit(hasMovedEventName, {
|
||||
moving,
|
||||
direction: this.previousDirection,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
oldX: x,
|
||||
oldY: y,
|
||||
});
|
||||
} else if (this.wasMoving && !moving) {
|
||||
this.stop();
|
||||
this.emit(hasMovedEventName, { moving, direction: this.previousDirection, x: this.x, y: this.y, oldX: x, oldY: y });
|
||||
this.emit(hasMovedEventName, {
|
||||
moving,
|
||||
direction: this.previousDirection,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
oldX: x,
|
||||
oldY: y,
|
||||
});
|
||||
}
|
||||
|
||||
if (direction !== null) {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import { writable } from "svelte/store";
|
||||
|
||||
export interface Emoji {
|
||||
unicode: string;
|
||||
url: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function createEmoteMenuStore() {
|
||||
const { subscribe, set } = writable(false);
|
||||
|
||||
|
@ -14,5 +20,5 @@ function createEmoteMenuStore() {
|
|||
};
|
||||
}
|
||||
|
||||
export const emoteStore = writable<string | null>(null);
|
||||
export const emoteStore = writable<Emoji | null>(null);
|
||||
export const emoteMenuStore = createEmoteMenuStore();
|
||||
|
|
|
@ -34,20 +34,20 @@ export const warningContainerStore = createWarningContainerStore();
|
|||
export enum SubMenusInterface {
|
||||
settings = "Settings",
|
||||
profile = "Profile",
|
||||
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.createMap,
|
||||
SubMenusInterface.aboutRoom,
|
||||
SubMenusInterface.globalMessages,
|
||||
SubMenusInterface.contact,
|
||||
SubMenusInterface.settings,
|
||||
SubMenusInterface.invite,
|
||||
SubMenusInterface.aboutRoom,
|
||||
]);
|
||||
|
||||
return {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Subject } from "rxjs";
|
|||
import { iframeListener } from "../Api/IframeListener";
|
||||
import { touchScreenManager } from "../Touch/TouchScreenManager";
|
||||
import { waScaleManager } from "../Phaser/Services/WaScaleManager";
|
||||
import { ICON_URL } from "../Enum/EnvironmentVariable";
|
||||
|
||||
enum iframeStates {
|
||||
closed = 1,
|
||||
|
@ -27,16 +28,16 @@ interface TouchMoveCoordinates {
|
|||
y: number;
|
||||
}
|
||||
|
||||
export type CoWebsite = {
|
||||
iframe: HTMLIFrameElement,
|
||||
icon: HTMLDivElement,
|
||||
position: number
|
||||
}
|
||||
export type CoWebsite = {
|
||||
iframe: HTMLIFrameElement;
|
||||
icon: HTMLDivElement;
|
||||
position: number;
|
||||
};
|
||||
|
||||
type CoWebsiteSlot = {
|
||||
container: HTMLElement,
|
||||
position: number
|
||||
}
|
||||
container: HTMLElement;
|
||||
position: number;
|
||||
};
|
||||
|
||||
class CoWebsiteManager {
|
||||
private openedMain: iframeStates = iframeStates.closed;
|
||||
|
@ -61,7 +62,7 @@ class CoWebsiteManager {
|
|||
|
||||
private slots: CoWebsiteSlot[];
|
||||
|
||||
private resizeObserver = new ResizeObserver(entries => {
|
||||
private resizeObserver = new ResizeObserver((entries) => {
|
||||
this.resizeAllIframes();
|
||||
});
|
||||
|
||||
|
@ -108,23 +109,23 @@ class CoWebsiteManager {
|
|||
this.slots = [
|
||||
{
|
||||
container: this.cowebsiteMainDom,
|
||||
position: 0
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>('cowebsite-slot-1'),
|
||||
position: 1
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite-slot-1"),
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>('cowebsite-slot-2'),
|
||||
position: 2
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite-slot-2"),
|
||||
position: 2,
|
||||
},
|
||||
{
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>('cowebsite-slot-3'),
|
||||
position: 3
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite-slot-3"),
|
||||
position: 3,
|
||||
},
|
||||
{
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>('cowebsite-slot-4'),
|
||||
position: 4
|
||||
container: HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite-slot-4"),
|
||||
position: 4,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -163,8 +164,10 @@ class CoWebsiteManager {
|
|||
}
|
||||
|
||||
private isSmallScreen(): boolean {
|
||||
return window.matchMedia("(max-aspect-ratio: 1/1)").matches ||
|
||||
window.matchMedia("(max-width:960px) and (max-height:768px)").matches;
|
||||
return (
|
||||
window.matchMedia("(max-aspect-ratio: 1/1)").matches ||
|
||||
window.matchMedia("(max-width:960px) and (max-height:768px)").matches
|
||||
);
|
||||
}
|
||||
|
||||
private initResizeListeners(touchMode: boolean) {
|
||||
|
@ -235,12 +238,12 @@ class CoWebsiteManager {
|
|||
|
||||
private initActionsListeners() {
|
||||
this.slots.forEach((slot: CoWebsiteSlot) => {
|
||||
const expandButton = slot.container.querySelector('.expand');
|
||||
const highlightButton = slot.container.querySelector('.hightlight');
|
||||
const closeButton = slot.container.querySelector('.close');
|
||||
const expandButton = slot.container.querySelector(".expand");
|
||||
const highlightButton = slot.container.querySelector(".hightlight");
|
||||
const closeButton = slot.container.querySelector(".close");
|
||||
|
||||
if (expandButton) {
|
||||
expandButton.addEventListener('click', (event) => {
|
||||
expandButton.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
const coWebsite = this.getCoWebsiteByPosition(slot.position);
|
||||
|
||||
|
@ -253,7 +256,7 @@ class CoWebsiteManager {
|
|||
}
|
||||
|
||||
if (highlightButton) {
|
||||
highlightButton.addEventListener('click', (event) => {
|
||||
highlightButton.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
const coWebsite = this.getCoWebsiteByPosition(slot.position);
|
||||
|
||||
|
@ -266,7 +269,7 @@ class CoWebsiteManager {
|
|||
}
|
||||
|
||||
if (closeButton) {
|
||||
closeButton.addEventListener('click', (event) => {
|
||||
closeButton.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
const coWebsite = this.getCoWebsiteByPosition(slot.position);
|
||||
|
||||
|
@ -284,37 +287,37 @@ class CoWebsiteManager {
|
|||
return this.coWebsites;
|
||||
}
|
||||
|
||||
public getCoWebsiteById(coWebsiteId: string): CoWebsite|undefined {
|
||||
public getCoWebsiteById(coWebsiteId: string): CoWebsite | undefined {
|
||||
return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.iframe.id === coWebsiteId);
|
||||
}
|
||||
|
||||
private getSlotByPosition(position: number): CoWebsiteSlot|undefined {
|
||||
private getSlotByPosition(position: number): CoWebsiteSlot | undefined {
|
||||
return this.slots.find((slot: CoWebsiteSlot) => slot.position === position);
|
||||
}
|
||||
|
||||
private getCoWebsiteByPosition(position: number): CoWebsite|undefined {
|
||||
private getCoWebsiteByPosition(position: number): CoWebsite | undefined {
|
||||
return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.position === position);
|
||||
}
|
||||
|
||||
private setIframeOffset(coWebsite: CoWebsite, slot: CoWebsiteSlot) {
|
||||
const bounding = slot.container.getBoundingClientRect();
|
||||
|
||||
if (coWebsite.iframe.classList.contains('thumbnail')) {
|
||||
coWebsite.iframe.style.width = ((bounding.right - bounding.left) * 2) + 'px';
|
||||
coWebsite.iframe.style.height = ((bounding.bottom - bounding.top) * 2) + 'px';
|
||||
coWebsite.iframe.style.top = (bounding.top - (Math.floor(bounding.height * 0.5))) + 'px';
|
||||
coWebsite.iframe.style.left = (bounding.left - (Math.floor(bounding.width * 0.5))) + 'px';
|
||||
if (coWebsite.iframe.classList.contains("thumbnail")) {
|
||||
coWebsite.iframe.style.width = (bounding.right - bounding.left) * 2 + "px";
|
||||
coWebsite.iframe.style.height = (bounding.bottom - bounding.top) * 2 + "px";
|
||||
coWebsite.iframe.style.top = bounding.top - Math.floor(bounding.height * 0.5) + "px";
|
||||
coWebsite.iframe.style.left = bounding.left - Math.floor(bounding.width * 0.5) + "px";
|
||||
} else {
|
||||
coWebsite.iframe.style.top = bounding.top + 'px';
|
||||
coWebsite.iframe.style.left = bounding.left + 'px';
|
||||
coWebsite.iframe.style.width = (bounding.right - bounding.left) + 'px';
|
||||
coWebsite.iframe.style.height = (bounding.bottom - bounding.top) + 'px';
|
||||
coWebsite.iframe.style.top = bounding.top + "px";
|
||||
coWebsite.iframe.style.left = bounding.left + "px";
|
||||
coWebsite.iframe.style.width = bounding.right - bounding.left + "px";
|
||||
coWebsite.iframe.style.height = bounding.bottom - bounding.top + "px";
|
||||
}
|
||||
}
|
||||
|
||||
private resizeAllIframes() {
|
||||
this.coWebsites.forEach((coWebsite: CoWebsite) => {
|
||||
const slot = this.getSlotByPosition(coWebsite.position);
|
||||
const slot = this.getSlotByPosition(coWebsite.position);
|
||||
|
||||
if (slot) {
|
||||
this.setIframeOffset(coWebsite, slot);
|
||||
|
@ -333,38 +336,48 @@ class CoWebsiteManager {
|
|||
coWebsite.iframe.scrolling = newPosition === 0 || newPosition === 1 ? "yes" : "no";
|
||||
|
||||
if (newPosition === 0) {
|
||||
coWebsite.iframe.classList.add('main');
|
||||
coWebsite.iframe.classList.add("main");
|
||||
coWebsite.icon.style.display = "none";
|
||||
} else {
|
||||
coWebsite.iframe.classList.remove('main');
|
||||
coWebsite.iframe.classList.remove("main");
|
||||
coWebsite.icon.style.display = "flex";
|
||||
}
|
||||
|
||||
if (newPosition === 1) {
|
||||
coWebsite.iframe.classList.add('sub-main');
|
||||
coWebsite.iframe.classList.add("sub-main");
|
||||
} else {
|
||||
coWebsite.iframe.classList.remove('sub-main');
|
||||
coWebsite.iframe.classList.remove("sub-main");
|
||||
}
|
||||
|
||||
if (newPosition >= 2) {
|
||||
coWebsite.iframe.classList.add('thumbnail');
|
||||
coWebsite.iframe.classList.add("thumbnail");
|
||||
} else {
|
||||
coWebsite.iframe.classList.remove('thumbnail');
|
||||
coWebsite.iframe.classList.remove("thumbnail");
|
||||
}
|
||||
|
||||
coWebsite.position = newPosition;
|
||||
|
||||
if (oldSlot && !this.getCoWebsiteByPosition(oldSlot.position)) {
|
||||
oldSlot.container.style.display = 'none';
|
||||
oldSlot.container.style.display = "none";
|
||||
}
|
||||
|
||||
newSlot.container.style.display = 'block';
|
||||
this.displayCowebsiteContainer();
|
||||
|
||||
coWebsite.iframe.classList.remove('pixel');
|
||||
newSlot.container.style.display = "block";
|
||||
|
||||
coWebsite.iframe.classList.remove("pixel");
|
||||
|
||||
this.resizeAllIframes();
|
||||
}
|
||||
|
||||
private displayCowebsiteContainer() {
|
||||
if (this.coWebsites.find((cowebsite) => cowebsite.position > 0)) {
|
||||
this.cowebsiteContainerDom.style.display = "block";
|
||||
} else {
|
||||
this.cowebsiteContainerDom.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
private moveLeftPreviousCoWebsite(coWebsite: CoWebsite, newPosition: number) {
|
||||
const nextCoWebsite = this.getCoWebsiteByPosition(coWebsite.position + 1);
|
||||
|
||||
|
@ -384,11 +397,7 @@ class CoWebsiteManager {
|
|||
|
||||
this.moveCoWebsite(coWebsite, newPosition);
|
||||
|
||||
if (newPosition === 4 ||
|
||||
!currentCoWebsite ||
|
||||
currentCoWebsite.iframe.id === coWebsite.iframe.id
|
||||
)
|
||||
{
|
||||
if (newPosition === 4 || !currentCoWebsite || currentCoWebsite.iframe.id === coWebsite.iframe.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -411,26 +420,26 @@ class CoWebsiteManager {
|
|||
if (coWebsite.position > 0) {
|
||||
const slot = this.getSlotByPosition(coWebsite.position);
|
||||
if (slot) {
|
||||
slot.container.style.display = 'none';
|
||||
slot.container.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
const previousCoWebsite = this.coWebsites.find((coWebsiteToCheck: CoWebsite) =>
|
||||
coWebsite.position + 1 === coWebsiteToCheck.position
|
||||
const previousCoWebsite = this.coWebsites.find(
|
||||
(coWebsiteToCheck: CoWebsite) => coWebsite.position + 1 === coWebsiteToCheck.position
|
||||
);
|
||||
|
||||
if (previousCoWebsite) {
|
||||
this.moveLeftPreviousCoWebsite(previousCoWebsite, coWebsite.position);
|
||||
}
|
||||
|
||||
this.displayCowebsiteContainer();
|
||||
|
||||
coWebsite.icon.remove();
|
||||
coWebsite.iframe.remove();
|
||||
}
|
||||
|
||||
public searchJitsi(): CoWebsite|undefined {
|
||||
return this.coWebsites.find((coWebsite : CoWebsite) =>
|
||||
coWebsite.iframe.id.toLowerCase().includes('jitsi')
|
||||
);
|
||||
public searchJitsi(): CoWebsite | undefined {
|
||||
return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.iframe.id.toLowerCase().includes("jitsi"));
|
||||
}
|
||||
|
||||
private generateCoWebsiteIcon(iframe: HTMLIFrameElement): HTMLDivElement {
|
||||
|
@ -439,7 +448,7 @@ class CoWebsiteManager {
|
|||
icon.style.display = "none";
|
||||
|
||||
const iconImage = document.createElement("img");
|
||||
iconImage.src = `https://www.google.com/s2/favicons?sz=64&domain_url=${iframe.src}`;
|
||||
iconImage.src = `${ICON_URL}/icon?url=${iframe.src}&size=16..30..256`;
|
||||
const url = new URL(iframe.src);
|
||||
iconImage.alt = url.hostname;
|
||||
|
||||
|
@ -456,27 +465,30 @@ class CoWebsiteManager {
|
|||
widthPercent?: number,
|
||||
position?: number
|
||||
): Promise<CoWebsite> {
|
||||
return this.addCoWebsite(
|
||||
(iframeBuffer) => {
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.src = new URL(url, base).toString();
|
||||
|
||||
return this.addCoWebsite((iframeBuffer) => {
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.src = new URL(url, base).toString()
|
||||
if (allowPolicy) {
|
||||
iframe.allow = allowPolicy;
|
||||
}
|
||||
|
||||
if (allowPolicy) {
|
||||
iframe.allow = allowPolicy;
|
||||
}
|
||||
if (allowApi) {
|
||||
iframeListener.registerIframe(iframe);
|
||||
}
|
||||
|
||||
if (allowApi) {
|
||||
iframeListener.registerIframe(iframe);
|
||||
}
|
||||
iframeBuffer.appendChild(iframe);
|
||||
|
||||
iframeBuffer.appendChild(iframe);
|
||||
|
||||
return iframe;
|
||||
}, widthPercent, position);
|
||||
return iframe;
|
||||
},
|
||||
widthPercent,
|
||||
position
|
||||
);
|
||||
}
|
||||
|
||||
public async addCoWebsite(
|
||||
callback: (iframeBuffer: HTMLDivElement) => PromiseLike<HTMLIFrameElement>|HTMLIFrameElement,
|
||||
callback: (iframeBuffer: HTMLDivElement) => PromiseLike<HTMLIFrameElement> | HTMLIFrameElement,
|
||||
widthPercent?: number,
|
||||
position?: number
|
||||
): Promise<CoWebsite> {
|
||||
|
@ -484,10 +496,10 @@ class CoWebsiteManager {
|
|||
if (this.coWebsites.length < 1) {
|
||||
this.loadMain();
|
||||
} else if (this.coWebsites.length === 5) {
|
||||
throw new Error('Too many we')
|
||||
throw new Error("Too many we");
|
||||
}
|
||||
|
||||
Promise.resolve(callback(this.cowebsiteBufferDom)).then(iframe =>{
|
||||
Promise.resolve(callback(this.cowebsiteBufferDom)).then((iframe) => {
|
||||
iframe?.classList.add("pixel");
|
||||
|
||||
if (!iframe.id) {
|
||||
|
@ -533,14 +545,14 @@ class CoWebsiteManager {
|
|||
|
||||
setTimeout(() => {
|
||||
this.fire();
|
||||
position !== undefined ?
|
||||
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) :
|
||||
this.moveCoWebsite(coWebsite, coWebsite.position);
|
||||
position !== undefined
|
||||
? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position)
|
||||
: this.moveCoWebsite(coWebsite, coWebsite.position);
|
||||
}, animationTime);
|
||||
} else {
|
||||
position !== undefined ?
|
||||
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) :
|
||||
this.moveCoWebsite(coWebsite, coWebsite.position);
|
||||
position !== undefined
|
||||
? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position)
|
||||
: this.moveCoWebsite(coWebsite, coWebsite.position);
|
||||
}
|
||||
|
||||
return resolve(coWebsite);
|
||||
|
@ -583,8 +595,7 @@ class CoWebsiteManager {
|
|||
}
|
||||
|
||||
public closeCoWebsites(): Promise<void> {
|
||||
this.currentOperationPromise = this.currentOperationPromise
|
||||
.then(() => {
|
||||
this.currentOperationPromise = this.currentOperationPromise.then(() => {
|
||||
this.coWebsites.forEach((coWebsite: CoWebsite) => {
|
||||
this.closeCoWebsite(coWebsite);
|
||||
});
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
|
||||
&-container {
|
||||
position: absolute;
|
||||
display: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import MiniCssExtractPlugin from "mini-css-extract-plugin";
|
|||
import sveltePreprocess from "svelte-preprocess";
|
||||
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
|
||||
import NodePolyfillPlugin from "node-polyfill-webpack-plugin";
|
||||
import { POSTHOG_API_KEY, PROFILE_URL } from "./src/Enum/EnvironmentVariable";
|
||||
|
||||
const mode = process.env.NODE_ENV ?? "development";
|
||||
const buildNpmTypingsForApi = !!process.env.BUILD_TYPINGS;
|
||||
|
@ -193,6 +192,7 @@ module.exports = {
|
|||
ADMIN_URL: undefined,
|
||||
CONTACT_URL: null,
|
||||
PROFILE_URL: null,
|
||||
ICON_URL: null,
|
||||
DEBUG_MODE: null,
|
||||
STUN_SERVER: null,
|
||||
TURN_SERVER: null,
|
||||
|
@ -207,6 +207,8 @@ module.exports = {
|
|||
POSTHOG_API_KEY: null,
|
||||
POSTHOG_URL: null,
|
||||
NODE_ENV: mode,
|
||||
DISABLE_ANONYMOUS: false,
|
||||
OPID_LOGIN_SCREEN_PROVIDER: null,
|
||||
}),
|
||||
],
|
||||
} as Configuration & WebpackDevServer.Configuration;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue