Merge branch 'develop' into feature-options-menu

This commit is contained in:
Hanusiak Piotr 2022-01-25 10:33:30 +01:00
commit 69a2379e53
84 changed files with 1446 additions and 310 deletions

View file

@ -5,6 +5,7 @@
import { get } from "svelte/store";
import type { Unsubscriber } from "svelte/store";
import { onDestroy, onMount } from "svelte";
import LL from "../../i18n/i18n-svelte";
let HTMLAudioPlayer: HTMLAudioElement;
let audioPlayerVolumeIcon: HTMLElement;
@ -144,7 +145,7 @@
</div>
<div class="audio-manager-reduce-conversation">
<label>
reduce in conversations
{$LL.audio.manager.reduce()}
<input type="checkbox" bind:checked={decreaseWhileTalking} on:change={setDecrease} />
</label>
<section class="audio-manager-file">

View file

@ -5,6 +5,7 @@
import ChatElement from "./ChatElement.svelte";
import { afterUpdate, beforeUpdate, onMount } from "svelte";
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import LL from "../../i18n/i18n-svelte";
let listDom: HTMLElement;
let chatWindowElement: HTMLElement;
@ -45,7 +46,7 @@
<p class="close-icon" on:click={closeChat}>&times</p>
<section class="messagesList" bind:this={listDom}>
<ul>
<li><p class="system-text">Here is your chat history:</p></li>
<li><p class="system-text">{$LL.chat.intro()}</p></li>
{#each $chatMessagesStore as message, i}
<li><ChatElement {message} line={i} /></li>
{/each}

View file

@ -1,4 +1,5 @@
<script lang="ts">
import LL from "../../i18n/i18n-svelte";
import { chatMessagesStore, chatInputFocusStore } from "../../Stores/ChatStore";
export const handleForm = {
@ -27,7 +28,7 @@
<input
type="text"
bind:value={newMessageText}
placeholder="Enter your message..."
placeholder={$LL.chat.enter()}
on:focus={onFocus}
on:blur={onBlur}
bind:this={inputElement}

View file

@ -1,4 +1,5 @@
<script lang="ts">
import LL from "../../i18n/i18n-svelte";
import type { PlayerInterface } from "../../Phaser/Game/PlayerInterface";
import { requestVisitCardsStore } from "../../Stores/GameStore";
@ -12,8 +13,12 @@
</script>
<ul class="selectMenu" style="border-top: {player.color || 'whitesmoke'} 5px solid">
<li><button class="text-btn" disabled={!player.visitCardUrl} on:click={openVisitCard}>Visit card</button></li>
<li><button class="text-btn" disabled>Add friend</button></li>
<li>
<button class="text-btn" disabled={!player.visitCardUrl} on:click={openVisitCard}
>{$LL.chat.menu.visitCard()}</button
>
</li>
<li><button class="text-btn" disabled>{$LL.chat.menu.addFriend}</button></li>
</ul>
<style lang="scss">

View file

@ -2,6 +2,7 @@
import type { Game } from "../../Phaser/Game/Game";
import { CustomizeScene, CustomizeSceneName } from "../../Phaser/Login/CustomizeScene";
import { activeRowStore } from "../../Stores/CustomCharacterStore";
import LL from "../../i18n/i18n-svelte";
export let game: Game;
@ -34,7 +35,7 @@
<form class="customCharacterScene">
<section class="text-center">
<h2>Customize your WOKA</h2>
<h2>{$LL.woka.customWoka.title()}</h2>
</section>
<section class="action action-move">
<button
@ -53,26 +54,29 @@
<section class="action">
{#if $activeRowStore === 0}
<button type="submit" class="customCharacterSceneFormBack nes-btn" on:click|preventDefault={previousScene}
>Return</button
>{$LL.woka.customWoka.navigation.return()}</button
>
{/if}
{#if $activeRowStore !== 0}
<button type="submit" class="customCharacterSceneFormBack nes-btn" on:click|preventDefault={selectUp}
>Back <img src="resources/objects/arrow_up_black.png" alt="" /></button
>{$LL.woka.customWoka.navigation.back()}
<img src="resources/objects/arrow_up_black.png" alt="" /></button
>
{/if}
{#if $activeRowStore === 5}
<button
type="submit"
class="customCharacterSceneFormSubmit nes-btn is-primary"
on:click|preventDefault={finish}>Finish</button
on:click|preventDefault={finish}>{$LL.woka.customWoka.navigation.finish()}</button
>
{/if}
{#if $activeRowStore !== 5}
<button
type="submit"
class="customCharacterSceneFormSubmit nes-btn is-primary"
on:click|preventDefault={selectDown}>Next <img src="resources/objects/arrow_down.png" alt="" /></button
on:click|preventDefault={selectDown}
>{$LL.woka.customWoka.navigation.next()}
<img src="resources/objects/arrow_down.png" alt="" /></button
>
{/if}
</section>

View file

@ -13,6 +13,7 @@
import cinemaCloseImg from "../images/cinema-close.svg";
import cinemaImg from "../images/cinema.svg";
import microphoneImg from "../images/microphone.svg";
import LL from "../../i18n/i18n-svelte";
export let game: Game;
let selectedCamera: string | undefined = undefined;
@ -76,7 +77,7 @@
<form class="enableCameraScene" on:submit|preventDefault={submit}>
<section class="text-center">
<h2>Turn on your camera and microphone</h2>
<h2>{$LL.camera.enable.title()}</h2>
</section>
{#if $localStreamStore.type === "success" && $localStreamStore.stream}
<video class="myCamVideoSetup" use:srcObject={$localStreamStore.stream} autoplay muted playsinline />
@ -121,7 +122,7 @@
{/if}
</section>
<section class="action">
<button type="submit" class="nes-btn is-primary letsgo">Let's go!</button>
<button type="submit" class="nes-btn is-primary letsgo">{$LL.camera.enable.start()}</button>
</section>
</form>

View file

@ -4,13 +4,14 @@ vim: ft=typescript
<script lang="ts">
import { gameManager } from "../../Phaser/Game/GameManager";
import followImg from "../images/follow.svg";
import { followStateStore, followRoleStore, followUsersStore } from "../../Stores/FollowStore";
import LL from "../../i18n/i18n-svelte";
const gameScene = gameManager.getCurrentGameScene();
function name(userId: number): string | undefined {
return gameScene.MapPlayersByKey.get(userId)?.PlayerValue;
function name(userId: number): string {
const user = gameScene.MapPlayersByKey.get(userId);
return user ? user.PlayerValue : "";
}
function sendFollowRequest() {
@ -42,11 +43,15 @@ vim: ft=typescript
{#if $followStateStore === "requesting" && $followRoleStore === "follower"}
<div class="interact-menu nes-container is-rounded">
<section class="interact-menu-title">
<h2>Do you want to follow {name($followUsersStore[0])}?</h2>
<h2>{$LL.follow.interactMenu.title.follow({ leader: name($followUsersStore[0]) })}</h2>
</section>
<section class="interact-menu-action">
<button type="button" class="nes-btn is-success" on:click|preventDefault={acceptFollowRequest}>Yes</button>
<button type="button" class="nes-btn is-error" on:click|preventDefault={reset}>No</button>
<button type="button" class="nes-btn is-success" on:click|preventDefault={acceptFollowRequest}
>{$LL.follow.interactMenu.yes()}</button
>
<button type="button" class="nes-btn is-error" on:click|preventDefault={reset}
>{$LL.follow.interactMenu.no()}</button
>
</section>
</div>
{/if}
@ -54,20 +59,24 @@ vim: ft=typescript
{#if $followStateStore === "ending"}
<div class="interact-menu nes-container is-rounded">
<section class="interact-menu-title">
<h2>Interaction</h2>
<h2>{$LL.follow.interactMenu.title.interact()}</h2>
</section>
{#if $followRoleStore === "follower"}
<section class="interact-menu-question">
<p>Do you want to stop following {name($followUsersStore[0])}?</p>
<p>{$LL.follow.interactMenu.stop.follower({ leader: name($followUsersStore[0]) })}</p>
</section>
{:else if $followRoleStore === "leader"}
<section class="interact-menu-question">
<p>Do you want to stop leading the way?</p>
<p>{$LL.follow.interactMenu.stop.leader()}</p>
</section>
{/if}
<section class="interact-menu-action">
<button type="button" class="nes-btn is-success" on:click|preventDefault={reset}>Yes</button>
<button type="button" class="nes-btn is-error" on:click|preventDefault={abortEnding}>No</button>
<button type="button" class="nes-btn is-success" on:click|preventDefault={reset}
>{$LL.follow.interactMenu.yes()}</button
>
<button type="button" class="nes-btn is-error" on:click|preventDefault={abortEnding}
>{$LL.follow.interactMenu.no()}</button
>
</section>
</div>
{/if}
@ -76,18 +85,24 @@ vim: ft=typescript
<div class="interact-status nes-container is-rounded">
<section class="interact-status">
{#if $followRoleStore === "follower"}
<p>Following {name($followUsersStore[0])}</p>
<p>{$LL.follow.interactStatus.following({ leader: name($followUsersStore[0]) })}</p>
{:else if $followUsersStore.length === 0}
<p>Waiting for followers' confirmation</p>
<p>{$LL.follow.interactStatus.waitingFollowers()}</p>
{:else if $followUsersStore.length === 1}
<p>{name($followUsersStore[0])} is following you</p>
<p>{$LL.follow.interactStatus.followed.one({ follower: name($followUsersStore[0]) })}</p>
{:else if $followUsersStore.length === 2}
<p>{name($followUsersStore[0])} and {name($followUsersStore[1])} are following you</p>
<p>
{$LL.follow.interactStatus.followed.two({
firstFollower: name($followUsersStore[0]),
secondFollower: name($followUsersStore[1]),
})}
</p>
{:else}
<p>
{$followUsersStore.slice(0, -1).map(name).join(", ")} and {name(
$followUsersStore[$followUsersStore.length - 1]
)} are following you
{$LL.follow.interactStatus.followed.many({
followers: $followUsersStore.slice(0, -1).map(name).join(", "),
lastFollower: name($followUsersStore[$followUsersStore.length - 1]),
})}
</p>
{/if}
</section>

View file

@ -4,6 +4,7 @@
import firefoxImg from "./images/help-setting-camera-permission-firefox.png";
import chromeImg from "./images/help-setting-camera-permission-chrome.png";
import { getNavigatorType, isAndroid as isAndroidFct, NavigatorType } from "../../WebRtc/DeviceUtils";
import LL from "../../i18n/i18n-svelte";
let isAndroid = isAndroidFct();
let isFirefox = getNavigatorType() === NavigatorType.firefox;
@ -24,14 +25,13 @@
transition:fly={{ y: -900, duration: 500 }}
>
<section>
<h2>Camera / Microphone access needed</h2>
<p class="err">Permission denied</p>
<p>You must allow camera and microphone access in your browser.</p>
<h2>{$LL.camera.help.title()}</h2>
<p class="err">{$LL.camera.help.permissionDenied()}</p>
<p>{$LL.camera.help.content()}</p>
<p>
{#if isFirefox}
<p class="err">
Please click the "Remember this decision" checkbox, if you don't want Firefox to keep asking you the
authorization.
{$LL.camera.help.firefoxContent()}
</p>
<img src={firefoxImg} alt="" />
{:else if isChrome && !isAndroid}
@ -40,9 +40,11 @@
</p>
</section>
<section>
<button class="helpCameraSettingsFormRefresh nes-btn" on:click|preventDefault={refresh}>Refresh</button>
<button class="helpCameraSettingsFormRefresh nes-btn" on:click|preventDefault={refresh}
>{$LL.camera.help.refresh()}</button
>
<button type="submit" class="helpCameraSettingsFormContinue nes-btn is-primary" on:click|preventDefault={close}
>Continue without webcam</button
>{$LL.camera.help.continue()}</button
>
</section>
</form>

View file

@ -4,6 +4,7 @@
import { DISPLAY_TERMS_OF_USE, MAX_USERNAME_LENGTH } from "../../Enum/EnvironmentVariable";
import logoImg from "../images/logo.png";
import { gameManager } from "../../Phaser/Game/GameManager";
import LL from "../../i18n/i18n-svelte";
export let game: Game;
@ -27,7 +28,7 @@
<img src={logoImg} alt="WorkAdventure logo" />
</section>
<section class="text-center">
<h2>Enter your name</h2>
<h2>{$LL.login.input.name.placeholder()}</h2>
</section>
<!-- svelte-ignore a11y-autofocus -->
<input
@ -44,22 +45,20 @@
/>
<section class="error-section">
{#if name.trim() === "" && startValidating}
<p class="err">The name is empty</p>
<p class="err">{$LL.login.input.name.empty()}</p>
{/if}
</section>
{#if DISPLAY_TERMS_OF_USE}
<section class="terms-and-conditions">
<a style="display: none;" href="traduction">Need for traduction</a>
<p>
By continuing, you are agreeing our <a href="https://workadventu.re/terms-of-use" target="_blank"
>terms of use</a
>, <a href="https://workadventu.re/privacy-policy" target="_blank">privacy policy</a> and
<a href="https://workadventu.re/cookie-policy" target="_blank">cookie policy</a>.
{$LL.login.terms()}
</p>
</section>
{/if}
<section class="action">
<button type="submit" class="nes-btn is-primary loginSceneFormSubmit">Continue</button>
<button type="submit" class="nes-btn is-primary loginSceneFormSubmit">{$LL.login.continue()}</button>
</section>
</form>

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { gameManager } from "../../Phaser/Game/GameManager";
import { onMount } from "svelte";
import LL from "../../i18n/i18n-svelte";
let gameScene = gameManager.getCurrentGameScene();
@ -11,7 +12,7 @@
let mapName: string = "";
let mapLink: string = "";
let mapDescription: string = "";
let mapCopyright: string = "The map creator did not declare a copyright for the map.";
let mapCopyright: string = $LL.menu.about.copyrights.map.empty();
let tilesetCopyright: string[] = [];
let audioCopyright: string[] = [];
@ -62,41 +63,37 @@
</script>
<div class="about-room-main">
<h2>Information on the map</h2>
<h2>{$LL.menu.about.mapInfo()}</h2>
<section class="container-overflow">
<h3>{mapName}</h3>
<p class="string-HTML">{mapDescription}</p>
{#if mapLink}
<p class="string-HTML">&gt; <a href={mapLink} target="_blank">link to this map</a> &lt;</p>
<p class="string-HTML">
&gt; <a href={mapLink} target="_blank">{$LL.menu.about.mapLink()}</a> &lt;
</p>
{/if}
<h3 class="nes-pointer hoverable" on:click={() => (expandedMapCopyright = !expandedMapCopyright)}>
Copyrights of the map
{$LL.menu.about.copyrights.map.title()}
</h3>
<p class="string-HTML" hidden={!expandedMapCopyright}>{mapCopyright}</p>
<h3 class="nes-pointer hoverable" on:click={() => (expandedTilesetCopyright = !expandedTilesetCopyright)}>
Copyrights of the tilesets
{$LL.menu.about.copyrights.tileset.title()}
</h3>
<section hidden={!expandedTilesetCopyright}>
{#each tilesetCopyright as copyright}
<p class="string-HTML">{copyright}</p>
{:else}
<p>
The map creator did not declare a copyright for the tilesets. This doesn't mean that those tilesets
have no license.
</p>
<p>{$LL.menu.about.copyrights.tileset.empty()}</p>
{/each}
</section>
<h3 class="nes-pointer hoverable" on:click={() => (expandedAudioCopyright = !expandedAudioCopyright)}>
Copyrights of audio files
{$LL.menu.about.copyrights.audio.title()}
</h3>
<section hidden={!expandedAudioCopyright}>
{#each audioCopyright as copyright}
<p class="string-HTML">{copyright}</p>
{:else}
<p>
The map creator did not declare a copyright for audio files. This doesn't mean that those tilesets
have no license.
</p>
<p>{$LL.menu.about.copyrights.audio.empty()}</p>
{/each}
</section>
</section>

View file

@ -4,6 +4,7 @@
import { AdminMessageEventTypes } from "../../Connexion/AdminMessagesService";
import uploadFile from "../images/music-file.svg";
import type { PlayGlobalMessageInterface } from "../../Connexion/ConnexionModels";
import LL from "../../i18n/i18n-svelte";
interface EventTargetFiles extends EventTarget {
files: Array<File>;
@ -76,7 +77,7 @@
<img
class="nes-pointer"
src={uploadFile}
alt="Upload a file"
alt={$LL.menu.globalAudio.uploadInfo()}
on:click|preventDefault={() => {
fileInput.click();
}}
@ -85,7 +86,7 @@
<p>{fileName} : {fileSize}</p>
{/if}
{#if errorFile}
<p class="err">No file selected. You need to upload a file before sending it.</p>
<p class="err">{$LL.menu.globalAudio.error()}</p>
{/if}
<input
type="file"

View file

@ -1,4 +1,7 @@
<script lang="ts">
import LL from "../../i18n/i18n-svelte";
import { contactPageStore } from "../../Stores/MenuStore";
function goToGettingStarted() {
const sparkHost = "https://workadventu.re/getting-started";
window.open(sparkHost, "_blank");
@ -8,25 +11,24 @@
const sparkHost = "https://workadventu.re/map-building/";
window.open(sparkHost, "_blank");
}
import { contactPageStore } from "../../Stores/MenuStore";
</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>
<h3>{$LL.menu.contact.gettingStarted.title()}</h3>
<p>{$LL.menu.contact.gettingStarted.description()}</p>
<button type="button" class="nes-btn is-primary" on:click={goToGettingStarted}
>{$LL.menu.contact.gettingStarted.title()}</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>
<h3>{$LL.menu.contact.createMap.title()}</h3>
<p>{$LL.menu.contact.createMap.description()}</p>
<button type="button" class="nes-btn" on:click={goToBuildingMap}
>{$LL.menu.contact.createMap.title()}</button
>
</section>
<iframe

View file

@ -1,6 +1,7 @@
<script lang="ts">
import TextGlobalMessage from "./TextGlobalMessage.svelte";
import AudioGlobalMessage from "./AudioGlobalMessage.svelte";
import LL from "../../i18n/i18n-svelte";
let handleSendText: { sendTextMessage(broadcast: boolean): void };
let handleSendAudio: { sendAudioMessage(broadcast: boolean): Promise<void> };
@ -35,14 +36,14 @@
<button
type="button"
class="nes-btn {inputSendTextActive ? 'is-disabled' : ''}"
on:click|preventDefault={activateInputText}>Text</button
on:click|preventDefault={activateInputText}>{$LL.menu.globalMessage.text()}</button
>
</section>
<section>
<button
type="button"
class="nes-btn {uploadAudioActive ? 'is-disabled' : ''}"
on:click|preventDefault={activateUploadAudio}>Audio</button
on:click|preventDefault={activateUploadAudio}>{$LL.menu.globalMessage.audio()}</button
>
</section>
</div>
@ -57,10 +58,10 @@
<div class="global-message-footer">
<label>
<input type="checkbox" class="nes-checkbox is-dark nes-pointer" bind:checked={broadcastToWorld} />
<span>Broadcast to all rooms of the world</span>
<span>{$LL.menu.globalMessage.warning()}</span>
</label>
<section>
<button class="nes-btn is-primary" on:click|preventDefault={send}>Send</button>
<button class="nes-btn is-primary" on:click|preventDefault={send}>{$LL.menu.globalMessage.send()}</button>
</section>
</div>
</div>

View file

@ -1,4 +1,6 @@
<script lang="ts">
import LL from "../../i18n/i18n-svelte";
function copyLink() {
const input: HTMLInputElement = document.getElementById("input-share-link") as HTMLInputElement;
input.focus();
@ -21,14 +23,14 @@
<div class="guest-main">
<section class="container-overflow">
<section class="share-url not-mobile">
<h3>Share the link of the room!</h3>
<h3>{$LL.menu.invite.description()}</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>
<button type="button" class="nes-btn is-primary" on:click={copyLink}>{$LL.menu.invite.copy()}</button>
</section>
<section class="is-mobile">
<h3>Share the link of the room!</h3>
<h3>{$LL.menu.invite.description()}</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>
<button type="button" class="nes-btn is-primary" on:click={shareLink}>{$LL.menu.invite.share()}</button>
</section>
</section>
</div>

View file

@ -14,26 +14,27 @@
SubMenusInterface,
subMenusStore,
} from "../../Stores/MenuStore";
import type { MenuItem } from "../../Stores/MenuStore";
import { onDestroy, onMount } from "svelte";
import { get } from "svelte/store";
import type { Unsubscriber } from "svelte/store";
import { sendMenuClickedEvent } from "../../Api/iframe/Ui/MenuItem";
import LL from "../../i18n/i18n-svelte";
let activeSubMenu: string = SubMenusInterface.profile;
let activeSubMenu: MenuItem = $subMenusStore[0];
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.profile);
if (!$subMenusStore.includes(activeSubMenu)) {
switchMenu($subMenusStore[0]);
}
});
checkSubMenuToShow();
switchMenu(SubMenusInterface.profile);
switchMenu($subMenusStore[0]);
});
onDestroy(() => {
@ -42,10 +43,10 @@
}
});
function switchMenu(menu: string) {
if (get(subMenusStore).find((subMenu) => subMenu === menu)) {
function switchMenu(menu: MenuItem) {
if (menu.type === "translated") {
activeSubMenu = menu;
switch (menu) {
switch (menu.key) {
case SubMenusInterface.settings:
activeComponent = SettingsSubMenu;
break;
@ -64,36 +65,46 @@
case SubMenusInterface.contact:
activeComponent = ContactSubMenu;
break;
default: {
const customMenu = customMenuIframe.get(menu);
if (customMenu !== undefined) {
props = { url: customMenu.url, allowApi: customMenu.allowApi };
activeComponent = CustomSubMenu;
} else {
sendMenuClickedEvent(menu);
menuVisiblilityStore.set(false);
}
break;
}
}
} else throw new Error("There is no menu called " + menu);
} else {
const customMenu = customMenuIframe.get(menu.label);
if (customMenu !== undefined) {
props = { url: customMenu.url, allowApi: customMenu.allowApi };
activeComponent = CustomSubMenu;
} else {
sendMenuClickedEvent(menu.label);
menuVisiblilityStore.set(false);
}
}
}
function closeMenu() {
menuVisiblilityStore.set(false);
}
function onKeyDown(e: KeyboardEvent) {
if (e.key === "Escape") {
closeMenu();
}
}
function translateMenuName(menu: MenuItem) {
if (menu.type === "scripting") {
return menu.label;
}
// Bypass the proxy of typesafe for getting the menu name : https://github.com/ivanhofer/typesafe-i18n/issues/156
const getMenuName = $LL.menu.sub[menu.key];
return getMenuName();
}
</script>
<svelte:window on:keydown={onKeyDown} />
<div class="menu-container-main">
<div class="menu-nav-sidebar nes-container is-rounded" transition:fly={{ x: -1000, duration: 500 }}>
<h2>Menu</h2>
<h2>{$LL.menu.title()}</h2>
<nav>
{#each $subMenusStore as submenu}
<button
@ -101,14 +112,14 @@
class="nes-btn {activeSubMenu === submenu ? 'is-disabled' : ''}"
on:click|preventDefault={() => switchMenu(submenu)}
>
{submenu}
{translateMenuName(submenu)}
</button>
{/each}
</nav>
</div>
<div class="menu-submenu-container nes-container is-rounded" transition:fly={{ y: -1000, duration: 500 }}>
<button type="button" class="nes-btn is-error close" on:click={closeMenu}>&times</button>
<h2>{activeSubMenu}</h2>
<h2>{translateMenuName(activeSubMenu)}</h2>
<svelte:component this={activeComponent} {...props} />
</div>
</div>

View file

@ -9,6 +9,7 @@
import { get } from "svelte/store";
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
import { showShareLinkMapModalStore } from "../../Stores/ModalStore";
import LL from "../../i18n/i18n-svelte";
function showMenu() {
menuVisiblilityStore.set(!get(menuVisiblilityStore));
@ -29,11 +30,21 @@
<main class="menuIcon">
{#if $limitMapStore}
<img src={logoInvite} alt="open menu" class="nes-pointer" on:click|preventDefault={showInvite} />
<img src={logoRegister} alt="open menu" class="nes-pointer" on:click|preventDefault={register} />
<img
src={logoInvite}
alt={$LL.menu.icon.open.invite()}
class="nes-pointer"
on:click|preventDefault={showInvite}
/>
<img
src={logoRegister}
alt={$LL.menu.icon.open.register()}
class="nes-pointer"
on:click|preventDefault={register}
/>
{:else}
<img src={logoWA} alt="open menu" class="nes-pointer" on:click|preventDefault={showMenu} />
<img src={logoTalk} alt="open menu" class="nes-pointer" on:click|preventDefault={showChat} />
<img src={logoWA} alt={$LL.menu.icon.open.menu()} class="nes-pointer" on:click|preventDefault={showMenu} />
<img src={logoTalk} alt={$LL.menu.icon.open.chat()} class="nes-pointer" on:click|preventDefault={showChat} />
{/if}
</main>

View file

@ -17,6 +17,7 @@
import btnProfileSubMenuCompanion from "../images/btn-menu-profile-companion.svg";
import Woka from "../Woka/Woka.svelte";
import Companion from "../Companion/Companion.svelte";
import LL from "../../i18n/i18n-svelte";
function disableMenuStores() {
menuVisiblilityStore.set(false);
@ -62,20 +63,20 @@
<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>
<img src={btnProfileSubMenuIdentity} alt={$LL.menu.profile.edit.name()} />
<span class="btn-hover">{$LL.menu.profile.edit.name()}</span>
</button>
<button type="button" class="nes-btn" on:click|preventDefault={openEditSkinScene}>
<Woka userId={-1} placeholderSrc="" width="26px" height="26px" />
<span class="btn-hover">Edit your WOKA</span>
<span class="btn-hover">{$LL.menu.profile.edit.woka()}</span>
</button>
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>
<Companion userId={-1} placeholderSrc={btnProfileSubMenuCompanion} width="26px" height="26px" />
<span class="btn-hover">Edit your companion</span>
<span class="btn-hover">{$LL.menu.profile.edit.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>
<img src={btnProfileSubMenuCamera} alt={$LL.menu.profile.edit.camera()} />
<span class="btn-hover">{$LL.menu.profile.edit.camera()}</span>
</button>
</section>
</div>
@ -88,11 +89,13 @@
{/if}
</section>
<section>
<button type="button" class="nes-btn" on:click|preventDefault={logOut}>Log out</button>
<button type="button" class="nes-btn" on:click|preventDefault={logOut}
>{$LL.menu.profile.logout()}</button
>
</section>
{:else}
<section>
<a type="button" class="nes-btn" href="/login">Sign in</a>
<a type="button" class="nes-btn" href="/login">{$LL.menu.profile.login()}</a>
</section>
{/if}
</div>

View file

@ -4,6 +4,9 @@
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { isMobile } from "../../Enum/EnvironmentVariable";
import { menuVisiblilityStore } from "../../Stores/MenuStore";
import LL, { locale } from "../../i18n/i18n-svelte";
import type { Locales } from "../../i18n/i18n-types";
import { displayableLocales, setCurrentLocale } from "../../i18n/locales";
let fullscreen: boolean = localUserStore.getFullscreen();
let notification: boolean = localUserStore.getNotification() === "granted";
@ -11,14 +14,17 @@
let ignoreFollowRequests: boolean = localUserStore.getIgnoreFollowRequests();
let valueGame: number = localUserStore.getGameQualityValue();
let valueVideo: number = localUserStore.getVideoQualityValue();
let valueLocale: string = $locale;
let previewValueGame = valueGame;
let previewValueVideo = valueVideo;
let previewValueLocale = valueLocale;
function saveSetting() {
if (valueGame !== previewValueGame) {
previewValueGame = valueGame;
localUserStore.setGameQualityValue(valueGame);
window.location.reload();
let change = false;
if (valueLocale !== previewValueLocale) {
previewValueLocale = valueLocale;
setCurrentLocale(valueLocale as Locales);
}
if (valueVideo !== previewValueVideo) {
@ -26,6 +32,16 @@
videoConstraintStore.setFrameRate(valueVideo);
}
if (valueGame !== previewValueGame) {
previewValueGame = valueGame;
localUserStore.setGameQualityValue(valueGame);
change = true;
}
if (change) {
window.location.reload();
}
closeMenu();
}
@ -73,34 +89,74 @@
<div class="settings-main" on:submit|preventDefault={saveSetting}>
<section>
<h3>Game quality</h3>
<h3>{$LL.menu.settings.gameQuality.title()}</h3>
<div class="nes-select is-dark">
<select bind:value={valueGame}>
<option value={120}>{isMobile() ? "High (120 fps)" : "High video quality (120 fps)"}</option>
<option value={60}
>{isMobile() ? "Medium (60 fps)" : "Medium video quality (60 fps, recommended)"}</option
<option value={120}
>{isMobile()
? $LL.menu.settings.gameQuality.short.high()
: $LL.menu.settings.gameQuality.long.high()}</option
>
<option value={60}
>{isMobile()
? $LL.menu.settings.gameQuality.short.medium()
: $LL.menu.settings.gameQuality.long.medium()}</option
>
<option value={40}
>{isMobile()
? $LL.menu.settings.gameQuality.short.minimum()
: $LL.menu.settings.gameQuality.long.minimum()}</option
>
<option value={20}
>{isMobile()
? $LL.menu.settings.gameQuality.short.small()
: $LL.menu.settings.gameQuality.long.small()}</option
>
<option value={40}>{isMobile() ? "Minimum (40 fps)" : "Minimum video quality (40 fps)"}</option>
<option value={20}>{isMobile() ? "Small (20 fps)" : "Small video quality (20 fps)"}</option>
</select>
</div>
</section>
<section>
<h3>Video quality</h3>
<h3>{$LL.menu.settings.videoQuality.title()}</h3>
<div class="nes-select is-dark">
<select bind:value={valueVideo}>
<option value={30}>{isMobile() ? "High (30 fps)" : "High video quality (30 fps)"}</option>
<option value={20}
>{isMobile() ? "Medium (20 fps)" : "Medium video quality (20 fps, recommended)"}</option
<option value={30}
>{isMobile()
? $LL.menu.settings.videoQuality.short.high()
: $LL.menu.settings.videoQuality.long.high()}</option
>
<option value={10}>{isMobile() ? "Minimum (10 fps)" : "Minimum video quality (10 fps)"}</option>
<option value={5}>{isMobile() ? "Small (5 fps)" : "Small video quality (5 fps)"}</option>
<option value={20}
>{isMobile()
? $LL.menu.settings.videoQuality.short.medium()
: $LL.menu.settings.videoQuality.long.medium()}</option
>
<option value={10}
>{isMobile()
? $LL.menu.settings.videoQuality.short.minimum()
: $LL.menu.settings.videoQuality.long.minimum()}</option
>
<option value={5}
>{isMobile()
? $LL.menu.settings.videoQuality.short.small()
: $LL.menu.settings.videoQuality.long.small()}</option
>
</select>
</div>
</section>
<section>
<h3>{$LL.menu.settings.language.title()}</h3>
<div class="nes-select is-dark">
<select class="languages-switcher" bind:value={valueLocale}>
{#each displayableLocales as locale (locale.id)}
<option value={locale.id}>{`${locale.language} (${locale.country})`}</option>
{/each}
</select>
</div>
</section>
<section class="settings-section-save">
<p>(Saving these settings will restart the game)</p>
<button type="button" class="nes-btn is-primary" on:click|preventDefault={saveSetting}>Save</button>
<p>{$LL.menu.settings.save.warning()}</p>
<button type="button" class="nes-btn is-primary" on:click|preventDefault={saveSetting}
>{$LL.menu.settings.save.button()}</button
>
</section>
<section class="settings-section-noSaveOption">
<label>
@ -110,7 +166,7 @@
bind:checked={fullscreen}
on:change={changeFullscreen}
/>
<span>Fullscreen</span>
<span>{$LL.menu.settings.fullscreen()}</span>
</label>
<label>
<input
@ -119,7 +175,7 @@
bind:checked={notification}
on:change={changeNotification}
/>
<span>Notifications</span>
<span>{$LL.menu.settings.notifications()}</span>
</label>
<label>
<input
@ -128,7 +184,7 @@
bind:checked={forceCowebsiteTrigger}
on:change={changeForceCowebsiteTrigger}
/>
<span>Always ask before opening websites and Jitsi Meet rooms</span>
<span>{$LL.menu.settings.cowebsiteTrigger()}</span>
</label>
<label>
<input
@ -137,7 +193,7 @@
bind:checked={ignoreFollowRequests}
on:change={changeIgnoreFollowRequests}
/>
<span>Ignore requests to follow other users</span>
<span>{$LL.menu.settings.ignoreFollowRequest()}</span>
</label>
</section>
</div>
@ -174,6 +230,10 @@
margin: 0 0 15px;
}
}
.languages-switcher option {
text-transform: capitalize;
}
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {

View file

@ -5,6 +5,7 @@
import { AdminMessageEventTypes } from "../../Connexion/AdminMessagesService";
import type { Quill } from "quill";
import type { PlayGlobalMessageInterface } from "../../Connexion/ConnexionModels";
import LL from "../../i18n/i18n-svelte";
//toolbar
const toolbarOptions = [
@ -58,7 +59,7 @@
const { default: Quill } = await import("quill"); // eslint-disable-line @typescript-eslint/no-explicit-any
quill = new Quill(QUILL_EDITOR, {
placeholder: "Enter your message here...",
placeholder: $LL.menu.globalMessage.enter(),
theme: "snow",
modules: {
toolbar: toolbarOptions,

View file

@ -4,6 +4,7 @@
import SoundMeterWidget from "./SoundMeterWidget.svelte";
import { onDestroy } from "svelte";
import { srcObject } from "./Video/utils";
import LL from "../i18n/i18n-svelte";
let stream: MediaStream | null;
@ -32,5 +33,5 @@
<SoundMeterWidget {stream} />
{/if}
</div>
<div class="is-silent" class:hide={isSilent}>Silent zone</div>
<div class="is-silent" class:hide={isSilent}>{$LL.camera.my.silentZone()}</div>
</div>

View file

@ -2,6 +2,7 @@
import { blackListManager } from "../../WebRtc/BlackListManager";
import { showReportScreenStore, userReportEmpty } from "../../Stores/ShowReportScreenStore";
import { onMount } from "svelte";
import LL from "../../i18n/i18n-svelte";
export let userUUID: string | undefined;
export let userName: string;
@ -29,10 +30,10 @@
</script>
<div class="block-container">
<h3>Block</h3>
<p>Block any communication from and to {userName}. This can be reverted.</p>
<h3>{$LL.report.block.title()}</h3>
<p>{$LL.report.block.content({ userName })}</p>
<button type="button" class="nes-btn is-error" on:click|preventDefault={blockUser}>
{userIsBlocked ? "Unblock this user" : "Block this user"}
{userIsBlocked ? $LL.report.block.unblock() : $LL.report.block.block()}
</button>
</div>

View file

@ -7,6 +7,7 @@
import { playersStore } from "../../Stores/PlayersStore";
import { connectionManager } from "../../Connexion/ConnectionManager";
import { get } from "svelte/store";
import LL from "../../i18n/i18n-svelte";
let blockActive = true;
let reportActive = !blockActive;
@ -59,7 +60,7 @@
<div class="report-menu-main nes-container is-rounded">
<section class="report-menu-title">
<h2>Moderate {userName}</h2>
<h2>{$LL.report.moderate.title({ userName })}</h2>
<section class="justify-center">
<button type="button" class="nes-btn" on:click|preventDefault={close}>X</button>
</section>
@ -69,14 +70,14 @@
<button
type="button"
class="nes-btn {blockActive ? 'is-disabled' : ''}"
on:click|preventDefault={activateBlock}>Block</button
on:click|preventDefault={activateBlock}>{$LL.report.moderate.block()}</button
>
</section>
<section class="justify-center">
<button
type="button"
class="nes-btn {reportActive ? 'is-disabled' : ''}"
on:click|preventDefault={activateReport}>Report</button
on:click|preventDefault={activateReport}>{$LL.report.moderate.report()}</button
>
</section>
</section>
@ -86,7 +87,7 @@
{:else if reportActive}
<ReportSubMenu {userUUID} />
{:else}
<p>ERROR : There is no action selected.</p>
<p>{$LL.report.moderate.noSelect()}</p>
{/if}
</section>
</div>

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { showReportScreenStore, userReportEmpty } from "../../Stores/ShowReportScreenStore";
import { gameManager } from "../../Phaser/Game/GameManager";
import LL from "../../i18n/i18n-svelte";
export let userUUID: string | undefined;
let reportMessage: string;
@ -22,18 +23,18 @@
</script>
<div class="report-container-main">
<h3>Report</h3>
<p>Send a report message to the administrators of this room. They may later ban this user.</p>
<h3>{$LL.report.title()}</h3>
<p>{$LL.report.content()}</p>
<form>
<section>
<label>
<span>Your message: </span>
<span>{$LL.report.message.title()}</span>
<textarea type="text" class="nes-textarea" bind:value={reportMessage} />
</label>
<p hidden={hiddenError}>Report message cannot to be empty.</p>
<p hidden={hiddenError}>{$LL.report.message.empty()}</p>
</section>
<section>
<button type="submit" class="nes-btn is-error" on:click={submitReport}>Report this user</button>
<button type="submit" class="nes-btn is-error" on:click={submitReport}>{$LL.report.submit()}</button>
</section>
</form>
</div>

View file

@ -1,4 +1,5 @@
<script lang="typescript">
import LL from "../../i18n/i18n-svelte";
import type { Game } from "../../Phaser/Game/Game";
import { SelectCompanionScene, SelectCompanionSceneName } from "../../Phaser/Login/SelectCompanionScene";
@ -25,7 +26,7 @@
<form class="selectCompanionScene">
<section class="text-center">
<h2>Select your companion</h2>
<h2>{$LL.companion.select.title()}</h2>
<button class="selectCharacterButton selectCharacterButtonLeft nes-btn" on:click|preventDefault={selectLeft}>
&lt;
</button>
@ -35,12 +36,12 @@
</section>
<section class="action">
<button href="/" class="selectCompanionSceneFormBack nes-btn" on:click|preventDefault={noCompanion}
>No companion</button
>{$LL.companion.select.any()}</button
>
<button
type="submit"
class="selectCompanionSceneFormSubmit nes-btn is-primary"
on:click|preventDefault={selectCompanion}>Continue</button
on:click|preventDefault={selectCompanion}>{$LL.companion.select.continue()}</button
>
</section>
</form>

View file

@ -3,6 +3,7 @@
import { onMount } from "svelte";
import type { Message } from "../../Stores/TypeMessageStore/MessageStore";
import { banMessageStore } from "../../Stores/TypeMessageStore/BanMessageStore";
import LL from "../../i18n/i18n-svelte";
export let message: Message;
@ -37,7 +38,8 @@
out:fade={{ duration: 200 }}
>
<h2 class="title-ban-message">
<img src="resources/logos/report.svg" alt="***" /> Important message
<img src="resources/logos/report.svg" alt="***" />
{$LL.warning.importantMessage()}
<img src="resources/logos/report.svg" alt="***" />
</h2>
<div class="content-ban-message">

View file

@ -3,6 +3,7 @@
import megaphoneImg from "./images/megaphone.svg";
import { soundPlayingStore } from "../../Stores/SoundPlayingStore";
import { afterUpdate } from "svelte";
import LL from "../../i18n/i18n-svelte";
export let url: string;
let audio: HTMLAudioElement;
@ -18,7 +19,7 @@
<div class="audio-playing" transition:fly={{ x: 210, duration: 500 }}>
<img src={megaphoneImg} alt="Audio playing" />
<p>Audio message</p>
<p>{$LL.audio.message()}</p>
<audio bind:this={audio} src={url} on:ended={soundEnded}>
<track kind="captions" />
</audio>

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { errorStore, hasClosableMessagesInErrorStore } from "../../Stores/ErrorStore";
import LL from "../../i18n/i18n-svelte";
function close(): boolean {
errorStore.clearClosableMessages();
@ -8,7 +9,7 @@
</script>
<div class="error-div nes-container is-dark is-rounded" open>
<p class="nes-text is-error title">Error</p>
<p class="nes-text is-error title">{$LL.error.error()}</p>
<div class="body">
{#each $errorStore as error}
<p>{error.message}</p>

View file

@ -2,6 +2,7 @@
import { fly } from "svelte/transition";
import { requestVisitCardsStore } from "../../Stores/GameStore";
import { onMount } from "svelte";
import LL from "../../i18n/i18n-svelte";
export let visitCardUrl: string;
let w = "500px";
@ -40,7 +41,7 @@
/>
{#if !hidden}
<div class="buttonContainer">
<button class="nes-btn is-popUpElement" on:click={closeCard}>Close</button>
<button class="nes-btn is-popUpElement" on:click={closeCard}>{$LL.menu.visitCard.close()}</button>
</div>
{/if}
</section>

View file

@ -2,6 +2,7 @@
import { fly } from "svelte/transition";
import { userIsAdminStore, limitMapStore } from "../../Stores/GameStore";
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
import LL from "../../i18n/i18n-svelte";
const upgradeLink = ADMIN_URL + "/pricing";
const registerLink = ADMIN_URL + "/second-step-register";
@ -9,19 +10,17 @@
<main class="warningMain" transition:fly={{ y: -200, duration: 500 }}>
{#if $userIsAdminStore}
<h2>Warning!</h2>
<h2>{$LL.warning.title()}</h2>
<p>
This world is close to its limit!. You can upgrade its capacity <a href={upgradeLink} target="_blank"
>here</a
>
{$LL.warning.content({ upgradeLink })}
</p>
{:else if $limitMapStore}
<p>
This map is available for 2 days. You can register your domain <a href={registerLink}>here</a>!
</p>
{:else}
<h2>Warning!</h2>
<p>This world is close to its limit!</p>
<h2>{$LL.warning.title()}</h2>
<p>{$LL.warning.limit()}</p>
{/if}
</main>

View file

@ -1,6 +1,7 @@
<script lang="typescript">
import type { Game } from "../../Phaser/Game/Game";
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
import LL from "../../i18n/i18n-svelte";
export let game: Game;
@ -25,7 +26,7 @@
<form class="selectCharacterScene">
<section class="text-center">
<h2>Select your WOKA</h2>
<h2>{$LL.woka.selectWoka.title()}</h2>
<button class="selectCharacterButton selectCharacterButtonLeft nes-btn" on:click|preventDefault={selectLeft}>
&lt;
</button>
@ -37,12 +38,12 @@
<button
type="submit"
class="selectCharacterSceneFormSubmit nes-btn is-primary"
on:click|preventDefault={cameraScene}>Continue</button
on:click|preventDefault={cameraScene}>{$LL.woka.selectWoka.continue()}</button
>
<button
type="submit"
class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn"
on:click|preventDefault={customizeScene}>Customize your WOKA</button
on:click|preventDefault={customizeScene}>{$LL.woka.selectWoka.customize()}</button
>
</section>
</form>