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

This commit is contained in:
_Bastler
2022-05-05 12:25:09 +02:00
68 changed files with 1375 additions and 280 deletions
+1 -3
View File
@@ -1,5 +1,3 @@
src/Messages/generated
src/Messages/JsonMessages
src/i18n/i18n-svelte.ts
src/i18n/i18n-types.ts
src/i18n/i18n-util.ts
src/i18n/i18n-*.ts
+1 -1
View File
@@ -1,5 +1,5 @@
{
"$schema": "https://unpkg.com/typesafe-i18n@2.59.0/schema/typesafe-i18n.json",
"$schema": "https://unpkg.com/typesafe-i18n@5.4.0/schema/typesafe-i18n.json",
"baseLocale": "en-US",
"adapter": "svelte"
}
+4 -2
View File
@@ -5,7 +5,7 @@
"license": "SEE LICENSE IN LICENSE.txt",
"devDependencies": {
"@geprog/vite-plugin-env-config": "^4.0.3",
"@home-based-studio/phaser3-utils": "^0.4.2",
"@home-based-studio/phaser3-utils": "^0.4.7",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.36",
"@tsconfig/svelte": "^1.0.10",
"@types/google-protobuf": "^3.7.3",
@@ -34,6 +34,7 @@
},
"dependencies": {
"@16bits/nes.css": "^2.3.2",
"@anatine/zod-openapi": "^1.3.0",
"@fontsource/press-start-2p": "^4.3.0",
"@joeattardi/emoji-button": "^4.6.2",
"@types/simple-peer": "^9.11.1",
@@ -47,6 +48,7 @@
"easystarjs": "^0.4.4",
"fast-deep-equal": "^3.1.3",
"google-protobuf": "^3.13.0",
"openapi3-ts": "^2.0.2",
"phaser": "3.55.1",
"phaser-animated-tiles": "workadventure/phaser-animated-tiles#da68bbededd605925621dd4f03bd27e69284b254",
"phaser3-rex-plugins": "^1.1.42",
@@ -61,7 +63,7 @@
"standardized-audio-context": "^25.2.4",
"ts-deferred": "^1.0.4",
"ts-proto": "^1.96.0",
"typesafe-i18n": "^2.59.0",
"typesafe-i18n": "^5.4.0",
"uuidv4": "^6.2.10",
"zod": "^3.14.3"
},
+307 -10
View File
@@ -17,7 +17,7 @@ class AnalyticsClient {
}
}
identifyUser(uuid: string, email: string | null) {
identifyUser(uuid: string, email: string | null): void {
this.posthogPromise
?.then((posthog) => {
posthog.identify(uuid, { uuid, email, wa: true });
@@ -25,7 +25,7 @@ class AnalyticsClient {
.catch((e) => console.error(e));
}
loggedWithSso() {
loggedWithSso(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-logged-sso");
@@ -33,7 +33,7 @@ class AnalyticsClient {
.catch((e) => console.error(e));
}
loggedWithToken() {
loggedWithToken(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-logged-token");
@@ -41,7 +41,7 @@ class AnalyticsClient {
.catch((e) => console.error(e));
}
enteredRoom(roomId: string, roomGroup: string | null) {
enteredRoom(roomId: string, roomGroup: string | null): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("$pageView", { roomId, roomGroup });
@@ -50,7 +50,7 @@ class AnalyticsClient {
.catch((e) => console.error(e));
}
openedMenu() {
openedMenu(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-opened-menu");
@@ -58,7 +58,7 @@ class AnalyticsClient {
.catch((e) => console.error(e));
}
launchEmote(emote: string) {
launchEmote(emote: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-emote-launch", { emote });
@@ -66,7 +66,7 @@ class AnalyticsClient {
.catch((e) => console.error(e));
}
enteredJitsi(roomName: string, roomId: string) {
enteredJitsi(roomName: string, roomId: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-entered-jitsi", { roomName, roomId });
@@ -74,7 +74,7 @@ class AnalyticsClient {
.catch((e) => console.error(e));
}
validationName() {
validationName(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-name-validation");
@@ -82,7 +82,7 @@ class AnalyticsClient {
.catch((e) => console.error(e));
}
validationWoka(scene: string) {
validationWoka(scene: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-woka-validation", { scene });
@@ -90,12 +90,309 @@ class AnalyticsClient {
.catch((e) => console.error(e));
}
validationVideo() {
validationVideo(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-video-validation");
})
.catch((e) => console.error(e));
}
/** New feature analytics **/
openedChat(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-opened-chat");
})
.catch((e) => console.error(e));
}
openRegister(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-opened-register");
})
.catch((e) => console.error(e));
}
openInvite(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-opened-invite");
})
.catch((e) => console.error(e));
}
lockDiscussion(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_lockroom");
})
.catch((e) => console.error(e));
}
screenSharing(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa-screensharing");
})
.catch((e) => console.error(e));
}
follow(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_follow");
})
.catch((e) => console.error(e));
}
camera(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_camera");
})
.catch((e) => console.error(e));
}
microphone(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_microphone");
})
.catch((e) => console.error(e));
}
settingMicrophone(value: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_setting_microphone", {
checkbox: value,
});
})
.catch((e) => console.error(e));
}
settingCamera(value: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_setting_camera", {
checkbox: value,
});
})
.catch((e) => console.error(e));
}
settingNotification(value: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_setting_notification", {
checkbox: value,
});
})
.catch((e) => console.error(e));
}
settingFullscreen(value: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_setting_fullscreen", {
checkbox: value,
});
})
.catch((e) => console.error(e));
}
settingAskWebsite(value: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_setting_ask_website", {
checkbox: value,
});
})
.catch((e) => console.error(e));
}
settingRequestFollow(value: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_setting_request_follow", {
checkbox: value,
});
})
.catch((e) => console.error(e));
}
settingDecreaseAudioVolume(value: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_setting_decrease_audio_volume", {
checkbox: value,
});
})
.catch((e) => console.error(e));
}
login(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_login");
})
.catch((e) => console.error(e));
}
logout(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_logout");
})
.catch((e) => console.error(e));
}
switchMultiIframe(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_multiiframe_switch");
})
.catch((e) => console.error(e));
}
closeMultiIframe(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_multiiframe_close");
})
.catch((e) => console.error(e));
}
fullScreenMultiIframe(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_multiiframe_fullscreen");
})
.catch((e) => console.error(e));
}
stackOpenCloseMultiIframe(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_multiiframe_stack_open_close");
})
.catch((e) => console.error(e));
}
menuCredit(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_menu_credit");
})
.catch((e) => console.error(e));
}
menuProfile(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_menu_profile");
})
.catch((e) => console.error(e));
}
menuSetting() {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_menu_setting");
})
.catch((e) => console.error(e));
}
menuInvite(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_menu_invite");
})
.catch((e) => console.error(e));
}
globalMessage(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_menu_globalmessage");
})
.catch((e) => console.error(e));
}
menuContact(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_menu_contact");
})
.catch((e) => console.error(e));
}
inviteCopyLink(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_menu_invite_copylink");
})
.catch((e) => console.error(e));
}
inviteCopyLinkWalk(value: string): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_menu_invite_copylink_walk", {
checkbox: value,
});
})
.catch((e) => console.error(e));
}
editCompanion(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_edit_companion");
})
.catch((e) => console.error(e));
}
editCamera(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_edit_camera");
})
.catch((e) => console.error(e));
}
editName(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_edit_name");
})
.catch((e) => console.error(e));
}
editWoka(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_edit_woka");
})
.catch((e) => console.error(e));
}
selectWoka(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_wokascene_select");
})
.catch((e) => console.error(e));
}
selectCustomWoka(): void {
this.posthogPromise
?.then((posthog) => {
posthog.capture("wa_wokascene_custom");
})
.catch((e) => console.error(e));
}
}
export const analyticsClient = new AnalyticsClient();
+19 -9
View File
@@ -242,7 +242,7 @@ class IframeListener {
} else if (iframeEvent.type === "cameraFollowPlayer") {
this._cameraFollowPlayerStream.next(iframeEvent.data);
} else if (iframeEvent.type === "chat") {
scriptUtils.sendAnonymousChat(iframeEvent.data);
scriptUtils.sendAnonymousChat(iframeEvent.data, iframe.contentWindow ?? undefined);
} else if (iframeEvent.type === "openPopup") {
this._openPopupStream.next(iframeEvent.data);
} else if (iframeEvent.type === "closePopup") {
@@ -404,13 +404,20 @@ class IframeListener {
this.scripts.delete(scriptUrl);
}
sendUserInputChat(message: string) {
this.postMessage({
type: "userInputChat",
data: {
message: message,
} as UserInputChatEvent,
});
/**
* @param message The message to dispatch
* @param exceptOrigin Don't dispatch the message to exceptOrigin (to avoid infinite loops)
*/
sendUserInputChat(message: string, exceptOrigin?: Window) {
this.postMessage(
{
type: "userInputChat",
data: {
message: message,
} as UserInputChatEvent,
},
exceptOrigin
);
}
sendEnterEvent(name: string) {
@@ -528,8 +535,11 @@ class IframeListener {
/**
* Sends the message... to all allowed iframes.
*/
public postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>) {
public postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>, exceptOrigin?: Window) {
for (const iframe of this.iframes) {
if (exceptOrigin === iframe.contentWindow) {
continue;
}
iframe.contentWindow?.postMessage(message, "*");
}
}
+2 -2
View File
@@ -11,9 +11,9 @@ class ScriptUtils {
window.location.href = url;
}
public sendAnonymousChat(chatEvent: ChatEvent) {
public sendAnonymousChat(chatEvent: ChatEvent, origin?: Window) {
const userId = playersStore.addFacticePlayer(chatEvent.author);
chatMessagesStore.addExternalMessage(userId, chatEvent.message);
chatMessagesStore.addExternalMessage(userId, chatEvent.message, origin);
}
}
+16 -2
View File
@@ -17,6 +17,7 @@
import { followRoleStore, followStateStore, followUsersStore } from "../Stores/FollowStore";
import { gameManager } from "../Phaser/Game/GameManager";
import { currentPlayerGroupLockStateStore } from "../Stores/CurrentPlayerGroupStore";
import { analyticsClient } from "../Administration/AnalyticsClient";
const gameScene = gameManager.getCurrentGameScene();
@@ -89,6 +90,7 @@
class="btn-follow nes-btn is-dark"
class:hide={($peerStore.size === 0 && $followStateStore === "off") || $silentStore}
class:disabled={$followStateStore !== "off"}
on:click={() => analyticsClient.follow()}
on:click={followClick}
>
<img class="noselect" src={followImg} alt="" />
@@ -98,6 +100,7 @@
class="btn-lock nes-btn is-dark"
class:hide={$peerStore.size === 0 || $silentStore}
class:disabled={$currentPlayerGroupLockStateStore}
on:click={() => analyticsClient.lockDiscussion()}
on:click={lockClick}
>
<img class="noselect" src={lockImg} alt="" />
@@ -105,6 +108,7 @@
<div
class="btn-monitor nes-btn is-dark"
on:click={() => analyticsClient.screenSharing()}
on:click={screenSharingClick}
class:hide={!$screenSharingAvailableStore || $silentStore}
class:enabled={$requestedScreenSharingState}
@@ -116,7 +120,12 @@
{/if}
</div>
<div class="btn-video nes-btn is-dark" on:click={cameraClick} class:disabled={!$requestedCameraState || $silentStore}>
<div
class="btn-video nes-btn is-dark"
on:click={() => analyticsClient.camera()}
on:click={cameraClick}
class:disabled={!$requestedCameraState || $silentStore}
>
{#if $requestedCameraState && !$silentStore}
<img class="noselect" src={cinemaImg} alt="Turn on webcam" />
{:else}
@@ -124,7 +133,12 @@
{/if}
</div>
<div class="btn-micro nes-btn is-dark" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || $silentStore}>
<div
class="btn-micro nes-btn is-dark"
on:click={() => analyticsClient.microphone()}
on:click={microphoneClick}
class:disabled={!$requestedMicrophoneState || $silentStore}
>
{#if $requestedMicrophoneState && !$silentStore}
<img class="noselect" src={microphoneImg} alt="Turn on microphone" />
{:else}
@@ -12,6 +12,7 @@
import { i18nJson } from "../../i18n/locales";
import uploadFile from "../images/jitsi.png";
import { analyticsClient } from "../../Administration/AnalyticsClient";
export let index: number;
export let coWebsite: CoWebsite;
@@ -103,6 +104,7 @@
class:ready={$state === "ready"}
class:displayed={isMain || isHighlight}
class:vertical
on:click={() => analyticsClient.stackOpenCloseMultiIframe()}
on:click={onClick}
>
<div class="cowebsite-thumnail-container">
@@ -19,6 +19,5 @@
padding-top: 2%;
height: 100%;
position: relative;
z-index: 200;
}
</style>
@@ -67,10 +67,11 @@
{/key}
{:else if $highlightedEmbedScreen.type === "cowebsite"}
{#key $highlightedEmbedScreen.embed.getId()}
<div
id={"cowebsite-slot-" + $highlightedEmbedScreen.embed.getId()}
class="highlighted-cowebsite nes-container is-rounded"
>
<div class="highlighted-cowebsite-container nes-container is-rounded">
<div
id={"cowebsite-slot-" + $highlightedEmbedScreen.embed.getId()}
class="highlighted-cowebsite"
/>
<div class="actions">
<button type="button" class="nes-btn is-error close" on:click={closeCoWebsite}
>&times;</button
@@ -120,20 +121,29 @@
.highlighted-cowebsite {
height: 100% !important;
width: 96%;
background-color: rgba(#000000, 0.6);
margin: 0 !important;
width: 100% !important;
position: relative;
z-index: 200;
.actions {
z-index: 200;
position: relative;
display: flex;
flex-direction: row;
justify-content: end;
gap: 2%;
&-container {
height: 100% !important;
width: 96%;
background-color: rgba(#000000, 0.6);
margin: 0 !important;
padding: 0 !important;
.actions {
z-index: 202;
position: absolute;
width: 100%;
top: 0;
display: flex;
flex-direction: row;
justify-content: end;
gap: 2%;
button {
pointer-events: all;
button {
pointer-events: all;
}
}
}
}
@@ -53,11 +53,14 @@
background: #eceeee;
margin-left: auto;
margin-right: auto;
position: absolute;
left: 0;
right: 0;
margin-top: 4%;
max-height: 80vh;
max-width: 80vw;
margin-left: 10%;
margin-right: 10%;
z-index: 600;
overflow: auto;
text-align: center;
+5 -5
View File
@@ -68,6 +68,11 @@
</aside>
<section id="main-layout-main">
<Lazy
when={$showDesktopCapturerSourcePicker}
component={() => import("./Video/DesktopCapturerSourcePicker.svelte")}
/>
{#if $menuVisiblilityStore}
<Menu />
{/if}
@@ -120,11 +125,6 @@
<Lazy when={$emoteMenuStore} component={() => import("./EmoteMenu/EmoteMenu.svelte")} />
<Lazy
when={$showDesktopCapturerSourcePicker}
component={() => import("./Video/DesktopCapturerSourcePicker.svelte")}
/>
{#if hasEmbedScreen}
<EmbedScreensContainer />
{/if}
+16 -4
View File
@@ -2,6 +2,7 @@
import LL from "../../i18n/i18n-svelte";
import { gameManager } from "../../Phaser/Game/GameManager";
import { startLayerNamesStore } from "../../Stores/StartLayerNamesStore";
import { analyticsClient } from "../../Administration/AnalyticsClient";
let entryPoint: string = $startLayerNamesStore[0];
let walkAutomatically: boolean = false;
@@ -54,16 +55,26 @@
class="link-url nes-input is-dark"
value={location.toString()}
/>
<button type="button" class="nes-btn is-primary" on:click={copyLink}>{$LL.menu.invite.copy()}</button>
<button
type="button"
class="nes-btn is-primary"
on:click={() => analyticsClient.inviteCopyLink()}
on:click={copyLink}>{$LL.menu.invite.copy()}</button
>
</section>
{:else}
<section class="is-mobile">
<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}>{$LL.menu.invite.share()}</button>
<button
type="button"
class="nes-btn is-primary"
on:click={() => analyticsClient.inviteCopyLink()}
on:click={shareLink}>{$LL.menu.invite.share()}</button
>
</section>
{/if}
<h3>Select an entry point</h3>
<h3>{$LL.menu.invite.selectEntryPoint()}</h3>
<section class="nes-select is-dark starting-points">
<select
bind:value={entryPoint}
@@ -81,11 +92,12 @@
type="checkbox"
class="nes-checkbox is-dark"
bind:checked={walkAutomatically}
on:change={(e) => analyticsClient.inviteCopyLinkWalk(e.currentTarget.value)}
on:change={() => {
updateInputFieldValue();
}}
/>
<span>{$LL.menu.invite.walk_automatically_to_position()}</span>
<span>{$LL.menu.invite.walkAutomaticallyToPosition()}</span>
</label>
</section>
</div>
+15 -13
View File
@@ -19,6 +19,7 @@
import type { Unsubscriber } from "svelte/store";
import { sendMenuClickedEvent } from "../../Api/iframe/Ui/MenuItem";
import LL from "../../i18n/i18n-svelte";
import { analyticsClient } from "../../Administration/AnalyticsClient";
let activeSubMenu: MenuItem = $subMenusStore[0];
let activeComponent: typeof ProfileSubMenu | typeof CustomSubMenu = ProfileSubMenu;
@@ -48,24 +49,30 @@
activeSubMenu = menu;
switch (menu.key) {
case SubMenusInterface.settings:
analyticsClient.menuSetting();
activeComponent = SettingsSubMenu;
break;
case SubMenusInterface.profile:
analyticsClient.menuProfile();
activeComponent = ProfileSubMenu;
break;
case SubMenusInterface.worlds:
activeComponent = WorldsSubMenu;
break;
case SubMenusInterface.invite:
analyticsClient.menuInvite();
activeComponent = GuestSubMenu;
break;
case SubMenusInterface.aboutRoom:
analyticsClient.menuCredit();
activeComponent = AboutRoomSubMenu;
break;
case SubMenusInterface.globalMessages:
analyticsClient.globalMessage();
activeComponent = (await import("./GlobalMessagesSubMenu.svelte")).default;
break;
case SubMenusInterface.contact:
analyticsClient.menuContact();
activeComponent = ContactSubMenu;
break;
}
@@ -92,16 +99,11 @@
}
}
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();
}
$: subMenuTranslations = $subMenusStore.map((subMenu) =>
subMenu.type === "scripting" ? subMenu.label : $LL.menu.sub[subMenu.key]()
);
$: activeSubMenuTranslation =
activeSubMenu.type === "scripting" ? activeSubMenu.label : $LL.menu.sub[activeSubMenu.key]();
</script>
<svelte:window on:keydown={onKeyDown} />
@@ -110,20 +112,20 @@
<div class="menu-nav-sidebar nes-container is-rounded" transition:fly={{ x: -1000, duration: 500 }}>
<h2>{$LL.menu.title()}</h2>
<nav>
{#each $subMenusStore as submenu}
{#each $subMenusStore as submenu, i}
<button
type="button"
class="nes-btn {activeSubMenu === submenu ? 'is-disabled' : ''}"
on:click|preventDefault={() => void switchMenu(submenu)}
>
{translateMenuName(submenu)}
{subMenuTranslations[i]}
</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>{translateMenuName(activeSubMenu)}</h2>
<h2>{activeSubMenuTranslation}</h2>
<svelte:component this={activeComponent} {...props} />
</div>
</div>
+9 -5
View File
@@ -10,6 +10,7 @@
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
import { showShareLinkMapModalStore } from "../../Stores/ModalStore";
import LL from "../../i18n/i18n-svelte";
import { analyticsClient } from "../../Administration/AnalyticsClient";
function showMenu() {
menuVisiblilityStore.set(!get(menuVisiblilityStore));
@@ -43,7 +44,8 @@
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showInvite}
on:click={() => analyticsClient.openInvite()}
on:click={showInvite}
/>
</span>
<span class="nes-btn is-dark">
@@ -53,7 +55,8 @@
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={register}
on:click={() => analyticsClient.openRegister()}
on:click={register}
/>
</span>
{:else}
@@ -64,11 +67,11 @@
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showMenu}
on:click={() => analyticsClient.openedMenu()}
on:click={showMenu}
/>
</span>
{/if}
<span class="nes-btn is-dark">
<img
src={logoTalk}
@@ -76,7 +79,8 @@
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showChat}
on:click={() => analyticsClient.openedMenu()}
on:click={showChat}
/>
</span>
</main>
@@ -21,6 +21,7 @@
import Woka from "../Woka/Woka.svelte";
import Companion from "../Companion/Companion.svelte";
import LL from "../../i18n/i18n-svelte";
import { analyticsClient } from "../../Administration/AnalyticsClient";
function disableMenuStores() {
menuVisiblilityStore.set(false);
@@ -54,21 +55,41 @@
<div class="customize-main">
<div class="submenu">
<section>
<!--
<button type="button" class="nes-btn" on:click|preventDefault={openEditNameScene}>
<!--
<button
type="button"
class="nes-btn"
on:click={() => analyticsClient.editName()}
on:click={openEditNameScene}
>
<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}>
-->
<button
type="button"
class="nes-btn"
on:click={() => analyticsClient.editWoka()}
on:click={openEditSkinScene}
>
<Woka userId={-1} placeholderSrc="" width="26px" height="26px" />
<span class="btn-hover">{$LL.menu.profile.edit.woka()}</span>
</button>
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>
<button
type="button"
class="nes-btn"
on:click={() => analyticsClient.editCompanion()}
on:click={openEditCompanionScene}
>
<Companion userId={-1} placeholderSrc={btnProfileSubMenuCompanion} width="26px" height="26px" />
<span class="btn-hover">{$LL.menu.profile.edit.companion()}</span>
</button>
<button type="button" class="nes-btn" on:click|preventDefault={openEnableCameraScene}>
<button
type="button"
class="nes-btn"
on:click={() => analyticsClient.editCamera()}
on:click={openEnableCameraScene}
>
<img src={btnProfileSubMenuCamera} alt={$LL.menu.profile.edit.camera()} />
<span class="btn-hover">{$LL.menu.profile.edit.camera()}</span>
</button>
@@ -83,13 +104,15 @@
{/if}
</section>
<section>
<button type="button" class="nes-btn" on:click|preventDefault={logOut}
<button type="button" class="nes-btn" on:click={() => analyticsClient.logout()} on:click={logOut}
>{$LL.menu.profile.logout()}</button
>
</section>
{:else}
<section>
<a type="button" class="nes-btn" href="/login">{$LL.menu.profile.login()}</a>
<a type="button" class="nes-btn" href="/login" on:click={() => analyticsClient.login()}
>{$LL.menu.profile.login()}</a
>
</section>
{/if}
</div>
@@ -10,6 +10,7 @@
import { audioManagerVolumeStore } from "../../Stores/AudioManagerStore";
import infoImg from "../images/info.svg";
import { analyticsClient } from "../../Administration/AnalyticsClient";
let fullscreen: boolean = localUserStore.getFullscreen();
let notification: boolean = localUserStore.getNotification() === "granted";
@@ -28,12 +29,12 @@
let previewCameraPrivacySettings = valueCameraPrivacySettings;
let previewMicrophonePrivacySettings = valueMicrophonePrivacySettings;
function saveSetting() {
async function saveSetting() {
let change = false;
if (valueLocale !== previewValueLocale) {
previewValueLocale = valueLocale;
setCurrentLocale(valueLocale as Locales);
await setCurrentLocale(valueLocale as Locales);
}
if (valueVideo !== previewValueVideo) {
@@ -174,7 +175,7 @@
<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>
<option value={locale.id}>{`${locale.language} (${locale.region})`}</option>
{/each}
</select>
</div>
@@ -191,11 +192,21 @@
</div>
</div>
<label>
<input type="checkbox" class="nes-checkbox is-dark" bind:checked={valueCameraPrivacySettings} />
<input
type="checkbox"
class="nes-checkbox is-dark"
on:change={(event) => analyticsClient.settingCamera(event.currentTarget.value)}
bind:checked={valueCameraPrivacySettings}
/>
<span>{$LL.menu.settings.privacySettings.cameraToggle()}</span>
</label>
<label>
<input type="checkbox" class="nes-checkbox is-dark" bind:checked={valueMicrophonePrivacySettings} />
<input
type="checkbox"
class="nes-checkbox is-dark"
on:change={(event) => analyticsClient.settingMicrophone(event.currentTarget.value)}
bind:checked={valueMicrophonePrivacySettings}
/>
<span>{$LL.menu.settings.privacySettings.microphoneToggle()}</span>
</label>
</section>
@@ -211,6 +222,7 @@
type="checkbox"
class="nes-checkbox is-dark"
bind:checked={fullscreen}
on:change={(event) => analyticsClient.settingFullscreen(event.currentTarget.value)}
on:change={changeFullscreen}
/>
<span>{$LL.menu.settings.fullscreen()}</span>
@@ -220,6 +232,7 @@
type="checkbox"
class="nes-checkbox is-dark"
bind:checked={notification}
on:change={(event) => analyticsClient.settingNotification(event.currentTarget.value)}
on:change={changeNotification}
/>
<span>{$LL.menu.settings.notifications()}</span>
@@ -229,6 +242,7 @@
type="checkbox"
class="nes-checkbox is-dark"
bind:checked={forceCowebsiteTrigger}
on:change={(event) => analyticsClient.settingAskWebsite(event.currentTarget.value)}
on:change={changeForceCowebsiteTrigger}
/>
<span>{$LL.menu.settings.cowebsiteTrigger()}</span>
@@ -238,6 +252,7 @@
type="checkbox"
class="nes-checkbox is-dark"
bind:checked={ignoreFollowRequests}
on:change={(event) => analyticsClient.settingRequestFollow(event.currentTarget.value)}
on:change={changeIgnoreFollowRequests}
/>
<span>{$LL.menu.settings.ignoreFollowRequest()}</span>
@@ -246,6 +261,7 @@
type="checkbox"
class="nes-checkbox is-dark"
bind:checked={decreaseAudioPlayerVolumeWhileTalking}
on:change={(event) => analyticsClient.settingDecreaseAudioVolume(event.currentTarget.value)}
on:change={changeDecreaseAudioPlayerVolumeWhileTalking}
/>
<span>{$LL.audio.manager.reduce()}</span>
+5 -3
View File
@@ -2,6 +2,7 @@
import { fly } from "svelte/transition";
import { errorScreenStore } from "../../Stores/ErrorScreenStore";
import { gameManager } from "../../Phaser/Game/GameManager";
import { connectionManager } from "../../Connexion/ConnectionManager";
import { get } from "svelte/store";
import { onDestroy } from "svelte";
@@ -11,7 +12,8 @@
let errorScreen = get(errorScreenStore);
function click() {
window.location.reload();
if (errorScreen.type === "unauthorized") void connectionManager.logout();
else window.location.reload();
}
let details = errorScreen.details;
let timeVar = errorScreen.timeToRetry ?? 0;
@@ -37,9 +39,9 @@
<p class="details">
{detailsStylized}{#if $errorScreenStore.type === "retry"}<div class="loading" />{/if}
</p>
{#if $errorScreenStore.type === "retry" && $errorScreenStore.canRetryManual}
{#if ($errorScreenStore.type === "retry" && $errorScreenStore.canRetryManual) || $errorScreenStore.type === "unauthorized"}
<button type="button" class="nes-btn is-primary button" on:click={click}>
<img src={reload} alt="" class="reload" />
{#if $errorScreenStore.type === "retry"}<img src={reload} alt="" class="reload" />{/if}
{$errorScreenStore.buttonTitle}
</button>
{/if}
@@ -3,6 +3,7 @@
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
import LL from "../../i18n/i18n-svelte";
import { customizeAvailableStore, selectedCollection } from "../../Stores/SelectCharacterSceneStore";
import { analyticsClient } from "../../Administration/AnalyticsClient";
export let game: Game;
@@ -40,13 +41,15 @@
<button
type="submit"
class="selectCharacterSceneFormSubmit nes-btn is-primary"
on:click|preventDefault={cameraScene}>{$LL.woka.selectWoka.continue()}</button
on:click={() => analyticsClient.selectWoka()}
on:click={cameraScene}>{$LL.woka.selectWoka.continue()}</button
>
{#if $customizeAvailableStore}
<button
type="submit"
class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn"
on:click|preventDefault={customizeScene}>{$LL.woka.selectWoka.customize()}</button
on:click={() => analyticsClient.selectCustomWoka()}
on:click={customizeScene}>{$LL.woka.selectWoka.customize()}</button
>
{/if}
</section>
+6 -8
View File
@@ -364,15 +364,13 @@ class ConnectionManager {
if (locale) {
try {
if (locales.indexOf(locale) == -1) {
locales.forEach((l) => {
if (l.startsWith(locale.split("-")[0])) {
setCurrentLocale(l);
return;
}
});
if (locales.indexOf(locale) !== -1) {
await setCurrentLocale(locale as Locales);
} else {
setCurrentLocale(locale as Locales);
const nonRegionSpecificLocale = locales.find((l) => l.startsWith(locale.split("-")[0]));
if (nonRegionSpecificLocale) {
await setCurrentLocale(nonRegionSpecificLocale);
}
}
} catch (err) {
console.warn("Could not set locale", err);
+9 -3
View File
@@ -483,9 +483,15 @@ export class RoomConnection implements RoomConnection {
}
case "errorScreenMessage": {
this._errorScreenMessageStream.next(message.errorScreenMessage);
if (message.errorScreenMessage.code !== "retry") this.closed = true;
console.error("An error occurred server side: " + message.errorScreenMessage.code);
errorScreenStore.setError(message.errorScreenMessage);
console.error("An error occurred server side: " + JSON.stringify(message.errorScreenMessage));
if (message.errorScreenMessage.code !== "retry") {
this.closed = true;
}
if (message.errorScreenMessage.type === "redirect" && message.errorScreenMessage.urlToRedirect) {
window.location.assign(message.errorScreenMessage.urlToRedirect);
} else {
errorScreenStore.setError(message.errorScreenMessage);
}
break;
}
default: {
@@ -61,9 +61,8 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container {
this.frame = this.scene.add.graphics();
this.turnIcon = this.scene.add
.image(this.background.displayWidth * 0.35, this.background.displayHeight * 0.35, "iconTurn")
.setScale(0.25)
.setTintFill(0xffffff)
.setAlpha(0.5);
.setScale(0.2)
.setAlpha(0.75);
this.drawFrame();
this.setSize(this.SIZE, this.SIZE);
@@ -130,11 +129,11 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container {
this.changeAnimation(direction, moving);
this.turnIconTween?.stop();
this.turnIcon.setScale(0.25);
this.turnIcon.setScale(0.2);
this.turnIconTween = this.scene.tweens.add({
targets: [this.turnIcon],
duration: 100,
scale: 0.2,
scale: 0.15,
yoyo: true,
ease: Easing.SineEaseIn,
});
+2 -1
View File
@@ -6,6 +6,7 @@ export interface IconButtonConfig {
hover: IconButtonAppearanceConfig;
pressed: IconButtonAppearanceConfig;
selected: IconButtonAppearanceConfig;
iconScale?: number;
}
export interface IconButtonAppearanceConfig {
@@ -34,7 +35,7 @@ export class IconButton extends Phaser.GameObjects.Container {
this.config = config;
this.background = this.scene.add.graphics();
this.icon = this.scene.add.image(0, 0, this.config.iconTextureKey);
this.icon = this.scene.add.image(0, 0, this.config.iconTextureKey).setScale(config.iconScale ?? 1);
this.drawBackground(this.config.idle);
this.add([this.background, this.icon]);
+4 -4
View File
@@ -4,7 +4,7 @@ import { Character } from "../Entity/Character";
import type { GameScene } from "../Game/GameScene";
import type { PointInterface } from "../../Connexion/ConnexionModels";
import type { PlayerAnimationDirections } from "../Player/Animation";
import type { Unsubscriber } from "svelte/store";
import { get, Unsubscriber } from "svelte/store";
import type { ActivatableInterface } from "../Game/ActivatableInterface";
import type CancelablePromise from "cancelable-promise";
import LL from "../../i18n/i18n-svelte";
@@ -113,7 +113,7 @@ export class RemotePlayer extends Character implements ActivatableInterface {
const actions: ActionsMenuAction[] = [];
if (this.visitCardUrl) {
actions.push({
actionName: LL.woka.menu.businessCard(),
actionName: get(LL).woka.menu.businessCard(),
protected: true,
priority: 1,
callback: () => {
@@ -125,8 +125,8 @@ export class RemotePlayer extends Character implements ActivatableInterface {
actions.push({
actionName: blackListManager.isBlackListed(this.userUuid)
? LL.report.block.unblock()
: LL.report.block.block(),
? get(LL).report.block.unblock()
: get(LL).report.block.block(),
protected: true,
priority: -1,
style: "is-error",
@@ -76,6 +76,7 @@ export class ActivatablesManager {
const currentPlayerPos = this.currentPlayer.getDirectionalActivationPosition(
this.directionalActivationPositionShift
);
this.activatableObjectsDistances.clear();
for (const object of objects) {
const distance = MathUtils.distanceBetween(currentPlayerPos, object.getPosition());
this.activatableObjectsDistances.set(object, distance);
+7
View File
@@ -1135,6 +1135,13 @@ export class GameScene extends DirtyScene {
})
);
this.iframeSubscriptionList.push(
iframeListener.stopSoundStream.subscribe((stopSoundEvent) => {
const url = new URL(stopSoundEvent.url, this.MapUrlFile);
soundManager.stopSound(this.sound, url.toString());
})
);
this.iframeSubscriptionList.push(
iframeListener.addActionsMenuKeyToRemotePlayerStream.subscribe((data) => {
this.MapPlayersByKey.get(data.id)?.registerActionsMenuAction({
+33 -7
View File
@@ -94,6 +94,7 @@ export class CustomizeScene extends AbstractCharacterScene {
}
public create(): void {
this.tryLoadLastUsedWokaLayers();
waScaleManager.zoomModifier = 1;
this.createSlotBackgroundTextures();
this.initializeCustomWokaPreviewer();
@@ -149,12 +150,35 @@ export class CustomizeScene extends AbstractCharacterScene {
this.scene.run(SelectCharacterSceneName);
}
private tryLoadLastUsedWokaLayers(): void {
try {
const savedWokaLayers = gameManager.getCharacterLayers();
if (savedWokaLayers && savedWokaLayers.length !== 0) {
this.selectedLayers = [];
for (let i = 0; i < savedWokaLayers.length; i += 1) {
this.selectedLayers.push(
this.layers[i].findIndex((item) => item.id === gameManager.getCharacterLayers()[i])
);
}
}
} catch {
console.warn("Cannot load previous WOKA");
}
}
private createSlotBackgroundTextures(): void {
for (let i = 0; i < 4; i += 1) {
if (this.textures.getTextureKeys().includes(`floorTexture${i}`)) {
continue;
}
TexturesHelper.createFloorRectangleTexture(this, `floorTexture${i}`, 50, 50, "floorTiles", i);
TexturesHelper.createFloorRectangleTexture(
this,
`floorTexture${i}`,
WokaBodyPartSlot.SIZE,
WokaBodyPartSlot.SIZE,
"floorTiles",
i
);
}
}
@@ -213,15 +237,16 @@ export class CustomizeScene extends AbstractCharacterScene {
),
[CustomWokaBodyPart.Body]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconBody")),
[CustomWokaBodyPart.Clothes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconClothes")),
[CustomWokaBodyPart.Eyes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconEyes")),
[CustomWokaBodyPart.Eyes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconEyes", 0.7)),
[CustomWokaBodyPart.Hair]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHair")),
[CustomWokaBodyPart.Hat]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHat")),
};
}
private getDefaultIconButtonConfig(iconTextureKey: string): IconButtonConfig {
private getDefaultIconButtonConfig(iconTextureKey: string, iconScale?: number): IconButtonConfig {
return {
iconTextureKey,
iconScale,
width: 25,
height: 25,
idle: {
@@ -327,13 +352,14 @@ export class CustomizeScene extends AbstractCharacterScene {
}
private handleCustomWokaPreviewerOnResize(): void {
const ratio = innerHeight / innerWidth;
this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + 10;
this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + (ratio > 1.6 ? 40 : 10);
}
private handleBodyPartButtonsOnResize(): void {
const ratio = innerHeight / innerWidth;
const slotDimension = 50;
const slotDimension = WokaBodyPartSlot.SIZE;
for (const part in this.bodyPartsButtons) {
this.bodyPartsButtons[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension);
@@ -420,7 +446,7 @@ export class CustomizeScene extends AbstractCharacterScene {
private handleRandomizeButtonOnResize(): void {
const x =
this.customWokaPreviewer.x +
this.customWokaPreviewer.x -
(this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5;
const y =
this.customWokaPreviewer.y +
@@ -431,7 +457,7 @@ export class CustomizeScene extends AbstractCharacterScene {
private handleFinishButtonOnResize(): void {
const x =
this.customWokaPreviewer.x -
this.customWokaPreviewer.x +
(this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5;
const y =
this.customWokaPreviewer.y +
+1 -4
View File
@@ -6,7 +6,6 @@ import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene";
import { localeDetector } from "../../i18n/locales";
import { errorScreenStore } from "../../Stores/ErrorScreenStore";
import { isErrorApiData } from "../../Messages/JsonMessages/ErrorApiData";
import { connectionManager } from "../../Connexion/ConnectionManager";
export const EntrySceneName = "EntryScene";
@@ -49,9 +48,7 @@ export class EntryScene extends Scene {
.catch((err) => {
const errorType = isErrorApiData.safeParse(err?.response?.data);
if (errorType.success) {
if (errorType.data.type === "unauthorized") {
void connectionManager.logout();
} else if (errorType.data.type === "redirect") {
if (errorType.data.type === "redirect") {
window.location.assign(errorType.data.urlToRedirect);
} else errorScreenStore.setError(err?.response?.data);
} else {
@@ -18,6 +18,7 @@ import { DraggableGrid } from "@home-based-studio/phaser3-utils";
import { WokaSlot } from "../Components/SelectWoka/WokaSlot";
import { DraggableGridEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/DraggableGrid";
import { wokaList } from "../../Messages/JsonMessages/PlayerTextures";
import { myCameraVisibilityStore } from "../../Stores/MyCameraStoreVisibility";
//todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene";
@@ -133,6 +134,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
return;
}
this.selectedWoka = null;
myCameraVisibilityStore.set(false);
this.scene.sleep(SelectCharacterSceneName);
this.scene.run(CustomizeSceneName);
selectCharacterSceneVisibleStore.set(false);
+5 -2
View File
@@ -87,7 +87,10 @@ function createChatMessagesStore() {
return list;
});
},
addExternalMessage(authorId: number, text: string) {
/**
* @param origin The iframe that originated this message (if triggered from the Scripting API), or undefined otherwise.
*/
addExternalMessage(authorId: number, text: string, origin?: Window) {
update((list) => {
const lastMessage = list[list.length - 1];
if (
@@ -106,7 +109,7 @@ function createChatMessagesStore() {
});
}
iframeListener.sendUserInputChat(text);
iframeListener.sendUserInputChat(text, origin);
return list;
});
chatVisibilityStore.set(true);
-2
View File
@@ -2,7 +2,6 @@ import { get, writable } from "svelte/store";
import Timeout = NodeJS.Timeout;
import { userIsAdminStore } from "./GameStore";
import { CONTACT_URL, IDENTITY_URL, PROFILE_URL } from "../Enum/EnvironmentVariable";
import { analyticsClient } from "../Administration/AnalyticsClient";
import type { Translation } from "../i18n/i18n-types";
import axios from "axios";
import { localUserStore } from "../Connexion/LocalUserStore";
@@ -14,7 +13,6 @@ export const userIsConnected = writable(false);
export const profileAvailable = writable(true);
menuVisiblilityStore.subscribe((value) => {
if (value) analyticsClient.openedMenu();
if (userIsConnected && value && IDENTITY_URL != null) {
axios.get(getMeUrl()).catch((err) => {
console.error("menuVisiblilityStore => err => ", err);
+4
View File
@@ -8,6 +8,7 @@ import { isMediaBreakpointDown } from "../Utils/BreakpointsUtils";
import { LayoutMode } from "./LayoutManager";
import type { CoWebsite } from "./CoWebsite/CoWesbite";
import type CancelablePromise from "cancelable-promise";
import { analyticsClient } from "../Administration/AnalyticsClient";
export enum iframeStates {
closed = 1,
@@ -126,6 +127,7 @@ class CoWebsiteManager {
const buttonCloseCoWebsite = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
buttonCloseCoWebsite.addEventListener("click", () => {
analyticsClient.closeMultiIframe();
const coWebsite = this.getMainCoWebsite();
if (!coWebsite) {
@@ -143,6 +145,7 @@ class CoWebsiteManager {
const buttonFullScreenFrame = HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId);
buttonFullScreenFrame.addEventListener("click", () => {
analyticsClient.fullScreenMultiIframe();
buttonFullScreenFrame.blur();
this.fullscreen();
});
@@ -159,6 +162,7 @@ class CoWebsiteManager {
});
buttonSwipe.addEventListener("click", () => {
analyticsClient.switchMultiIframe();
const mainCoWebsite = this.getMainCoWebsite();
const highlightedEmbed = get(highlightedEmbedScreen);
if (highlightedEmbed?.type === "cowebsite") {
+1 -3
View File
@@ -1,3 +1 @@
i18n-svelte.ts
i18n-types.ts
i18n-util.ts
i18n-*.ts
-2
View File
@@ -16,8 +16,6 @@ import trigger from "./trigger";
const de_DE: Translation = {
...(en_US as Translation),
language: "Deutsch",
country: "Deutschland",
audio,
camera,
chat,
+2 -1
View File
@@ -77,7 +77,8 @@ const menu: NonNullable<Translation["menu"]> = {
description: "Link zu diesem Raum teilen!",
copy: "Kopieren",
share: "Teilen",
walk_automatically_to_position: "Automatisch zu meiner Position gehen",
walkAutomaticallyToPosition: "Automatisch zu meiner Position gehen",
selectEntryPoint: "Select an entry point",
},
globalMessage: {
text: "Text",
-2
View File
@@ -14,8 +14,6 @@ import emoji from "./emoji";
import trigger from "./trigger";
const en_US: BaseTranslation = {
language: "English",
country: "United States",
audio,
camera,
chat,
+2 -1
View File
@@ -77,7 +77,8 @@ const menu: BaseTranslation = {
description: "Share the link of the room!",
copy: "Copy",
share: "Share",
walk_automatically_to_position: "Walk automatically to my position",
walkAutomaticallyToPosition: "Walk automatically to my position",
selectEntryPoint: "Select an entry point",
},
globalMessage: {
text: "Text",
+2 -2
View File
@@ -1,8 +1,8 @@
import type { AsyncFormattersInitializer } from "typesafe-i18n";
import type { FormattersInitializer } from "typesafe-i18n";
import type { Locales, Formatters } from "./i18n-types";
// eslint-disable-next-line @typescript-eslint/require-await
export const initFormatters: AsyncFormattersInitializer<Locales, Formatters> = async () => {
export const initFormatters: FormattersInitializer<Locales, Formatters> = async () => {
const formatters: Formatters = {
// add your formatter functions here
};
+23 -49
View File
@@ -1,70 +1,44 @@
import { detectLocale, navigatorDetector, initLocalStorageDetector } from "typesafe-i18n/detectors";
import { FALLBACK_LOCALE } from "../Enum/EnvironmentVariable";
import { initI18n, setLocale, locale } from "./i18n-svelte";
import { setLocale } from "./i18n-svelte";
import type { Locales } from "./i18n-types";
import { baseLocale, getTranslationForLocale, locales } from "./i18n-util";
import { get } from "svelte/store";
import { baseLocale, locales } from "./i18n-util";
import { loadLocaleAsync } from "./i18n-util.async";
const fallbackLocale = FALLBACK_LOCALE || baseLocale;
const fallbackLocale = (FALLBACK_LOCALE || baseLocale) as Locales;
const localStorageProperty = "language";
export const localeDetector = async () => {
const exist = localStorage.getItem(localStorageProperty);
let detectedLocale: Locales = fallbackLocale as Locales;
let detectedLocale: Locales = fallbackLocale;
if (exist) {
const localStorageDetector = initLocalStorageDetector(localStorageProperty);
detectedLocale = detectLocale(fallbackLocale, locales, localStorageDetector) as Locales;
detectedLocale = detectLocale(fallbackLocale, locales, localStorageDetector);
} else {
detectedLocale = detectLocale(fallbackLocale, locales, navigatorDetector) as Locales;
detectedLocale = detectLocale(fallbackLocale, locales, navigatorDetector);
}
await initI18n(detectedLocale);
await setCurrentLocale(detectedLocale);
};
export const setCurrentLocale = (locale: Locales) => {
export const setCurrentLocale = async (locale: Locales) => {
localStorage.setItem(localStorageProperty, locale);
setLocale(locale).catch(() => {
console.log("Cannot reload the locale!");
});
await loadLocaleAsync(locale);
setLocale(locale);
};
export type DisplayableLocale = { id: Locales; language: string; country: string };
export const displayableLocales: { id: Locales; language: string; region: string }[] = locales.map((locale) => {
const [language, region] = locale.split("-");
function getDisplayableLocales() {
const localesObject: DisplayableLocale[] = [];
locales.forEach((locale) => {
getTranslationForLocale(locale)
.then((translations) => {
localesObject.push({
id: locale,
language: translations.language,
country: translations.country,
});
})
.catch((error) => {
console.log(error);
});
});
return localesObject;
}
export const displayableLocales = getDisplayableLocales();
export const i18nJson = (text: string): string => {
if (text.trim().startsWith("{")) {
try {
const textObject = JSON.parse(text);
if (textObject[get(locale)]) {
return textObject[get(locale)];
} else if (Object.keys(textObject).length > 0) {
// fallback to first value
return textObject[Object.keys(textObject)[0]];
}
} catch (err) {
//
}
// backwards compatibility
if (!Intl.DisplayNames) {
return { id: locale, language, region };
}
return text;
};
return {
id: locale,
language: new Intl.DisplayNames(locale, { type: "language" }).of(language),
region: new Intl.DisplayNames(locale, { type: "region" }).of(region),
};
});
+1 -1
View File
@@ -95,7 +95,7 @@
&-buffer {
iframe {
z-index: 45 !important;
z-index: 201 !important;
pointer-events: none !important;
overflow: hidden;
border: 0;
+1 -1
View File
@@ -8,7 +8,7 @@
"moduleResolution": "node",
//"module": "CommonJS",
"module": "ESNext",
"target": "ES2017",
"target": "ES2020",
"declaration": false,
"downlevelIteration": true,
"jsx": "react",
+33 -8
View File
@@ -7,6 +7,14 @@
resolved "https://registry.yarnpkg.com/@16bits/nes.css/-/nes.css-2.3.2.tgz#e69db834119b33ae8d3cb044f106a07a17cadd6f"
integrity sha512-nEM5PIth+Bab5JSOa4uUR+PMNUsNTYxA55oVlG3gXI/4LoYtWS767Uv9Pu/KCbHXVvnIjt4ZXt13kZw3083qTw==
"@anatine/zod-openapi@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@anatine/zod-openapi/-/zod-openapi-1.3.0.tgz#b5b38c3d821b79674226aa7b327c88c371860d0d"
integrity sha512-l54DypUdDsIq1Uwjv4ib9IBkTXMKZQLUj7qvdFL51EExC5LdSSqOlTOyaVVZZGYgWPKM7ZjGklhdoknLz4EC+w==
dependencies:
ts-deepmerge "^1.1.0"
validator "^13.7.0"
"@babel/runtime@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
@@ -77,10 +85,10 @@
resolved "https://registry.yarnpkg.com/@geprog/vite-plugin-env-config/-/vite-plugin-env-config-4.0.3.tgz#ca04bd9ad9f55fe568917db79266afe8e766e25e"
integrity sha512-2HDCV+6XXJjSuBAhDWLRr111buMQ3bIZrKo3dymIhEJ4oJCC/3yDqg7HDQIn8Y8KKbsM0AtuHMZW4yz2tPBsYg==
"@home-based-studio/phaser3-utils@^0.4.2":
version "0.4.2"
resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.4.2.tgz#b2c1815a6b51321ea8dab027b5badcf714d99fd6"
integrity sha512-S0VkAq3z0Kf0vEUUyCDes911icvc+nkUq7lGp23zD/5lk7LTGM51NswSAfel7Rm/DLY8IBxvDTBJADTf/De82w==
"@home-based-studio/phaser3-utils@^0.4.7":
version "0.4.7"
resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.4.7.tgz#d0464c81cb27328657d3fd048396f6936e200c48"
integrity sha512-gYt1mkuad85uzYwHK0+wp+mrsGASV4sRZPaHZHnO8A2ofTAnX36S3PcI+BqKchdJ0I7jvBQcfh0yp1Ug0BHT+A==
dependencies:
phaser "3.55.1"
@@ -2060,6 +2068,13 @@ onetime@^5.1.0, onetime@^5.1.2:
dependencies:
mimic-fn "^2.1.0"
openapi3-ts@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/openapi3-ts/-/openapi3-ts-2.0.2.tgz#a200dd838bf24c9086c8eedcfeb380b7eb31e82a"
integrity sha512-TxhYBMoqx9frXyOgnRHufjQfPXomTIHYKhSKJ6jHfj13kS8OEIhvmE8CTuQyKtjjWttAjX5DPxM1vmalEpo8Qw==
dependencies:
yaml "^1.10.2"
optionator@^0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
@@ -2880,6 +2895,11 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
ts-deepmerge@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ts-deepmerge/-/ts-deepmerge-1.1.0.tgz#4236ae102199affe2e77690dcf198a420160eef2"
integrity sha512-VvwaV/6RyYMwT9d8dClmfHIsG2PCdm6WY430QKOIbPRR50Y/1Q2ilp4i2XEZeHFcNqfaYnAQzpyUC6XA0AqqBg==
ts-deferred@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/ts-deferred/-/ts-deferred-1.0.4.tgz#58145ebaeef5b8f2a290b8cec3d060839f9489c7"
@@ -2996,10 +3016,10 @@ type-fest@^0.21.3:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
typesafe-i18n@^2.59.0:
version "2.59.0"
resolved "https://registry.yarnpkg.com/typesafe-i18n/-/typesafe-i18n-2.59.0.tgz#09a9a32e61711418d927a389fa52e1c06a5fa5c4"
integrity sha512-Qv3Mrwmb8b73VNzQDPHPECzwymdBRVyDiZ3w2qnp4c2iv/7TGuiJegNHT/l3MooEN7IPbSpc5tbXw2x3MbGtFg==
typesafe-i18n@^5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/typesafe-i18n/-/typesafe-i18n-5.4.0.tgz#cab696160bb144c387d7cbd13f7a728aa8371777"
integrity sha512-htewpld3FzZQv3Y1G31w54bofaaKR11MCkDK0FIYuXCpX72y1G6fkXUDslqzZCyVkZWRnIhY8leviNDxLwEzRw==
typescript@*:
version "4.3.2"
@@ -3071,6 +3091,11 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
validator@^13.7.0:
version "13.7.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857"
integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==
vite-plugin-rewrite-all@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/vite-plugin-rewrite-all/-/vite-plugin-rewrite-all-0.1.2.tgz#312bbcd76c700ceac5153bfc5ad7e3e3e4bc9606"