Improve game overlay UI

This commit is contained in:
Alexis Faizeau 2022-01-05 10:27:40 +01:00
parent 5f1dd09cb9
commit 0bf1acfefb
63 changed files with 976 additions and 984 deletions

View file

@ -1,166 +1,52 @@
<script lang="typescript"> <script lang="typescript">
import MenuIcon from "./Menu/MenuIcon.svelte";
import { menuIconVisiblilityStore, menuVisiblilityStore } from "../Stores/MenuStore";
import { emoteMenuStore } from "../Stores/EmoteStore";
import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
import CameraControls from "./CameraControls.svelte";
import MyCamera from "./MyCamera.svelte";
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
import { selectCompanionSceneVisibleStore } from "../Stores/SelectCompanionStore";
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte";
import { customCharacterSceneVisibleStore } from "../Stores/CustomCharacterStore";
import { errorStore } from "../Stores/ErrorStore";
import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte";
import LoginScene from "./Login/LoginScene.svelte";
import Chat from "./Chat/Chat.svelte";
import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte";
import VisitCard from "./VisitCard/VisitCard.svelte";
import { requestVisitCardsStore } from "../Stores/GameStore";
import type { Game } from "../Phaser/Game/Game"; import type { Game } from "../Phaser/Game/Game";
import { chatVisibilityStore } from "../Stores/ChatStore"; import { chatVisibilityStore } from "../Stores/ChatStore";
import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore"; import { customCharacterSceneVisibleStore } from "../Stores/CustomCharacterStore";
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte"; import { errorStore } from "../Stores/ErrorStore";
import { showLimitRoomModalStore, showShareLinkMapModalStore } from "../Stores/ModalStore"; import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
import LimitRoomModal from "./Modal/LimitRoomModal.svelte"; import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte"; import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
import AudioPlaying from "./UI/AudioPlaying.svelte"; import { selectCompanionSceneVisibleStore } from "../Stores/SelectCompanionStore";
import { soundPlayingStore } from "../Stores/SoundPlayingStore"; import Chat from "./Chat/Chat.svelte";
import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte";
import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte";
import LoginScene from "./Login/LoginScene.svelte";
import MainLayout from "./MainLayout.svelte";
import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte";
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
import ErrorDialog from "./UI/ErrorDialog.svelte"; import ErrorDialog from "./UI/ErrorDialog.svelte";
import Menu from "./Menu/Menu.svelte";
import EmoteMenu from "./EmoteMenu/EmoteMenu.svelte";
import VideoOverlay from "./Video/VideoOverlay.svelte";
import { gameOverlayVisibilityStore } from "../Stores/GameOverlayStoreVisibility";
import BanMessageContainer from "./TypeMessage/BanMessageContainer.svelte";
import TextMessageContainer from "./TypeMessage/TextMessageContainer.svelte";
import { banMessageStore } from "../Stores/TypeMessageStore/BanMessageStore";
import { textMessageStore } from "../Stores/TypeMessageStore/TextMessageStore";
import { warningContainerStore } from "../Stores/MenuStore";
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
import { layoutManagerVisibilityStore } from "../Stores/LayoutManagerStore";
import LayoutManager from "./LayoutManager/LayoutManager.svelte";
import { audioManagerVisibilityStore } from "../Stores/AudioManagerStore";
import AudioManager from "./AudioManager/AudioManager.svelte";
import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore";
import ReportMenu from "./ReportMenu/ReportMenu.svelte";
import { followStateStore } from "../Stores/FollowStore";
import { peerStore } from "../Stores/PeerStore";
import FollowMenu from "./FollowMenu/FollowMenu.svelte";
export let game: Game; export let game: Game;
</script> </script>
<div> {#if $errorStore.length > 0}
{#if $loginSceneVisibleStore} <div>
<div class="scrollable"> <ErrorDialog />
<LoginScene {game} /> </div>
</div> {:else if $loginSceneVisibleStore}
{/if} <div class="scrollable">
{#if $selectCharacterSceneVisibleStore} <LoginScene {game} />
<div> </div>
<SelectCharacterScene {game} /> {:else if $selectCharacterSceneVisibleStore}
</div> <div>
{/if} <SelectCharacterScene {game} />
{#if $customCharacterSceneVisibleStore} </div>
<div> {:else if $customCharacterSceneVisibleStore}
<CustomCharacterScene {game} /> <div>
</div> <CustomCharacterScene {game} />
{/if} </div>
{#if $selectCompanionSceneVisibleStore} {:else if $selectCompanionSceneVisibleStore}
<div> <div>
<SelectCompanionScene {game} /> <SelectCompanionScene {game} />
</div> </div>
{/if} {:else if $enableCameraSceneVisibilityStore}
{#if $enableCameraSceneVisibilityStore} <div class="scrollable">
<div class="scrollable"> <EnableCameraScene {game} />
<EnableCameraScene {game} /> </div>
</div> {:else}
{/if} <MainLayout />
{#if $banMessageStore.length > 0}
<div>
<BanMessageContainer />
</div>
{:else if $textMessageStore.length > 0}
<div>
<TextMessageContainer />
</div>
{/if}
{#if $soundPlayingStore}
<div>
<AudioPlaying url={$soundPlayingStore} />
</div>
{/if}
{#if $audioManagerVisibilityStore}
<div>
<AudioManager />
</div>
{/if}
{#if $layoutManagerVisibilityStore}
<div>
<LayoutManager />
</div>
{/if}
{#if $showReportScreenStore !== userReportEmpty}
<div>
<ReportMenu />
</div>
{/if}
{#if $followStateStore !== "off" || $peerStore.size > 0}
<div>
<FollowMenu />
</div>
{/if}
{#if $menuIconVisiblilityStore}
<div>
<MenuIcon />
</div>
{/if}
{#if $menuVisiblilityStore}
<div>
<Menu />
</div>
{/if}
{#if $emoteMenuStore}
<div>
<EmoteMenu />
</div>
{/if}
{#if $gameOverlayVisibilityStore}
<div>
<VideoOverlay />
<MyCamera />
<CameraControls />
</div>
{/if}
{#if $helpCameraSettingsVisibleStore}
<div>
<HelpCameraSettingsPopup />
</div>
{/if}
{#if $showLimitRoomModalStore}
<div>
<LimitRoomModal />
</div>
{/if}
{#if $showShareLinkMapModalStore}
<div>
<ShareLinkMapModal />
</div>
{/if}
{#if $requestVisitCardsStore}
<VisitCard visitCardUrl={$requestVisitCardsStore} />
{/if}
{#if $errorStore.length > 0}
<div>
<ErrorDialog />
</div>
{/if}
{#if $chatVisibilityStore} {#if $chatVisibilityStore}
<Chat /> <Chat />
{/if} {/if}
{#if $warningContainerStore} {/if}
<WarningContainer />
{/if}
</div>

View file

@ -157,13 +157,16 @@
<style lang="scss"> <style lang="scss">
div.main-audio-manager.nes-container.is-rounded { div.main-audio-manager.nes-container.is-rounded {
position: relative; position: absolute;
top: 0.5rem; top: 1%;
max-height: clamp(150px, 10vh, 15vh); //replace @media for small screen max-height: clamp(150px, 10vh, 15vh); //replace @media for small screen
width: clamp(200px, 15vw, 15vw); width: clamp(200px, 15vw, 15vw);
padding: 3px 3px; padding: 3px 3px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
left: 0;
right: 0;
z-index: 200;
background-color: rgb(0, 0, 0, 0.5); background-color: rgb(0, 0, 0, 0.5);
display: grid; display: grid;

View file

@ -9,10 +9,15 @@
import microphoneCloseImg from "./images/microphone-close.svg"; import microphoneCloseImg from "./images/microphone-close.svg";
import layoutPresentationImg from "./images/layout-presentation.svg"; import layoutPresentationImg from "./images/layout-presentation.svg";
import layoutChatImg from "./images/layout-chat.svg"; import layoutChatImg from "./images/layout-chat.svg";
import { layoutModeStore } from "../Stores/StreamableCollectionStore"; import followImg from "./images/follow.svg";
import { LayoutMode } from "../WebRtc/LayoutManager"; import { LayoutMode } from "../WebRtc/LayoutManager";
import { peerStore } from "../Stores/PeerStore"; import { peerStore } from "../Stores/PeerStore";
import { onDestroy } from "svelte"; import { onDestroy } from "svelte";
import { embedScreenLayout } from "../Stores/EmbedScreensStore";
import { followRoleStore, followStateStore, followUsersStore } from "../Stores/FollowStore";
import { gameManager } from "../Phaser/Game/GameManager";
const gameScene = gameManager.getCurrentGameScene();
function screenSharingClick(): void { function screenSharingClick(): void {
if (isSilent) return; if (isSilent) return;
@ -42,10 +47,26 @@
} }
function switchLayoutMode() { function switchLayoutMode() {
if ($layoutModeStore === LayoutMode.Presentation) { if ($embedScreenLayout === LayoutMode.Presentation) {
$layoutModeStore = LayoutMode.VideoChat; $embedScreenLayout = LayoutMode.VideoChat;
} else { } else {
$layoutModeStore = LayoutMode.Presentation; $embedScreenLayout = LayoutMode.Presentation;
}
}
function followClick() {
switch ($followStateStore) {
case "off":
gameScene.connection?.emitFollowRequest();
followRoleStore.set("leader");
followStateStore.set("active");
break;
case "requesting":
case "active":
case "ending":
gameScene.connection?.emitFollowAbort();
followUsersStore.stopFollowing();
break;
} }
} }
@ -56,40 +77,161 @@
onDestroy(unsubscribeIsSilent); onDestroy(unsubscribeIsSilent);
</script> </script>
<div> <div class="btn-cam-action">
<div class="btn-cam-action"> <div class="btn-layout" on:click={switchLayoutMode} class:hide={$peerStore.size === 0}>
<div class="btn-layout" on:click={switchLayoutMode} class:hide={$peerStore.size === 0}> {#if $embedScreenLayout === LayoutMode.Presentation}
{#if $layoutModeStore === LayoutMode.Presentation} <img class="noselect" src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode" />
<img src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode" /> {:else}
{:else} <img class="noselect" src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode" />
<img src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode" /> {/if}
{/if} </div>
</div>
<div <div
class="btn-monitor" class="btn-follow"
on:click={screenSharingClick} class:hide={($peerStore.size === 0 && $followStateStore === "off") || isSilent}
class:hide={!$screenSharingAvailableStore || isSilent} class:disabled={$followStateStore !== "off"}
class:enabled={$requestedScreenSharingState} on:click={followClick}
> >
{#if $requestedScreenSharingState && !isSilent} <img class="noselect" src={followImg} alt="" />
<img src={monitorImg} alt="Start screen sharing" /> </div>
{:else}
<img src={monitorCloseImg} alt="Stop screen sharing" /> <div
{/if} class="btn-monitor"
</div> on:click={screenSharingClick}
<div class="btn-video" on:click={cameraClick} class:disabled={!$requestedCameraState || isSilent}> class:hide={!$screenSharingAvailableStore || isSilent}
{#if $requestedCameraState && !isSilent} class:enabled={$requestedScreenSharingState}
<img src={cinemaImg} alt="Turn on webcam" /> >
{:else} {#if $requestedScreenSharingState && !isSilent}
<img src={cinemaCloseImg} alt="Turn off webcam" /> <img class="noselect" src={monitorImg} alt="Start screen sharing" />
{/if} {:else}
</div> <img class="noselect" src={monitorCloseImg} alt="Stop screen sharing" />
<div class="btn-micro" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || isSilent}> {/if}
{#if $requestedMicrophoneState && !isSilent} </div>
<img src={microphoneImg} alt="Turn on microphone" />
{:else} <div class="btn-video" on:click={cameraClick} class:disabled={!$requestedCameraState || isSilent}>
<img src={microphoneCloseImg} alt="Turn off microphone" /> {#if $requestedCameraState && !isSilent}
{/if} <img class="noselect" src={cinemaImg} alt="Turn on webcam" />
</div> {:else}
<img class="noselect" src={cinemaCloseImg} alt="Turn off webcam" />
{/if}
</div>
<div class="btn-micro" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || isSilent}>
{#if $requestedMicrophoneState && !isSilent}
<img class="noselect" src={microphoneImg} alt="Turn on microphone" />
{:else}
<img class="noselect" src={microphoneCloseImg} alt="Turn off microphone" />
{/if}
</div> </div>
</div> </div>
<style lang="scss">
@import "../../style/breakpoints.scss";
.btn-cam-action {
pointer-events: all;
position: absolute;
display: inline-flex;
bottom: 10px;
right: 15px;
width: 360px;
height: 40px;
text-align: center;
align-content: center;
justify-content: flex-end;
&:hover {
div.hide {
transform: translateY(60px);
}
}
}
/*btn animation*/
.btn-cam-action div {
cursor: url("../../style/images/cursor_pointer.png"), pointer;
display: flex;
align-items: center;
justify-content: center;
border: solid 0px black;
width: 44px;
height: 44px;
background: #666;
box-shadow: 2px 2px 24px #444;
border-radius: 48px;
transform: translateY(15px);
transition-timing-function: ease-in-out;
transition: all 0.3s;
margin: 0 4%;
&.hide {
transform: translateY(60px);
}
}
.btn-cam-action div.disabled {
background: #d75555;
}
.btn-cam-action div.enabled {
background: #73c973;
}
.btn-cam-action:hover div {
transform: translateY(0);
}
.btn-cam-action div:hover {
background: #407cf7;
box-shadow: 4px 4px 48px #666;
transition: 120ms;
}
.btn-micro {
pointer-events: auto;
}
.btn-video {
pointer-events: auto;
transition: all 0.25s;
}
.btn-monitor {
pointer-events: auto;
}
.btn-layout {
pointer-events: auto;
transition: all 0.15s;
}
.btn-cam-action div img {
height: 22px;
width: 30px;
position: relative;
cursor: url("../../style/images/cursor_pointer.png"), pointer;
}
.btn-follow {
pointer-events: auto;
img {
filter: brightness(0) invert(1);
}
}
@media (hover: none) {
/**
* If we cannot hover over elements, let's display camera button in full.
*/
.btn-cam-action {
div {
transform: translateY(0px);
}
}
}
@include media-breakpoint-up(sm) {
.btn-cam-action {
right: 0;
width: 100%;
height: 40%;
max-height: 40px;
div {
width: 20%;
max-height: 44px;
}
}
}
</style>

View file

@ -43,7 +43,7 @@
<svelte:window on:keydown={onKeyDown} on:click={onClick} /> <svelte:window on:keydown={onKeyDown} on:click={onClick} />
<aside class="chatWindow" transition:fly={{ x: -1000, duration: 500 }} bind:this={chatWindowElement}> <aside class="chatWindow" transition:fly={{ x: -1000, duration: 500 }} bind:this={chatWindowElement}>
<p class="close-icon" on:click={closeChat}>&times</p> <p class="close-icon noselect" on:click={closeChat}>&times</p>
<section class="messagesList" bind:this={listDom}> <section class="messagesList" bind:this={listDom}>
<ul> <ul>
<li><p class="system-text">{$LL.chat.intro()}</p></li> <li><p class="system-text">{$LL.chat.intro()}</p></li>
@ -78,7 +78,7 @@
} }
aside.chatWindow { aside.chatWindow {
z-index: 100; z-index: 1000;
pointer-events: auto; pointer-events: auto;
position: absolute; position: absolute;
top: 0; top: 0;

View file

@ -83,6 +83,8 @@
</form> </form>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
form.customCharacterScene { form.customCharacterScene {
font-family: "Press Start 2P"; font-family: "Press Start 2P";
pointer-events: auto; pointer-events: auto;
@ -129,7 +131,7 @@
} }
} }
@media only screen and (max-width: 800px) { @include media-breakpoint-up(md) {
form.customCharacterScene button.customCharacterSceneButtonLeft { form.customCharacterScene button.customCharacterSceneButtonLeft {
left: 5vw; left: 5vw;
} }

View file

@ -3,8 +3,8 @@
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore"; import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
import { onDestroy, onMount } from "svelte"; import { onDestroy, onMount } from "svelte";
import { EmojiButton } from "@joeattardi/emoji-button"; import { EmojiButton } from "@joeattardi/emoji-button";
import { isMobile } from "../../Enum/EnvironmentVariable";
import LL from "../../i18n/i18n-svelte"; import LL from "../../i18n/i18n-svelte";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
let emojiContainer: HTMLElement; let emojiContainer: HTMLElement;
let picker: EmojiButton; let picker: EmojiButton;
@ -20,7 +20,7 @@
"--secondary-text-color": "whitesmoke", "--secondary-text-color": "whitesmoke",
"--category-button-color": "whitesmoke", "--category-button-color": "whitesmoke",
}, },
emojisPerRow: isMobile() ? 6 : 8, emojisPerRow: isMediaBreakpointUp("md") ? 6 : 8,
autoFocusSearch: false, autoFocusSearch: false,
style: "twemoji", style: "twemoji",
showPreview: false, showPreview: false,
@ -86,6 +86,8 @@
height: 100%; height: 100%;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
position: absolute;
z-index: 101;
} }
.emote-menu { .emote-menu {

View file

@ -127,6 +127,8 @@
</form> </form>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
.enableCameraScene { .enableCameraScene {
pointer-events: auto; pointer-events: auto;
margin: 20px auto 0; margin: 20px auto 0;
@ -214,7 +216,7 @@
} }
} }
@media only screen and (max-width: 800px) { @include media-breakpoint-up(md) {
.enableCameraScene h2 { .enableCameraScene h2 {
font-size: 80%; font-size: 80%;
} }

View file

@ -0,0 +1,33 @@
<script lang="typescript">
import followImg from "../images/follow.svg";
export let hidden: Boolean;
let cancelButton = false;
</script>
<div class="btn-follow" class:hide={hidden} class:cancel={cancelButton}>
<img src={followImg} alt="" />
</div>
<style lang="scss">
.btn-follow {
cursor: url("../../../style/images/cursor_pointer.png"), pointer;
display: flex;
align-items: center;
justify-content: center;
border: solid 0px black;
width: 44px;
height: 44px;
background: #666;
box-shadow: 2px 2px 24px #444;
border-radius: 48px;
transform: translateY(15px);
transition-timing-function: ease-in-out;
margin: 0 4%;
img {
filter: brightness(0) invert(1);
}
}
</style>

View file

@ -1,9 +1,5 @@
<!--
vim: ft=typescript
-->
<script lang="ts"> <script lang="ts">
import { gameManager } from "../../Phaser/Game/GameManager"; import { gameManager } from "../../Phaser/Game/GameManager";
import followImg from "../images/follow.svg";
import { followStateStore, followRoleStore, followUsersStore } from "../../Stores/FollowStore"; import { followStateStore, followRoleStore, followUsersStore } from "../../Stores/FollowStore";
import LL from "../../i18n/i18n-svelte"; import LL from "../../i18n/i18n-svelte";
@ -14,10 +10,6 @@ vim: ft=typescript
return user ? user.PlayerValue : ""; return user ? user.PlayerValue : "";
} }
function sendFollowRequest() {
gameScene.CurrentPlayer.sendFollowRequest();
}
function acceptFollowRequest() { function acceptFollowRequest() {
gameScene.CurrentPlayer.startFollowing(); gameScene.CurrentPlayer.startFollowing();
} }
@ -83,7 +75,7 @@ vim: ft=typescript
{#if $followStateStore === "active" || $followStateStore === "ending"} {#if $followStateStore === "active" || $followStateStore === "ending"}
<div class="interact-status nes-container is-rounded"> <div class="interact-status nes-container is-rounded">
<section class="interact-status"> <section>
{#if $followRoleStore === "follower"} {#if $followRoleStore === "follower"}
<p>{$LL.follow.interactStatus.following({ leader: name($followUsersStore[0]) })}</p> <p>{$LL.follow.interactStatus.following({ leader: name($followUsersStore[0]) })}</p>
{:else if $followUsersStore.length === 0} {:else if $followUsersStore.length === 0}
@ -109,48 +101,27 @@ vim: ft=typescript
</div> </div>
{/if} {/if}
{#if $followStateStore === "off"}
<button
type="button"
class="nes-btn is-primary follow-menu-button"
on:click|preventDefault={sendFollowRequest}
title="Ask others to follow"><img class="background-img" src={followImg} alt="" /></button
>
{/if}
{#if $followStateStore === "active" || $followStateStore === "ending"}
{#if $followRoleStore === "follower"}
<button
type="button"
class="nes-btn is-error follow-menu-button"
on:click|preventDefault={reset}
title="Stop following"><img class="background-img" src={followImg} alt="" /></button
>
{:else}
<button
type="button"
class="nes-btn is-error follow-menu-button"
on:click|preventDefault={reset}
title="Stop leading the way"><img class="background-img" src={followImg} alt="" /></button
>
{/if}
{/if}
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
.nes-container { .nes-container {
padding: 5px; padding: 5px;
} }
div.interact-status { .interact-status {
background-color: #333333; background-color: #333333;
color: whitesmoke; color: whitesmoke;
position: relative; position: absolute;
height: 2.7em; max-height: 2.7em;
width: 40vw; width: 40vw;
top: 87vh; top: 87vh;
margin: auto;
text-align: center; text-align: center;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
z-index: 150;
} }
div.interact-menu { div.interact-menu {
@ -158,10 +129,14 @@ vim: ft=typescript
background-color: #333333; background-color: #333333;
color: whitesmoke; color: whitesmoke;
position: relative; position: absolute;
width: 60vw; width: 60vw;
top: 60vh; top: 60vh;
margin: auto; left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
z-index: 150;
section.interact-menu-title { section.interact-menu-title {
margin-bottom: 20px; margin-bottom: 20px;
@ -189,23 +164,16 @@ vim: ft=typescript
} }
} }
.follow-menu-button { @include media-breakpoint-up(md) {
position: absolute; .interact-status {
bottom: 10px; width: 90vw;
left: 10px;
pointer-events: all;
}
@media only screen and (max-width: 800px) {
div.interact-status {
width: 100vw;
top: 78vh; top: 78vh;
font-size: 0.75em; font-size: 0.75em;
} }
div.interact-menu { div.interact-menu {
height: 21vh; max-height: 21vh;
width: 100vw; width: 90vw;
font-size: 0.75em; font-size: 0.75em;
} }
} }

View file

@ -22,7 +22,7 @@
<form <form
class="helpCameraSettings nes-container" class="helpCameraSettings nes-container"
on:submit|preventDefault={close} on:submit|preventDefault={close}
transition:fly={{ y: -900, duration: 500 }} transition:fly={{ y: -50, duration: 500 }}
> >
<section> <section>
<h2>{$LL.camera.help.title()}</h2> <h2>{$LL.camera.help.title()}</h2>
@ -55,9 +55,12 @@
background: #eceeee; background: #eceeee;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
margin-top: 10vh; left: 0;
right: 0;
margin-top: 4%;
max-height: 80vh; max-height: 80vh;
max-width: 80vw; max-width: 80vw;
z-index: 250;
overflow: auto; overflow: auto;
text-align: center; text-align: center;

View file

@ -21,9 +21,11 @@
left: 0; left: 0;
right: 0; right: 0;
bottom: 40px; bottom: 40px;
margin: 0 auto; margin-right: auto;
margin-left: auto;
padding: 0; padding: 0;
width: clamp(200px, 20vw, 20vw); width: clamp(200px, 20vw, 20vw);
z-index: 155;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -31,6 +33,10 @@
animation: moveMessage 0.5s; animation: moveMessage 0.5s;
animation-iteration-count: infinite; animation-iteration-count: infinite;
animation-timing-function: ease-in-out; animation-timing-function: ease-in-out;
div {
margin-bottom: 5%;
}
} }
div.nes-container.is-rounded { div.nes-container.is-rounded {

View file

@ -0,0 +1,163 @@
<script lang="typescript">
import { onMount } from "svelte";
import { audioManagerVisibilityStore } from "../Stores/AudioManagerStore";
import { hasEmbedScreen } from "../Stores/EmbedScreensStore";
import { emoteMenuStore } from "../Stores/EmoteStore";
import { myCameraVisibilityStore } from "../Stores/MyCameraStoreVisibility";
import { requestVisitCardsStore } from "../Stores/GameStore";
import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore";
import { layoutManagerActionVisibilityStore } from "../Stores/LayoutManagerStore";
import { menuIconVisiblilityStore, menuVisiblilityStore, warningContainerStore } from "../Stores/MenuStore";
import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore";
import AudioManager from "./AudioManager/AudioManager.svelte";
import CameraControls from "./CameraControls.svelte";
import EmbedScreensContainer from "./EmbedScreens/EmbedScreensContainer.svelte";
import EmoteMenu from "./EmoteMenu/EmoteMenu.svelte";
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte";
import LayoutActionManager from "./LayoutActionManager/LayoutActionManager.svelte";
import Menu from "./Menu/Menu.svelte";
import MenuIcon from "./Menu/MenuIcon.svelte";
import MyCamera from "./MyCamera.svelte";
import ReportMenu from "./ReportMenu/ReportMenu.svelte";
import VisitCard from "./VisitCard/VisitCard.svelte";
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
import CoWebsitesContainer from "./EmbedScreens/CoWebsitesContainer.svelte";
import FollowMenu from "./FollowMenu/FollowMenu.svelte";
import { followStateStore } from "../Stores/FollowStore";
import { peerStore } from "../Stores/PeerStore";
import { banMessageStore } from "../Stores/TypeMessageStore/BanMessageStore";
import BanMessageContainer from "./TypeMessage/BanMessageContainer.svelte";
import { textMessageStore } from "../Stores/TypeMessageStore/TextMessageStore";
import TextMessageContainer from "./TypeMessage/TextMessageContainer.svelte";
import { soundPlayingStore } from "../Stores/SoundPlayingStore";
import AudioPlaying from "./UI/AudioPlaying.svelte";
import { showLimitRoomModalStore, showShareLinkMapModalStore } from "../Stores/ModalStore";
import LimitRoomModal from "./Modal/LimitRoomModal.svelte";
import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
let mainLayout: HTMLDivElement;
let displayCoWebsiteContainer = isMediaBreakpointUp("md");
const resizeObserver = new ResizeObserver(() => {
displayCoWebsiteContainer = isMediaBreakpointUp("md");
});
onMount(() => {
resizeObserver.observe(mainLayout);
});
</script>
<div id="main-layout" bind:this={mainLayout}>
<aside id="main-layout-left-aside">
{#if $menuIconVisiblilityStore}
<MenuIcon />
{/if}
{#if displayCoWebsiteContainer}
<CoWebsitesContainer vertical={true} />
{/if}
</aside>
<section id="main-layout-main">
{#if $menuVisiblilityStore}
<Menu />
{/if}
{#if $banMessageStore.length > 0}
<BanMessageContainer />
{:else if $textMessageStore.length > 0}
<TextMessageContainer />
{/if}
{#if $soundPlayingStore}
<AudioPlaying url={$soundPlayingStore} />
{/if}
{#if $showReportScreenStore !== userReportEmpty}
<ReportMenu />
{/if}
{#if $warningContainerStore}
<WarningContainer />
{/if}
{#if $helpCameraSettingsVisibleStore}
<HelpCameraSettingsPopup />
{/if}
{#if $audioManagerVisibilityStore}
<AudioManager />
{/if}
{#if $showLimitRoomModalStore}
<LimitRoomModal />
{/if}
{#if $showShareLinkMapModalStore}
<ShareLinkMapModal />
{/if}
{#if $followStateStore !== "off" || $peerStore.size > 0}
<FollowMenu />
{/if}
{#if $requestVisitCardsStore}
<VisitCard visitCardUrl={$requestVisitCardsStore} />
{/if}
{#if $emoteMenuStore}
<EmoteMenu />
{/if}
{#if hasEmbedScreen}
<EmbedScreensContainer />
{/if}
</section>
<section id="main-layout-baseline">
{#if $layoutManagerActionVisibilityStore}
<LayoutActionManager />
{/if}
{#if $myCameraVisibilityStore}
<MyCamera />
<CameraControls />
{/if}
</section>
</div>
<style lang="scss">
@import "../../style/breakpoints.scss";
#main-layout {
display: grid;
grid-template-columns: 7% 93%;
grid-template-rows: 80% 20%;
&-left-aside {
min-width: 80px;
}
&-baseline {
grid-column: 1/3;
}
}
@include media-breakpoint-up(md) {
#main-layout {
grid-template-columns: 15% 85%;
&-left-aside {
min-width: auto;
}
}
}
@include media-breakpoint-up(sm) {
#main-layout {
grid-template-columns: 20% 80%;
}
}
</style>

View file

@ -100,6 +100,8 @@
</div> </div>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
.string-HTML { .string-HTML {
white-space: pre-line; white-space: pre-line;
} }
@ -126,7 +128,7 @@
} }
} }
@media only screen and (max-width: 800px), only screen and (max-height: 800px) { @include media-breakpoint-up(md) {
div.about-room-main { div.about-room-main {
section.container-overflow { section.container-overflow {
height: calc(100% - 120px); height: calc(100% - 120px);

View file

@ -67,6 +67,8 @@
</div> </div>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
div.global-message-main { div.global-message-main {
height: calc(100% - 50px); height: calc(100% - 50px);
display: grid; display: grid;
@ -109,7 +111,7 @@
} }
} }
@media only screen and (max-width: 800px), only screen and (max-height: 800px) { @include media-breakpoint-up(md) {
.global-message-content { .global-message-content {
height: calc(100% - 5px); height: calc(100% - 5px);
} }

View file

@ -36,6 +36,8 @@
</div> </div>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
div.guest-main { div.guest-main {
height: calc(100% - 56px); height: calc(100% - 56px);
@ -57,7 +59,7 @@
} }
} }
@media only screen and (max-width: 900px), only screen and (max-height: 600px) { @include media-breakpoint-up(md) {
div.guest-main { div.guest-main {
section.share-url.not-mobile { section.share-url.not-mobile {
display: none; display: none;

View file

@ -125,6 +125,8 @@
</div> </div>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
.nes-container { .nes-container {
padding: 5px; padding: 5px;
} }
@ -136,11 +138,15 @@
pointer-events: auto; pointer-events: auto;
height: 80%; height: 80%;
width: 75%; width: 75%;
top: 10%; top: 4%;
position: relative; left: 0;
z-index: 80; right: 0;
margin: auto; margin-left: auto;
margin-right: auto;
position: absolute;
z-index: 900;
display: grid; display: grid;
grid-template-columns: var(--size-first-columns-grid) calc(100% - var(--size-first-columns-grid)); grid-template-columns: var(--size-first-columns-grid) calc(100% - var(--size-first-columns-grid));
@ -173,12 +179,12 @@
} }
} }
@media only screen and (max-width: 800px) { @include media-breakpoint-up(md) {
div.menu-container-main { div.menu-container-main {
--size-first-columns-grid: 120px; --size-first-columns-grid: 120px;
height: 70%; height: 70%;
top: 55px; top: 55px;
width: 100%; width: 95%;
font-size: 0.5em; font-size: 0.5em;
div.menu-nav-sidebar { div.menu-nav-sidebar {

View file

@ -14,6 +14,7 @@
function showMenu() { function showMenu() {
menuVisiblilityStore.set(!get(menuVisiblilityStore)); menuVisiblilityStore.set(!get(menuVisiblilityStore));
} }
function showChat() { function showChat() {
chatVisibilityStore.set(true); chatVisibilityStore.set(true);
} }
@ -21,72 +22,94 @@
function register() { function register() {
window.open(`${ADMIN_URL}/second-step-register`, "_self"); window.open(`${ADMIN_URL}/second-step-register`, "_self");
} }
function showInvite() { function showInvite() {
showShareLinkMapModalStore.set(true); showShareLinkMapModalStore.set(true);
} }
function noDrag() {
return false;
}
</script> </script>
<svelte:window /> <svelte:window />
<main class="menuIcon"> <main class="menuIcon noselect">
{#if $limitMapStore} {#if $limitMapStore}
<img <img
src={logoInvite} src={logoInvite}
alt={$LL.menu.icon.open.invite()} alt={$LL.menu.icon.open.invite()}
class="nes-pointer" class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showInvite} on:click|preventDefault={showInvite}
/> />
<img <img
src={logoRegister} src={logoRegister}
alt={$LL.menu.icon.open.register()} alt={$LL.menu.icon.open.register()}
class="nes-pointer" class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={register} on:click|preventDefault={register}
/> />
{:else} {:else}
<img src={logoWA} alt={$LL.menu.icon.open.menu()} class="nes-pointer" on:click|preventDefault={showMenu} /> <img
<img src={logoTalk} alt={$LL.menu.icon.open.chat()} class="nes-pointer" on:click|preventDefault={showChat} /> src={logoWA}
alt={$LL.menu.icon.open.menu()}
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showMenu}
/>
<img
src={logoTalk}
alt={$LL.menu.icon.open.chat()}
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showChat}
/>
{/if} {/if}
</main> </main>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
.menuIcon { .menuIcon {
display: inline-grid; display: flex;
z-index: 90; flex-direction: column;
align-items: center;
margin-top: 20%;
z-index: 800;
position: relative; position: relative;
margin: 25px;
img { img {
pointer-events: auto; pointer-events: auto;
width: 60px; width: 60px;
padding-top: 0; padding-top: 0;
margin: 3px; margin: 5%;
image-rendering: pixelated;
} }
} }
.menuIcon img:hover { .menuIcon img:hover {
transform: scale(1.2); transform: scale(1.2);
} }
@media only screen and (max-width: 800px), only screen and (max-height: 800px) { @include media-breakpoint-up(sm) {
.menuIcon { .menuIcon {
display: inline-grid; margin-top: 10%;
z-index: 90;
position: relative;
margin: 25px;
img { img {
pointer-events: auto; pointer-events: auto;
width: 60px; width: 60px;
padding-top: 0; padding-top: 0;
margin: 3px;
} }
} }
.menuIcon img:hover { .menuIcon img:hover {
transform: scale(1.2); transform: scale(1.2);
} }
@media only screen and (max-width: 800px), only screen and (max-height: 800px) { }
.menuIcon {
margin: 3px; @include media-breakpoint-up(md) {
img { .menuIcon {
width: 50px; img {
} width: 50px;
} }
} }
} }

View file

@ -102,6 +102,8 @@
</div> </div>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
div.customize-main { div.customize-main {
width: 100%; width: 100%;
display: inline-flex; display: inline-flex;
@ -161,7 +163,7 @@
} }
} }
@media only screen and (max-width: 800px) { @include media-breakpoint-up(md) {
div.customize-main.content section button { div.customize-main.content section button {
width: 130px; width: 130px;
} }

View file

@ -2,11 +2,11 @@
import { localUserStore } from "../../Connexion/LocalUserStore"; import { localUserStore } from "../../Connexion/LocalUserStore";
import { videoConstraintStore } from "../../Stores/MediaStore"; import { videoConstraintStore } from "../../Stores/MediaStore";
import { HtmlUtils } from "../../WebRtc/HtmlUtils"; import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { isMobile } from "../../Enum/EnvironmentVariable";
import { menuVisiblilityStore } from "../../Stores/MenuStore"; import { menuVisiblilityStore } from "../../Stores/MenuStore";
import LL, { locale } from "../../i18n/i18n-svelte"; import LL, { locale } from "../../i18n/i18n-svelte";
import type { Locales } from "../../i18n/i18n-types"; import type { Locales } from "../../i18n/i18n-types";
import { displayableLocales, setCurrentLocale } from "../../i18n/locales"; import { displayableLocales, setCurrentLocale } from "../../i18n/locales";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
let fullscreen: boolean = localUserStore.getFullscreen(); let fullscreen: boolean = localUserStore.getFullscreen();
let notification: boolean = localUserStore.getNotification() === "granted"; let notification: boolean = localUserStore.getNotification() === "granted";
@ -85,6 +85,8 @@
function closeMenu() { function closeMenu() {
menuVisiblilityStore.set(false); menuVisiblilityStore.set(false);
} }
const isMobile = isMediaBreakpointUp("md");
</script> </script>
<div class="settings-main" on:submit|preventDefault={saveSetting}> <div class="settings-main" on:submit|preventDefault={saveSetting}>
@ -93,22 +95,22 @@
<div class="nes-select is-dark"> <div class="nes-select is-dark">
<select bind:value={valueGame}> <select bind:value={valueGame}>
<option value={120} <option value={120}
>{isMobile() >{isMobile
? $LL.menu.settings.gameQuality.short.high() ? $LL.menu.settings.gameQuality.short.high()
: $LL.menu.settings.gameQuality.long.high()}</option : $LL.menu.settings.gameQuality.long.high()}</option
> >
<option value={60} <option value={60}
>{isMobile() >{isMobile
? $LL.menu.settings.gameQuality.short.medium() ? $LL.menu.settings.gameQuality.short.medium()
: $LL.menu.settings.gameQuality.long.medium()}</option : $LL.menu.settings.gameQuality.long.medium()}</option
> >
<option value={40} <option value={40}
>{isMobile() >{isMobile
? $LL.menu.settings.gameQuality.short.small() ? $LL.menu.settings.gameQuality.short.small()
: $LL.menu.settings.gameQuality.long.small()}</option : $LL.menu.settings.gameQuality.long.small()}</option
> >
<option value={20} <option value={20}
>{isMobile() >{isMobile
? $LL.menu.settings.gameQuality.short.minimum() ? $LL.menu.settings.gameQuality.short.minimum()
: $LL.menu.settings.gameQuality.long.minimum()}</option : $LL.menu.settings.gameQuality.long.minimum()}</option
> >
@ -120,22 +122,22 @@
<div class="nes-select is-dark"> <div class="nes-select is-dark">
<select bind:value={valueVideo}> <select bind:value={valueVideo}>
<option value={30} <option value={30}
>{isMobile() >{isMobile
? $LL.menu.settings.videoQuality.short.high() ? $LL.menu.settings.videoQuality.short.high()
: $LL.menu.settings.videoQuality.long.high()}</option : $LL.menu.settings.videoQuality.long.high()}</option
> >
<option value={20} <option value={20}
>{isMobile() >{isMobile
? $LL.menu.settings.videoQuality.short.medium() ? $LL.menu.settings.videoQuality.short.medium()
: $LL.menu.settings.videoQuality.long.medium()}</option : $LL.menu.settings.videoQuality.long.medium()}</option
> >
<option value={10} <option value={10}
>{isMobile() >{isMobile
? $LL.menu.settings.videoQuality.short.small() ? $LL.menu.settings.videoQuality.short.small()
: $LL.menu.settings.videoQuality.long.small()}</option : $LL.menu.settings.videoQuality.long.small()}</option
> >
<option value={5} <option value={5}
>{isMobile() >{isMobile
? $LL.menu.settings.videoQuality.short.minimum() ? $LL.menu.settings.videoQuality.short.minimum()
: $LL.menu.settings.videoQuality.long.minimum()}</option : $LL.menu.settings.videoQuality.long.minimum()}</option
> >
@ -199,6 +201,8 @@
</div> </div>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
div.settings-main { div.settings-main {
height: calc(100% - 40px); height: calc(100% - 40px);
overflow-y: auto; overflow-y: auto;
@ -236,7 +240,7 @@
} }
} }
@media only screen and (max-width: 800px), only screen and (max-height: 800px) { @include media-breakpoint-up(md) {
div.settings-main { div.settings-main {
section { section {
padding: 0; padding: 0;

View file

@ -2,7 +2,7 @@
import { obtainedMediaConstraintStore } from "../Stores/MediaStore"; import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
import { localStreamStore, isSilentStore } from "../Stores/MediaStore"; import { localStreamStore, isSilentStore } from "../Stores/MediaStore";
import SoundMeterWidget from "./SoundMeterWidget.svelte"; import SoundMeterWidget from "./SoundMeterWidget.svelte";
import { onDestroy } from "svelte"; import { onDestroy, onMount } from "svelte";
import { srcObject } from "./Video/utils"; import { srcObject } from "./Video/utils";
import LL from "../i18n/i18n-svelte"; import LL from "../i18n/i18n-svelte";
@ -23,15 +23,77 @@
isSilent = value; isSilent = value;
}); });
let cameraContainer: HTMLDivElement;
onMount(() => {
cameraContainer.addEventListener("transitionend", () => {
if (cameraContainer.classList.contains("hide")) {
cameraContainer.style.visibility = "hidden";
}
});
cameraContainer.addEventListener("transitionstart", () => {
if (!cameraContainer.classList.contains("hide")) {
cameraContainer.style.visibility = "visible";
}
});
});
onDestroy(unsubscribeIsSilent); onDestroy(unsubscribeIsSilent);
</script> </script>
<div> <div
<div class="video-container div-myCamVideo" class:hide={!$obtainedMediaConstraintStore.video || isSilent}> class="nes-container is-rounded my-cam-video-container"
{#if $localStreamStore.type === "success" && $localStreamStore.stream} class:hide={($localStreamStore.type !== "success" || !$obtainedMediaConstraintStore.video) && !isSilent}
<video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline /> bind:this={cameraContainer}
<SoundMeterWidget {stream} /> >
{/if} {#if isSilent}
</div> <div class="is-silent">{$LL.camera.my.silentZone()}</div>
<div class="is-silent" class:hide={isSilent}>{$LL.camera.my.silentZone()}</div> {:else if $localStreamStore.type === "success" && $localStreamStore.stream}
<video class="my-cam-video" use:srcObject={stream} autoplay muted playsinline />
<SoundMeterWidget {stream} />
{/if}
</div> </div>
<style lang="scss">
@import "../../style/breakpoints.scss";
.my-cam-video-container {
position: absolute;
right: 15px;
bottom: 30px;
max-height: 20%;
transition: transform 1000ms;
padding: 0;
background-color: #00000099;
overflow: hidden;
}
.my-cam-video-container.hide {
transform: translateX(200%);
}
.my-cam-video {
background-color: #00000099;
max-height: 20vh;
width: 100%;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.is-silent {
font-size: 2em;
color: white;
padding: 40px 20px;
}
@include media-breakpoint-up(md) {
.my-cam-video {
width: 150px;
}
.my-cam-video-container.hide {
right: -160px;
}
}
</style>

View file

@ -108,12 +108,16 @@
pointer-events: auto; pointer-events: auto;
background-color: #333333; background-color: #333333;
color: whitesmoke; color: whitesmoke;
z-index: 300;
position: relative; position: absolute;
height: 70vh; height: 70vh;
width: 50vw; width: 50vw;
top: 10vh; top: 4%;
margin: auto;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
section.report-menu-title { section.report-menu-title {
display: grid; display: grid;
@ -137,13 +141,4 @@
display: none; display: none;
} }
} }
@media only screen and (max-width: 800px) {
div.report-menu-main {
top: 21vh;
height: 60vh;
width: 100vw;
font-size: 0.5em;
}
}
</style> </style>

View file

@ -47,6 +47,8 @@
</form> </form>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
form.selectCompanionScene { form.selectCompanionScene {
font-family: "Press Start 2P"; font-family: "Press Start 2P";
pointer-events: auto; pointer-events: auto;
@ -85,7 +87,7 @@
} }
} }
@media only screen and (max-width: 800px) { @include media-breakpoint-up(md) {
form.selectCompanionScene button.selectCharacterButtonLeft { form.selectCompanionScene button.selectCharacterButtonLeft {
left: 5vw; left: 5vw;
} }

View file

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { fly, fade } from "svelte/transition"; import { fly, fade } from "svelte/transition";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { gameManager } from "../../Phaser/Game/GameManager";
import type { Message } from "../../Stores/TypeMessageStore/MessageStore"; import type { Message } from "../../Stores/TypeMessageStore/MessageStore";
import { banMessageStore } from "../../Stores/TypeMessageStore/BanMessageStore"; import { banMessageStore } from "../../Stores/TypeMessageStore/BanMessageStore";
import LL from "../../i18n/i18n-svelte"; import LL from "../../i18n/i18n-svelte";
@ -13,6 +14,8 @@
onMount(() => { onMount(() => {
timeToRead(); timeToRead();
const gameScene = gameManager.getCurrentGameScene();
gameScene.playSound("audio-report-message");
}); });
function timeToRead() { function timeToRead() {
@ -53,18 +56,19 @@
on:click|preventDefault={closeBanMessage}>{nameButton}</button on:click|preventDefault={closeBanMessage}>{nameButton}</button
> >
</div> </div>
<!-- svelte-ignore a11y-media-has-caption -->
<audio id="report-message" autoplay>
<source src="/resources/objects/report-message.mp3" type="audio/mp3" />
</audio>
</div> </div>
<style lang="scss"> <style lang="scss">
div.main-ban-message { div.main-ban-message {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative; position: absolute;
top: 15vh; top: 4%;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
z-index: 850;
height: 70vh; height: 70vh;
width: 60vw; width: 60vw;

View file

@ -42,14 +42,17 @@
div.main-text-message { div.main-text-message {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: absolute;
max-height: 25vh; max-height: 25%;
width: 80vw; width: 60%;
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
margin-bottom: 16px; top: 6%;
margin-top: 0; left: 0;
right: 0;
padding-bottom: 0; padding-bottom: 0;
z-index: 240;
pointer-events: auto; pointer-events: auto;
background-color: #333333; background-color: #333333;

View file

@ -25,11 +25,17 @@
<style lang="scss"> <style lang="scss">
div.error-div { div.error-div {
pointer-events: auto; pointer-events: auto;
margin-top: 10vh; margin-top: 4%;
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
left: 0;
right: 0;
position: absolute;
width: max-content; width: max-content;
max-width: 80vw; max-width: 80vw;
z-index: 230;
height: auto !important;
background-clip: padding-box;
.button-bar { .button-bar {
text-align: center; text-align: center;

View file

@ -1,15 +1,33 @@
<script lang="typescript"> <script lang="typescript">
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
import type { ScreenSharingLocalMedia } from "../../Stores/ScreenSharingStore"; import type { ScreenSharingLocalMedia } from "../../Stores/ScreenSharingStore";
import { videoFocusStore } from "../../Stores/VideoFocusStore"; import type { Streamable } from "../../Stores/StreamableCollectionStore";
import { srcObject } from "./utils"; import { srcObject } from "./utils";
export let clickable = false;
export let peer: ScreenSharingLocalMedia; export let peer: ScreenSharingLocalMedia;
let stream = peer.stream; let stream = peer.stream;
export let cssClass: string | undefined; export let cssClass: string | undefined;
let embedScreen: EmbedScreen;
if (stream) {
embedScreen = {
type: "streamable",
embed: stream as unknown as Streamable,
};
}
</script> </script>
<div class="video-container {cssClass ? cssClass : ''}" class:hide={!stream}> <div class="video-container {cssClass ? cssClass : ''}" class:hide={!stream}>
{#if stream} {#if stream}
<video use:srcObject={stream} autoplay muted playsinline on:click={() => videoFocusStore.toggleFocus(peer)} /> <video
use:srcObject={stream}
autoplay
muted
playsinline
on:click={() => (clickable ? highlightedEmbedScreen.toggleHighlight(embedScreen) : null)}
/>
{/if} {/if}
</div> </div>

View file

@ -7,14 +7,65 @@
import type { Streamable } from "../../Stores/StreamableCollectionStore"; import type { Streamable } from "../../Stores/StreamableCollectionStore";
export let streamable: Streamable; export let streamable: Streamable;
export let isHightlighted = false;
export let clickable = false;
</script> </script>
<div class="media-container"> <div class="media-container nes-container is-rounded {isHightlighted ? 'hightlighted' : ''}" class:clickable>
{#if streamable instanceof VideoPeer} {#if streamable instanceof VideoPeer}
<VideoMediaBox peer={streamable} /> <VideoMediaBox peer={streamable} {clickable} />
{:else if streamable instanceof ScreenSharingPeer} {:else if streamable instanceof ScreenSharingPeer}
<ScreenSharingMediaBox peer={streamable} /> <ScreenSharingMediaBox peer={streamable} {clickable} />
{:else} {:else}
<LocalStreamMediaBox peer={streamable} cssClass="" /> <LocalStreamMediaBox peer={streamable} {clickable} cssClass="" />
{/if} {/if}
</div> </div>
<style lang="scss">
@import "../../../style/breakpoints.scss";
.media-container {
display: flex;
margin-top: 4%;
margin-bottom: 4%;
margin-left: auto;
margin-right: auto;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s,
max-width 0.2s;
pointer-events: auto;
background-color: rgba(0, 0, 0, 0.6);
padding: 0;
max-height: 85%;
max-width: 85%;
&:hover {
margin-top: 2%;
margin-bottom: 2%;
}
&.hightlighted {
margin-top: 0% !important;
margin-bottom: 0% !important;
margin-left: 0% !important;
max-height: 100% !important;
max-width: 96% !important;
&:hover {
margin-top: 0% !important;
margin-bottom: 0% !important;
}
}
&.clickable {
cursor: url("../../../style/images/cursor_pointer.png"), pointer;
}
}
@include media-breakpoint-only(md) {
.media-container {
margin-top: 10%;
margin-bottom: 10%;
}
}
</style>

View file

@ -1,26 +0,0 @@
<script lang="ts">
import { streamableCollectionStore } from "../../Stores/StreamableCollectionStore";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
import { afterUpdate } from "svelte";
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
import MediaBox from "./MediaBox.svelte";
afterUpdate(() => {
biggestAvailableAreaStore.recompute();
});
</script>
<div class="main-section">
{#if $videoFocusStore}
{#key $videoFocusStore.uniqueId}
<MediaBox streamable={$videoFocusStore} />
{/key}
{/if}
</div>
<aside class="sidebar">
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
{#if peer !== $videoFocusStore}
<MediaBox streamable={peer} />
{/if}
{/each}
</aside>

View file

@ -1,12 +1,26 @@
<script lang="ts"> <script lang="ts">
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
import type { Streamable } from "../../Stores/StreamableCollectionStore";
import type { ScreenSharingPeer } from "../../WebRtc/ScreenSharingPeer"; import type { ScreenSharingPeer } from "../../WebRtc/ScreenSharingPeer";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
import { getColorByString, srcObject } from "./utils"; import { getColorByString, srcObject } from "./utils";
export let clickable = false;
export let peer: ScreenSharingPeer; export let peer: ScreenSharingPeer;
let streamStore = peer.streamStore; let streamStore = peer.streamStore;
let name = peer.userName; let name = peer.userName;
let statusStore = peer.statusStore; let statusStore = peer.statusStore;
let embedScreen: EmbedScreen;
if (peer) {
embedScreen = {
type: "streamable",
embed: peer as unknown as Streamable,
};
}
</script> </script>
<div class="video-container"> <div class="video-container">
@ -20,7 +34,12 @@
<i style="background-color: {getColorByString(name)};">{name}</i> <i style="background-color: {getColorByString(name)};">{name}</i>
{:else} {:else}
<!-- svelte-ignore a11y-media-has-caption --> <!-- svelte-ignore a11y-media-has-caption -->
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)} /> <video
use:srcObject={$streamStore}
autoplay
playsinline
on:click={() => (clickable ? highlightedEmbedScreen.toggleHighlight(embedScreen) : null)}
/>
{/if} {/if}
</div> </div>

View file

@ -4,11 +4,17 @@
import microphoneCloseImg from "../images/microphone-close.svg"; import microphoneCloseImg from "../images/microphone-close.svg";
import reportImg from "./images/report.svg"; import reportImg from "./images/report.svg";
import blockSignImg from "./images/blockSign.svg"; import blockSignImg from "./images/blockSign.svg";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore"; import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
import { getColorByString, srcObject } from "./utils"; import { getColorByString, srcObject } from "./utils";
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
import type { Streamable } from "../../Stores/StreamableCollectionStore";
import Woka from "../Woka/Woka.svelte"; import Woka from "../Woka/Woka.svelte";
import { onMount } from "svelte";
import { isMediaBreakpointOnly } from "../../Utils/BreakpointsUtils";
export let clickable = false;
export let peer: VideoPeer; export let peer: VideoPeer;
let streamStore = peer.streamStore; let streamStore = peer.streamStore;
@ -19,9 +25,32 @@
function openReport(peer: VideoPeer): void { function openReport(peer: VideoPeer): void {
showReportScreenStore.set({ userId: peer.userId, userName: peer.userName }); showReportScreenStore.set({ userId: peer.userId, userName: peer.userName });
} }
let embedScreen: EmbedScreen;
let videoContainer: HTMLDivElement;
let minimized = isMediaBreakpointOnly("md");
if (peer) {
embedScreen = {
type: "streamable",
embed: peer as unknown as Streamable,
};
}
function noDrag() {
return false;
}
const resizeObserver = new ResizeObserver(() => {
minimized = isMediaBreakpointOnly("md");
});
onMount(() => {
resizeObserver.observe(videoContainer);
});
</script> </script>
<div class="video-container"> <div class="video-container" class:no-clikable={!clickable} bind:this={videoContainer}>
{#if $statusStore === "connecting"} {#if $statusStore === "connecting"}
<div class="connecting-spinner" /> <div class="connecting-spinner" />
{/if} {/if}
@ -30,42 +59,60 @@
{/if} {/if}
<!-- {#if !$constraintStore || $constraintStore.video === false} --> <!-- {#if !$constraintStore || $constraintStore.video === false} -->
<i <i
class="container {!$constraintStore || $constraintStore.video === false ? '' : 'minimized'}" class="container"
class:has-video={$constraintStore && $constraintStore.video === true}
class:minimized={(!$constraintStore || $constraintStore.video !== true) && minimized}
style="background-color: {getColorByString(name)};" style="background-color: {getColorByString(name)};"
> >
<span>{peer.userName}</span> <span style="noselect">{peer.userName}</span>
<div class="woka-icon"><Woka userId={peer.userId} placeholderSrc={""} /></div> <div class="woka-icon"><Woka userId={peer.userId} placeholderSrc={""} /></div>
</i> </i>
<!-- {/if} --> <!-- {/if} -->
{#if $constraintStore && $constraintStore.audio === false} {#if $constraintStore && $constraintStore.audio === false}
<img src={microphoneCloseImg} class="active" alt="Muted" /> <img
src={microphoneCloseImg}
class="active noselect"
draggable="false"
on:dragstart|preventDefault={noDrag}
alt="Muted"
/>
{/if} {/if}
<button class="report" on:click={() => openReport(peer)}> <button class="report" on:click={() => openReport(peer)}>
<img alt="Report this user" src={reportImg} /> <img alt="Report this user" draggable="false" on:dragstart|preventDefault={noDrag} src={reportImg} />
<span>Report/Block</span> <span class="noselect">Report/Block</span>
</button> </button>
<!-- svelte-ignore a11y-media-has-caption --> <!-- svelte-ignore a11y-media-has-caption -->
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)} /> <video
<img src={blockSignImg} class="block-logo" alt="Block" /> use:srcObject={$streamStore}
autoplay
playsinline
on:click={() => (clickable ? highlightedEmbedScreen.toggleHighlight(embedScreen) : null)}
/>
<img src={blockSignImg} draggable="false" on:dragstart|preventDefault={noDrag} class="block-logo" alt="Block" />
{#if $constraintStore && $constraintStore.audio !== false} {#if $constraintStore && $constraintStore.audio !== false}
<SoundMeterWidget stream={$streamStore} /> <SoundMeterWidget stream={$streamStore} />
{/if} {/if}
</div> </div>
<style> <style lang="scss">
.container { .container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding-top: 15px; padding-top: 15px;
}
.minimized { &.has-video {
left: auto; left: auto;
transform: scale(0.5); transform: scale(0.5);
opacity: 0.5; opacity: 0.5;
} }
.woka-icon { &.minimized {
margin-right: 3px; transform: scale(0.5);
opacity: 0.5;
}
.woka-icon {
margin-right: 3px;
}
} }
</style> </style>

View file

@ -1,16 +1,16 @@
<script lang="ts"> <script lang="ts">
import { LayoutMode } from "../../WebRtc/LayoutManager"; // import {LayoutMode} from "../../WebRtc/LayoutManager";
import { layoutModeStore } from "../../Stores/StreamableCollectionStore"; // import {layoutModeStore} from "../../Stores/StreamableCollectionStore";
import PresentationLayout from "./PresentationLayout.svelte"; // import PresentationLayout from "./PresentationLayout.svelte";
import ChatLayout from "./ChatLayout.svelte"; // import ChatLayout from "./ChatLayout.svelte";
</script> </script>
<div class="video-overlay"> <div class="video-overlay">
{#if $layoutModeStore === LayoutMode.Presentation} <!-- {#if $layoutModeStore === LayoutMode.Presentation }
<PresentationLayout /> <PresentationLayout />
{:else} {:else}
<ChatLayout /> <ChatLayout />
{/if} {/if} -->
</div> </div>
<style lang="scss"> <style lang="scss">

View file

@ -57,6 +57,7 @@
height: 120px; height: 120px;
margin: auto; margin: auto;
animation: spin 2s linear infinite; animation: spin 2s linear infinite;
z-index: 102;
} }
@keyframes spin { @keyframes spin {

View file

@ -27,18 +27,22 @@
<style lang="scss"> <style lang="scss">
main.warningMain { main.warningMain {
pointer-events: auto; pointer-events: auto;
width: 100vw; width: 80%;
background-color: #f9e81e; background-color: #F9E81E;
color: #14304c; color: #14304C;
text-align: center; text-align: center;
position: absolute; position: absolute;
left: 50%;
top: 4%;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
transform: translate(-50%, 0); transform: translate(-50%, 0);
font-family: Lato; font-family: Lato;
min-width: 300px; min-width: 300px;
opacity: 0.9; opacity: 0.9;
z-index: 2; z-index: 270;
h2 { h2 {
padding: 5px; padding: 5px;
} }

View file

@ -22,10 +22,21 @@
src = source ?? placeholderSrc; src = source ?? placeholderSrc;
}); });
function noDrag() {
return false;
}
onDestroy(unsubscribe); onDestroy(unsubscribe);
</script> </script>
<img {src} alt="" class="nes-pointer" style="--theme-width: {width}; --theme-height: {height}" /> <img
{src}
alt=""
class="nes-pointer noselect"
style="--theme-width: {width}; --theme-height: {height}"
draggable="false"
on:dragstart|preventDefault={noDrag}
/>
<style> <style>
img { img {

View file

@ -49,6 +49,8 @@
</form> </form>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss";
form.selectCharacterScene { form.selectCharacterScene {
font-family: "Press Start 2P"; font-family: "Press Start 2P";
pointer-events: auto; pointer-events: auto;
@ -91,7 +93,7 @@
} }
} }
@media only screen and (max-width: 800px) { @include media-breakpoint-up(md) {
form.selectCharacterScene button.selectCharacterButtonLeft { form.selectCharacterScene button.selectCharacterButtonLeft {
left: 5vw; left: 5vw;
} }

View file

@ -42,8 +42,6 @@ export const DISABLE_ANONYMOUS: boolean = getEnv("DISABLE_ANONYMOUS") === "true"
export const OPID_LOGIN_SCREEN_PROVIDER = getEnv("OPID_LOGIN_SCREEN_PROVIDER"); export const OPID_LOGIN_SCREEN_PROVIDER = getEnv("OPID_LOGIN_SCREEN_PROVIDER");
const FALLBACK_LOCALE = getEnv("FALLBACK_LOCALE") || undefined; const FALLBACK_LOCALE = getEnv("FALLBACK_LOCALE") || undefined;
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;
export { export {
DEBUG_MODE, DEBUG_MODE,
START_ROOM_URL, START_ROOM_URL,

View file

@ -63,8 +63,6 @@ export class SoundMeter {
//this.slow = 0.95 * that.slow + 0.05 * that.instant; //this.slow = 0.95 * that.slow + 0.05 * that.instant;
//this.clip = clipcount / input.length; //this.clip = clipcount / input.length;
//console.log('instant', this.instant, 'clip', this.clip);
return this.instant; return this.instant;
} }

View file

@ -20,7 +20,6 @@ export enum GameMapProperties {
OPEN_WEBSITE = "openWebsite", OPEN_WEBSITE = "openWebsite",
OPEN_WEBSITE_ALLOW_API = "openWebsiteAllowApi", OPEN_WEBSITE_ALLOW_API = "openWebsiteAllowApi",
OPEN_WEBSITE_POLICY = "openWebsitePolicy", OPEN_WEBSITE_POLICY = "openWebsitePolicy",
OPEN_WEBSITE_WIDTH = "openWebsiteWidth",
OPEN_WEBSITE_POSITION = "openWebsitePosition", OPEN_WEBSITE_POSITION = "openWebsitePosition",
OPEN_WEBSITE_TRIGGER = "openWebsiteTrigger", OPEN_WEBSITE_TRIGGER = "openWebsiteTrigger",
OPEN_WEBSITE_TRIGGER_MESSAGE = "openWebsiteTriggerMessage", OPEN_WEBSITE_TRIGGER_MESSAGE = "openWebsiteTriggerMessage",

View file

@ -252,7 +252,7 @@ export class GameScene extends DirtyScene {
} }
this.load.audio("audio-webrtc-in", "/resources/objects/webrtc-in.mp3"); this.load.audio("audio-webrtc-in", "/resources/objects/webrtc-in.mp3");
this.load.audio("audio-webrtc-out", "/resources/objects/webrtc-out.mp3"); this.load.audio("audio-webrtc-out", "/resources/objects/webrtc-out.mp3");
//this.load.audio('audio-report-message', '/resources/objects/report-message.mp3'); this.load.audio("audio-report-message", "/resources/objects/report-message.mp3");
this.sound.pauseOnBlur = false; this.sound.pauseOnBlur = false;
this.load.on(FILE_LOAD_ERROR, (file: { src: string }) => { this.load.on(FILE_LOAD_ERROR, (file: { src: string }) => {
@ -632,13 +632,9 @@ export class GameScene extends DirtyScene {
this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { this.peerStoreUnsubscribe = peerStore.subscribe((peers) => {
const newPeerNumber = peers.size; const newPeerNumber = peers.size;
if (newPeerNumber > oldPeerNumber) { if (newPeerNumber > oldPeerNumber) {
this.sound.play("audio-webrtc-in", { this.playSound("audio-webrtc-in");
volume: 0.2,
});
} else if (newPeerNumber < oldPeerNumber) { } else if (newPeerNumber < oldPeerNumber) {
this.sound.play("audio-webrtc-out", { this.playSound("audio-webrtc-out");
volume: 0.2,
});
} }
oldPeerNumber = newPeerNumber; oldPeerNumber = newPeerNumber;
}); });
@ -1265,21 +1261,21 @@ ${escapedMessage}
throw new Error("Unknown query source"); throw new Error("Unknown query source");
} }
const coWebsite = await coWebsiteManager.loadCoWebsite( const coWebsite = coWebsiteManager.addCoWebsite(
openCoWebsite.url, openCoWebsite.url,
iframeListener.getBaseUrlFromSource(source), iframeListener.getBaseUrlFromSource(source),
openCoWebsite.allowApi, openCoWebsite.allowApi,
openCoWebsite.allowPolicy, openCoWebsite.allowPolicy,
openCoWebsite.position openCoWebsite.position,
openCoWebsite.closable ?? true
); );
if (!coWebsite) { if (openCoWebsite.lazy !== undefined && !openCoWebsite.lazy) {
throw new Error("Error on opening co-website"); await coWebsiteManager.loadCoWebsite(coWebsite);
} }
return { return {
id: coWebsite.iframe.id, id: coWebsite.iframe.id,
position: coWebsite.position,
}; };
}); });
@ -1289,7 +1285,6 @@ ${escapedMessage}
return coWebsites.map((coWebsite: CoWebsite) => { return coWebsites.map((coWebsite: CoWebsite) => {
return { return {
id: coWebsite.iframe.id, id: coWebsite.iframe.id,
position: coWebsite.position,
}; };
}); });
}); });
@ -1565,6 +1560,12 @@ ${escapedMessage}
} }
} }
public playSound(sound: string) {
this.sound.play(sound, {
volume: 0.2,
});
}
public cleanupClosingScene(): void { public cleanupClosingScene(): void {
// stop playing audio, close any open website, stop any open Jitsi // stop playing audio, close any open website, stop any open Jitsi
coWebsiteManager.closeCoWebsites().catch((e) => console.error(e)); coWebsiteManager.closeCoWebsites().catch((e) => console.error(e));
@ -1602,7 +1603,7 @@ ${escapedMessage}
this.sharedVariablesManager?.close(); this.sharedVariablesManager?.close();
this.embeddedWebsiteManager?.close(); this.embeddedWebsiteManager?.close();
mediaManager.hideGameOverlay(); mediaManager.hideMyCamera();
for (const iframeEvents of this.iframeSubscriptionList) { for (const iframeEvents of this.iframeSubscriptionList) {
iframeEvents.unsubscribe(); iframeEvents.unsubscribe();
@ -2111,6 +2112,17 @@ ${escapedMessage}
biggestAvailableAreaStore.recompute(); biggestAvailableAreaStore.recompute();
} }
public enableMediaBehaviors() {
const silent = this.gameMap.getCurrentProperties().get(GameMapProperties.SILENT);
this.connection?.setSilent(!!silent);
mediaManager.showMyCamera();
}
public disableMediaBehaviors() {
this.connection?.setSilent(true);
mediaManager.hideMyCamera();
}
public startJitsi(roomName: string, jwt?: string): void { public startJitsi(roomName: string, jwt?: string): void {
const allProps = this.gameMap.getCurrentProperties(); const allProps = this.gameMap.getCurrentProperties();
const jitsiConfig = this.safeParseJSONstring( const jitsiConfig = this.safeParseJSONstring(
@ -2122,28 +2134,21 @@ ${escapedMessage}
GameMapProperties.JITSI_INTERFACE_CONFIG GameMapProperties.JITSI_INTERFACE_CONFIG
); );
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
const jitsiWidth = allProps.get(GameMapProperties.JITSI_WIDTH) as number | undefined;
jitsiFactory jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl).catch(() => {
.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl, jitsiWidth) console.error("Cannot start a Jitsi co-website");
.catch((e) => console.error(e));
this.connection?.setSilent(true);
mediaManager.hideGameOverlay();
analyticsClient.enteredJitsi(roomName, this.room.id);
//permit to stop jitsi when user close iframe
mediaManager.addTriggerCloseJitsiFrameButton("close-jitsi", () => {
this.stopJitsi();
}); });
this.disableMediaBehaviors();
analyticsClient.enteredJitsi(roomName, this.room.id);
} }
public stopJitsi(): void { public stopJitsi(): void {
const silent = this.gameMap.getCurrentProperties().get(GameMapProperties.SILENT); const coWebsite = coWebsiteManager.searchJitsi();
this.connection?.setSilent(!!silent); if (coWebsite) {
jitsiFactory.stop(); coWebsiteManager.closeCoWebsite(coWebsite).catch(() => {
mediaManager.showGameOverlay(); console.error("Error during Jitsi co-website closing");
});
mediaManager.removeTriggerCloseJitsiFrameButton("close-jitsi"); }
} }
//todo: put this into an 'orchestrator' scene (EntryScene?) //todo: put this into an 'orchestrator' scene (EntryScene?)

View file

@ -11,10 +11,10 @@ import { areCharacterLayersValid } from "../../Connexion/LocalUser";
import { SelectCharacterSceneName } from "./SelectCharacterScene"; import { SelectCharacterSceneName } from "./SelectCharacterScene";
import { activeRowStore, customCharacterSceneVisibleStore } from "../../Stores/CustomCharacterStore"; import { activeRowStore, customCharacterSceneVisibleStore } from "../../Stores/CustomCharacterStore";
import { waScaleManager } from "../Services/WaScaleManager"; import { waScaleManager } from "../Services/WaScaleManager";
import { isMobile } from "../../Enum/EnvironmentVariable";
import { CustomizedCharacter } from "../Entity/CustomizedCharacter"; import { CustomizedCharacter } from "../Entity/CustomizedCharacter";
import { get } from "svelte/store"; import { get } from "svelte/store";
import { analyticsClient } from "../../Administration/AnalyticsClient"; import { analyticsClient } from "../../Administration/AnalyticsClient";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
export const CustomizeSceneName = "CustomizeScene"; export const CustomizeSceneName = "CustomizeScene";
@ -67,12 +67,12 @@ export class CustomizeScene extends AbstractCharacterScene {
customCharacterSceneVisibleStore.set(true); customCharacterSceneVisibleStore.set(true);
this.events.addListener("wake", () => { this.events.addListener("wake", () => {
waScaleManager.saveZoom(); waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 3 : 1; waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
customCharacterSceneVisibleStore.set(true); customCharacterSceneVisibleStore.set(true);
}); });
waScaleManager.saveZoom(); waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 3 : 1; waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
this.Rectangle = this.add.rectangle( this.Rectangle = this.add.rectangle(
this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.x + this.cameras.main.width / 2,

View file

@ -12,8 +12,8 @@ import { touchScreenManager } from "../../Touch/TouchScreenManager";
import { PinchManager } from "../UserInput/PinchManager"; import { PinchManager } from "../UserInput/PinchManager";
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore"; import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
import { waScaleManager } from "../Services/WaScaleManager"; import { waScaleManager } from "../Services/WaScaleManager";
import { isMobile } from "../../Enum/EnvironmentVariable";
import { analyticsClient } from "../../Administration/AnalyticsClient"; import { analyticsClient } from "../../Administration/AnalyticsClient";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
//todo: put this constants in a dedicated file //todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene"; export const SelectCharacterSceneName = "SelectCharacterScene";
@ -60,7 +60,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
selectCharacterSceneVisibleStore.set(true); selectCharacterSceneVisibleStore.set(true);
this.events.addListener("wake", () => { this.events.addListener("wake", () => {
waScaleManager.saveZoom(); waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 2 : 1; waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
selectCharacterSceneVisibleStore.set(true); selectCharacterSceneVisibleStore.set(true);
}); });
@ -69,7 +69,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
} }
waScaleManager.saveZoom(); waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 2 : 1; waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xffffff); this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xffffff);

View file

@ -9,7 +9,7 @@ import { touchScreenManager } from "../../Touch/TouchScreenManager";
import { PinchManager } from "../UserInput/PinchManager"; import { PinchManager } from "../UserInput/PinchManager";
import { selectCompanionSceneVisibleStore } from "../../Stores/SelectCompanionStore"; import { selectCompanionSceneVisibleStore } from "../../Stores/SelectCompanionStore";
import { waScaleManager } from "../Services/WaScaleManager"; import { waScaleManager } from "../Services/WaScaleManager";
import { isMobile } from "../../Enum/EnvironmentVariable"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
export const SelectCompanionSceneName = "SelectCompanionScene"; export const SelectCompanionSceneName = "SelectCompanionScene";
@ -44,7 +44,7 @@ export class SelectCompanionScene extends ResizableScene {
selectCompanionSceneVisibleStore.set(true); selectCompanionSceneVisibleStore.set(true);
waScaleManager.saveZoom(); waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 2 : 1; waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
if (touchScreenManager.supportTouchScreen) { if (touchScreenManager.supportTouchScreen) {
new PinchManager(this); new PinchManager(this);

View file

@ -37,19 +37,17 @@ export class WaScaleManager {
height: height * devicePixelRatio, height: height * devicePixelRatio,
}); });
if (gameSize.width == 0) { if (realSize.width !== 0 && gameSize.width !== 0 && devicePixelRatio !== 0) {
return; this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
} }
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio; this.scaleManager.setZoom(this.actualZoom);
this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio);
this.scaleManager.resize(gameSize.width, gameSize.height); this.scaleManager.resize(gameSize.width, gameSize.height);
// Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves // Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves
const style = this.scaleManager.canvas.style; const style = this.scaleManager.canvas.style;
style.width = Math.ceil(realSize.width / devicePixelRatio) + "px"; style.width = Math.ceil(realSize.width !== 0 ? realSize.width / devicePixelRatio : 0) + "px";
style.height = Math.ceil(realSize.height / devicePixelRatio) + "px"; style.height = Math.ceil(realSize.height !== 0 ? realSize.height / devicePixelRatio : 0) + "px";
// Resize the game element at the same size at the canvas // Resize the game element at the same size at the canvas
const gameStyle = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("game").style; const gameStyle = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("game").style;

View file

@ -2,14 +2,14 @@ import { get, writable } from "svelte/store";
import type { Box } from "../WebRtc/LayoutManager"; import type { Box } from "../WebRtc/LayoutManager";
import { HtmlUtils } from "../WebRtc/HtmlUtils"; import { HtmlUtils } from "../WebRtc/HtmlUtils";
import { LayoutMode } from "../WebRtc/LayoutManager"; import { LayoutMode } from "../WebRtc/LayoutManager";
import { layoutModeStore } from "./StreamableCollectionStore"; import { embedScreenLayout } from "./EmbedScreensStore";
/** /**
* Tries to find the biggest available box of remaining space (this is a space where we can center the character) * Tries to find the biggest available box of remaining space (this is a space where we can center the character)
*/ */
function findBiggestAvailableArea(): Box { function findBiggestAvailableArea(): Box {
const game = HtmlUtils.querySelectorOrFail<HTMLCanvasElement>("#game canvas"); const game = HtmlUtils.querySelectorOrFail<HTMLCanvasElement>("#game canvas");
if (get(layoutModeStore) === LayoutMode.VideoChat) { if (get(embedScreenLayout) === LayoutMode.VideoChat) {
const children = document.querySelectorAll<HTMLDivElement>("div.chat-mode > div"); const children = document.querySelectorAll<HTMLDivElement>("div.chat-mode > div");
const htmlChildren = Array.from(children.values()); const htmlChildren = Array.from(children.values());

View file

@ -1,17 +0,0 @@
import { writable } from "svelte/store";
/**
* A store that contains whether the game overlay is shown or not.
* Typically, the overlay is hidden when entering Jitsi meet.
*/
function createGameOverlayVisibilityStore() {
const { subscribe, set, update } = writable(false);
return {
subscribe,
showGameOverlay: () => set(true),
hideGameOverlay: () => set(false),
};
}
export const gameOverlayVisibilityStore = createGameOverlayVisibilityStore();

View file

@ -51,6 +51,6 @@ function createLayoutManagerAction() {
export const layoutManagerActionStore = createLayoutManagerAction(); export const layoutManagerActionStore = createLayoutManagerAction();
export const layoutManagerVisibilityStore = derived(layoutManagerActionStore, ($layoutManagerActionStore) => { export const layoutManagerActionVisibilityStore = derived(layoutManagerActionStore, ($layoutManagerActionStore) => {
return !!$layoutManagerActionStore.length; return !!$layoutManagerActionStore.length;
}); });

View file

@ -6,7 +6,7 @@ import { BrowserTooOldError } from "./Errors/BrowserTooOldError";
import { errorStore } from "./ErrorStore"; import { errorStore } from "./ErrorStore";
import { getNavigatorType, isIOS, NavigatorType } from "../WebRtc/DeviceUtils"; import { getNavigatorType, isIOS, NavigatorType } from "../WebRtc/DeviceUtils";
import { WebviewOnOldIOS } from "./Errors/WebviewOnOldIOS"; import { WebviewOnOldIOS } from "./Errors/WebviewOnOldIOS";
import { gameOverlayVisibilityStore } from "./GameOverlayStoreVisibility"; import { myCameraVisibilityStore } from "./MyCameraStoreVisibility";
import { peerStore } from "./PeerStore"; import { peerStore } from "./PeerStore";
import { privacyShutdownStore } from "./PrivacyShutdownStore"; import { privacyShutdownStore } from "./PrivacyShutdownStore";
import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError"; import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError";
@ -233,7 +233,7 @@ export const mediaStreamConstraintsStore = derived(
[ [
requestedCameraState, requestedCameraState,
requestedMicrophoneState, requestedMicrophoneState,
gameOverlayVisibilityStore, myCameraVisibilityStore,
enableCameraSceneVisibilityStore, enableCameraSceneVisibilityStore,
videoConstraintStore, videoConstraintStore,
audioConstraintStore, audioConstraintStore,
@ -245,7 +245,7 @@ export const mediaStreamConstraintsStore = derived(
[ [
$requestedCameraState, $requestedCameraState,
$requestedMicrophoneState, $requestedMicrophoneState,
$gameOverlayVisibilityStore, $myCameraVisibilityStore,
$enableCameraSceneVisibilityStore, $enableCameraSceneVisibilityStore,
$videoConstraintStore, $videoConstraintStore,
$audioConstraintStore, $audioConstraintStore,
@ -283,7 +283,7 @@ export const mediaStreamConstraintsStore = derived(
} }
// Disable webcam and microphone when in a Jitsi // Disable webcam and microphone when in a Jitsi
if ($gameOverlayVisibilityStore === false) { if ($myCameraVisibilityStore === false) {
currentVideoConstraint = false; currentVideoConstraint = false;
currentAudioConstraint = false; currentAudioConstraint = false;
} }

View file

@ -0,0 +1,8 @@
import { writable } from "svelte/store";
/**
* A store that contains whether my camera & actions is shown or not.
* Typically, the overlay is hidden when entering Jitsi meet.
*/
export const myCameraVisibilityStore = writable(false);

View file

@ -1,7 +1,7 @@
import { derived, Readable, readable, writable } from "svelte/store"; import { derived, Readable, readable, writable } from "svelte/store";
import { peerStore } from "./PeerStore"; import { peerStore } from "./PeerStore";
import type { LocalStreamStoreValue } from "./MediaStore"; import type { LocalStreamStoreValue } from "./MediaStore";
import { gameOverlayVisibilityStore } from "./GameOverlayStoreVisibility"; import { myCameraVisibilityStore } from "./MyCameraStoreVisibility";
declare const navigator: any; // eslint-disable-line @typescript-eslint/no-explicit-any declare const navigator: any; // eslint-disable-line @typescript-eslint/no-explicit-any
@ -41,8 +41,8 @@ let previousComputedAudioConstraint: boolean | MediaTrackConstraints = false;
* A store containing the media constraints we want to apply. * A store containing the media constraints we want to apply.
*/ */
export const screenSharingConstraintsStore = derived( export const screenSharingConstraintsStore = derived(
[requestedScreenSharingState, gameOverlayVisibilityStore, peerStore], [requestedScreenSharingState, myCameraVisibilityStore, peerStore],
([$requestedScreenSharingState, $gameOverlayVisibilityStore, $peerStore], set) => { ([$requestedScreenSharingState, $myCameraVisibilityStore, $peerStore], set) => {
let currentVideoConstraint: boolean | MediaTrackConstraints = true; let currentVideoConstraint: boolean | MediaTrackConstraints = true;
let currentAudioConstraint: boolean | MediaTrackConstraints = false; let currentAudioConstraint: boolean | MediaTrackConstraints = false;
@ -53,7 +53,7 @@ export const screenSharingConstraintsStore = derived(
} }
// Disable screen sharing when in a Jitsi // Disable screen sharing when in a Jitsi
if (!$gameOverlayVisibilityStore) { if (!$myCameraVisibilityStore) {
currentVideoConstraint = false; currentVideoConstraint = false;
currentAudioConstraint = false; currentAudioConstraint = false;
} }

View file

@ -1,13 +1,10 @@
import { derived, get, Readable, writable } from "svelte/store"; import { derived, get, Readable } from "svelte/store";
import { ScreenSharingLocalMedia, screenSharingLocalMedia } from "./ScreenSharingStore"; import { ScreenSharingLocalMedia, screenSharingLocalMedia } from "./ScreenSharingStore";
import { peerStore, screenSharingStreamStore } from "./PeerStore"; import { peerStore, screenSharingStreamStore } from "./PeerStore";
import type { RemotePeer } from "../WebRtc/SimplePeer"; import type { RemotePeer } from "../WebRtc/SimplePeer";
import { LayoutMode } from "../WebRtc/LayoutManager";
export type Streamable = RemotePeer | ScreenSharingLocalMedia; export type Streamable = RemotePeer | ScreenSharingLocalMedia;
export const layoutModeStore = writable<LayoutMode>(LayoutMode.Presentation);
/** /**
* A store that contains everything that can produce a stream (so the peers + the local screen sharing stream) * A store that contains everything that can produce a stream (so the peers + the local screen sharing stream)
*/ */

View file

@ -7,6 +7,11 @@ export class HtmlUtils {
throw new Error("Cannot find HTML element with id '" + id + "'"); throw new Error("Cannot find HTML element with id '" + id + "'");
} }
public static getElementById<T extends HTMLElement>(id: string): T | undefined {
const elem = document.getElementById(id);
return HtmlUtils.isHtmlElement<T>(elem) ? elem : undefined;
}
public static querySelectorOrFail<T extends HTMLElement>(selector: string): T { public static querySelectorOrFail<T extends HTMLElement>(selector: string): T {
const elem = document.querySelector<T>(selector); const elem = document.querySelector<T>(selector);
if (HtmlUtils.isHtmlElement<T>(elem)) { if (HtmlUtils.isHtmlElement<T>(elem)) {

View file

@ -7,8 +7,7 @@ import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStor
export type StartScreenSharingCallback = (media: MediaStream) => void; export type StartScreenSharingCallback = (media: MediaStream) => void;
export type StopScreenSharingCallback = (media: MediaStream) => void; export type StopScreenSharingCallback = (media: MediaStream) => void;
import { cowebsiteCloseButtonId } from "./CoWebsiteManager"; import { myCameraVisibilityStore } from "../Stores/MyCameraStoreVisibility";
import { gameOverlayVisibilityStore } from "../Stores/GameOverlayStoreVisibility";
import { layoutManagerActionStore } from "../Stores/LayoutManagerStore"; import { layoutManagerActionStore } from "../Stores/LayoutManagerStore";
import { MediaStreamConstraintsError } from "../Stores/Errors/MediaStreamConstraintsError"; import { MediaStreamConstraintsError } from "../Stores/Errors/MediaStreamConstraintsError";
import { localUserStore } from "../Connexion/LocalUserStore"; import { localUserStore } from "../Connexion/LocalUserStore";
@ -20,8 +19,6 @@ export class MediaManager {
startScreenSharingCallBacks: Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>(); startScreenSharingCallBacks: Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
stopScreenSharingCallBacks: Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>(); stopScreenSharingCallBacks: Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
private triggerCloseJistiFrame: Map<String, Function> = new Map<String, Function>();
private userInputManager?: UserInputManager; private userInputManager?: UserInputManager;
constructor() { constructor() {
@ -73,36 +70,12 @@ export class MediaManager {
}); });
} }
public showGameOverlay(): void { public showMyCamera(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail("game-overlay"); myCameraVisibilityStore.set(true);
gameOverlay.classList.add("active");
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
const functionTrigger = () => {
this.triggerCloseJitsiFrameButton();
};
buttonCloseFrame.removeEventListener("click", () => {
buttonCloseFrame.blur();
functionTrigger();
});
gameOverlayVisibilityStore.showGameOverlay();
} }
public hideGameOverlay(): void { public hideMyCamera(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail("game-overlay"); myCameraVisibilityStore.set(false);
gameOverlay.classList.remove("active");
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
const functionTrigger = () => {
this.triggerCloseJitsiFrameButton();
};
buttonCloseFrame.addEventListener("click", () => {
buttonCloseFrame.blur();
functionTrigger();
});
gameOverlayVisibilityStore.hideGameOverlay();
} }
private getScreenSharingId(userId: string): string { private getScreenSharingId(userId: string): string {
@ -179,20 +152,6 @@ export class MediaManager {
return connectingSpinnerDiv; return connectingSpinnerDiv;
} }
public addTriggerCloseJitsiFrameButton(id: String, Function: Function) {
this.triggerCloseJistiFrame.set(id, Function);
}
public removeTriggerCloseJitsiFrameButton(id: String) {
this.triggerCloseJistiFrame.delete(id);
}
private triggerCloseJitsiFrameButton(): void {
for (const callback of this.triggerCloseJistiFrame.values()) {
callback();
}
}
public setUserInputManager(userInputManager: UserInputManager) { public setUserInputManager(userInputManager: UserInputManager) {
this.userInputManager = userInputManager; this.userInputManager = userInputManager;
} }

View file

@ -3,9 +3,9 @@ import type { RoomConnection } from "../Connexion/RoomConnection";
import { MESSAGE_TYPE_CONSTRAINT, PeerStatus } from "./VideoPeer"; import { MESSAGE_TYPE_CONSTRAINT, PeerStatus } from "./VideoPeer";
import type { UserSimplePeerInterface } from "./SimplePeer"; import type { UserSimplePeerInterface } from "./SimplePeer";
import { Readable, readable } from "svelte/store"; import { Readable, readable } from "svelte/store";
import { videoFocusStore } from "../Stores/VideoFocusStore";
import { getIceServersConfig } from "../Components/Video/utils"; import { getIceServersConfig } from "../Components/Video/utils";
import { isMobile } from "../Enum/EnvironmentVariable"; import { highlightedEmbedScreen } from "../Stores/EmbedScreensStore";
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer"); const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
@ -43,7 +43,10 @@ export class ScreenSharingPeer extends Peer {
this.streamStore = readable<MediaStream | null>(null, (set) => { this.streamStore = readable<MediaStream | null>(null, (set) => {
const onStream = (stream: MediaStream | null) => { const onStream = (stream: MediaStream | null) => {
videoFocusStore.focus(this); highlightedEmbedScreen.highlight({
type: "streamable",
embed: this,
});
set(stream); set(stream);
}; };
const onData = (chunk: Buffer) => { const onData = (chunk: Buffer) => {
@ -177,7 +180,13 @@ export class ScreenSharingPeer extends Peer {
public stopPushingScreenSharingToRemoteUser(stream: MediaStream) { public stopPushingScreenSharingToRemoteUser(stream: MediaStream) {
this.removeStream(stream); this.removeStream(stream);
this.write( this.write(
new Buffer(JSON.stringify({ type: MESSAGE_TYPE_CONSTRAINT, streamEnded: true, isMobile: isMobile() })) new Buffer(
JSON.stringify({
type: MESSAGE_TYPE_CONSTRAINT,
streamEnded: true,
isMobile: isMediaBreakpointUp("md"),
})
)
); );
} }
} }

View file

@ -86,7 +86,7 @@ export class SimplePeer {
} }
); );
mediaManager.showGameOverlay(); mediaManager.showMyCamera();
//receive message start //receive message start
this.Connection.webRtcStartMessageStream.subscribe((message: UserSimplePeerInterface) => { this.Connection.webRtcStartMessageStream.subscribe((message: UserSimplePeerInterface) => {

View file

@ -9,7 +9,7 @@ import { localStreamStore, obtainedMediaConstraintStore, ObtainedMediaStreamCons
import { playersStore } from "../Stores/PlayersStore"; import { playersStore } from "../Stores/PlayersStore";
import { chatMessagesStore, newChatMessageSubject } from "../Stores/ChatStore"; import { chatMessagesStore, newChatMessageSubject } from "../Stores/ChatStore";
import { getIceServersConfig } from "../Components/Video/utils"; import { getIceServersConfig } from "../Components/Video/utils";
import { isMobile } from "../Enum/EnvironmentVariable"; import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer"); const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
@ -202,7 +202,7 @@ export class VideoPeer extends Peer {
JSON.stringify({ JSON.stringify({
type: MESSAGE_TYPE_CONSTRAINT, type: MESSAGE_TYPE_CONSTRAINT,
...constraints, ...constraints,
isMobile: isMobile(), isMobile: isMediaBreakpointUp("md"),
}) })
) )
); );

View file

@ -2,7 +2,7 @@ import "phaser";
import GameConfig = Phaser.Types.Core.GameConfig; import GameConfig = Phaser.Types.Core.GameConfig;
import "../style/index.scss"; import "../style/index.scss";
import { DEBUG_MODE, isMobile } from "./Enum/EnvironmentVariable"; import { DEBUG_MODE } from "./Enum/EnvironmentVariable";
import { LoginScene } from "./Phaser/Login/LoginScene"; import { LoginScene } from "./Phaser/Login/LoginScene";
import { ReconnectingScene } from "./Phaser/Reconnecting/ReconnectingScene"; import { ReconnectingScene } from "./Phaser/Reconnecting/ReconnectingScene";
import { SelectCharacterScene } from "./Phaser/Login/SelectCharacterScene"; import { SelectCharacterScene } from "./Phaser/Login/SelectCharacterScene";
@ -24,6 +24,7 @@ import App from "./Components/App.svelte";
import { HtmlUtils } from "./WebRtc/HtmlUtils"; import { HtmlUtils } from "./WebRtc/HtmlUtils";
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
import { analyticsClient } from "./Administration/AnalyticsClient"; import { analyticsClient } from "./Administration/AnalyticsClient";
import { isMediaBreakpointUp } from "./Utils/BreakpointsUtils";
const { width, height } = coWebsiteManager.getGameSize(); const { width, height } = coWebsiteManager.getGameSize();
const valueGameQuality = localUserStore.getGameQualityValue(); const valueGameQuality = localUserStore.getGameQualityValue();
@ -90,7 +91,7 @@ const config: GameConfig = {
scene: [ scene: [
EntryScene, EntryScene,
LoginScene, LoginScene,
isMobile() ? SelectCharacterMobileScene : SelectCharacterScene, isMediaBreakpointUp("md") ? SelectCharacterMobileScene : SelectCharacterScene,
SelectCompanionScene, SelectCompanionScene,
EnableCameraScene, EnableCameraScene,
ReconnectingScene, ReconnectingScene,
@ -136,7 +137,6 @@ const config: GameConfig = {
}, },
}; };
//const game = new Phaser.Game(config);
const game = new Game(config); const game = new Game(config);
waScaleManager.setGame(game); waScaleManager.setGame(game);
@ -156,7 +156,7 @@ coWebsiteManager.onResize.subscribe(() => {
iframeListener.init(); iframeListener.init();
const app = new App({ const app = new App({
target: HtmlUtils.getElementByIdOrFail("svelte-overlay"), target: HtmlUtils.getElementByIdOrFail("game-overlay"),
props: { props: {
game: game, game: game,
}, },

View file

@ -30,7 +30,7 @@ section.section-input-send-text {
} }
} }
@media only screen and (max-width: 800px), only screen and (max-height: 800px) { @include media-breakpoint-up(md) {
section.section-input-send-text { section.section-input-send-text {
--height-toolbar: 30%; --height-toolbar: 30%;

View file

@ -1,136 +0,0 @@
@media (max-aspect-ratio: 1/1) {
#main-container {
display: flex;
flex-direction: column-reverse;
}
#cowebsite {
left: 0;
top: 0;
width: 100%;
height: 50%;
display: flex;
flex-direction: column-reverse;
&.loading {
transform: translateY(-90%);
}
&.hidden {
transform: translateY(-100%);
}
main {
height: 100%;
}
&-container {
display: none;
}
aside {
height: 50px;
cursor: ns-resize;
flex-direction: row-reverse;
align-items: center;
display: flex;
justify-content: space-between;
#cowebsite-aside-holder {
pointer-events: none;
height: 100%;
img {
height: 80%;
}
}
#cowebsite-sub-icons {
display: inline-flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-top: 0;
height: 100%;
visibility: visible;
img {
height: 30px;
width: 30px;
cursor: pointer !important;
border-radius: 50%;
background-color: whitesmoke;
}
&>div {
display: flex;
margin-left: 2px;
margin-right: 2px;
}
}
#cowebsite-aside-buttons {
flex-direction: row-reverse;
margin-left: auto;
margin-bottom: 0;
justify-content: end;
}
img {
cursor: ns-resize;
}
#cowebsite-fullscreen {
padding-top: 0;
}
.top-right-btn {
img {
width: 15px;
}
}
}
}
}
@media (max-width:960px) and (max-height:768px) {
#cowebsite {
&-container {
display: none;
}
aside {
#cowebsite-sub-icons {
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 0;
height: 100%;
visibility: visible;
img {
height: 28px;
width: 28px;
cursor: pointer !important;
border-radius: 50%;
background-color: whitesmoke;
}
&>div {
display: flex;
margin-top: 2px;
margin-bottom: 2px;
transform: rotate(-90deg);
}
}
img {
cursor: ns-resize;
}
}
}
}

View file

@ -1,10 +1,5 @@
@import "~@fontsource/press-start-2p/index.css"; @import "~@fontsource/press-start-2p/index.css";
@font-face {
font-family: "Twemoji Mozilla";
src: url("./fonts/TwemojiMozilla.ttf") format('truetype');
}
*{ *{
font-family: PixelFont-7,monospace; font-family: PixelFont-7,monospace;
} }

Binary file not shown.

View file

@ -1,6 +1,6 @@
@charset 'UTF-8';
@import "breakpoints";
@import "cowebsite.scss"; @import "cowebsite.scss";
@import "cowebsite-mobile.scss";
@import "style";
@import "mobile-style.scss";
@import "fonts.scss"; @import "fonts.scss";
@import "style";
@import "TextGlobalMessageSvelte-Style"; @import "TextGlobalMessageSvelte-Style";

View file

@ -1,48 +0,0 @@
@media (hover: none) {
/**
* If we cannot hover over elements, let's display camera button in full.
*/
.btn-cam-action {
div {
transform: translateY(0px);
}
}
}
@media screen and (max-width: 700px),
screen and (max-height: 700px){
video.myCamVideo {
width: 150px;
}
.div-myCamVideo.hide {
right: -160px;
}
.sidebar {
width: 20%;
min-width: 200px;
position: absolute;
display: block;
right: 0;
height: 80%;
&> div {
min-width: 200px;
max-height: 21vh;
}
.video-container{
min-width: 200px;
}
}
.main-section {
position: absolute;
width: 100%;
& > div {
z-index: 2;
}
}
}

View file

@ -36,15 +36,22 @@ body .message-info.warning{
} }
.video-container { .video-container {
position: relative; display: flex;
transition: all 0.2s ease; transition: all 0.2s ease;
background-color: #00000099;
cursor: url('./images/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
width: 100%;
&.no-clikable {
video {
cursor: url('./images/cursor_normal.png'), pointer;
}
}
video { video {
height: fit-content;
max-height: 100%;
max-width: 100%;
width: 100%; width: 100%;
height: 100%;
object-fit: cover;
cursor: url('./images/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
&.mobile{ &.mobile{
@ -146,35 +153,6 @@ body .message-info.warning{
} }
} }
.video-container.div-myCamVideo{
border: none;
background-color: transparent;
}
.div-myCamVideo {
position: absolute;
right: 15px;
bottom: 30px;
border-radius: 15px 15px 15px 15px;
max-height: 20%;
transition: right 350ms;
}
.div-myCamVideo.hide {
right: -20vw;
}
video.myCamVideo{
background-color: #00000099;
max-height: 20vh;
width: 15vw;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
border-radius: 15px 15px 15px 15px;
/*width: 200px;*/
/*height: 113px;*/
}
.sound-progress{ .sound-progress{
display: none; display: none;
position: absolute; position: absolute;
@ -211,94 +189,6 @@ video.myCamVideo{
top: calc(50% - 20px); top: calc(50% - 20px);
} }
.btn-cam-action {
pointer-events: all;
position: absolute;
display: inline-flex;
bottom: 10px;
right: 15px;
width: 240px;
height: 40px;
text-align: center;
align-content: center;
align-items: center;
justify-content: flex-end;
justify-items: center;
}
/*btn animation*/
.btn-cam-action div{
cursor: url('./images/cursor_pointer.png'), pointer;
display: flex;
align-items: center;
justify-content: center;
border: solid 0px black;
width: 44px;
height: 44px;
background: #666;
box-shadow: 2px 2px 24px #444;
border-radius: 48px;
transform: translateY(15px);
transition-timing-function: ease-in-out;
margin: 0 4%;
}
.btn-cam-action div.disabled {
background: #d75555;
}
.btn-cam-action div.enabled {
background: #73c973;
}
.btn-cam-action:hover div{
transform: translateY(0);
}
.btn-cam-action div:hover{
background: #407cf7;
box-shadow: 4px 4px 48px #666;
transition: 120ms;
}
.btn-micro{
pointer-events: auto;
transition: all .3s;
/*right: 44px;*/
}
.btn-video{
pointer-events: auto;
transition: all .25s;
/*right: 134px;*/
}
.btn-monitor{
pointer-events: auto;
transition: all .2s;
/*right: 224px;*/
}
.btn-monitor.hide {
transform: translateY(60px);
}
.btn-cam-action:hover .btn-monitor.hide{
transform: translateY(60px);
}
.btn-layout{
pointer-events: auto;
transition: all .15s;
}
.btn-layout.hide {
transform: translateY(60px);
}
.btn-cam-action:hover .btn-layout.hide{
transform: translateY(60px);
}
.btn-copy{
pointer-events: auto;
transition: all .3s;
right: 44px;
opacity: 1;
}
.btn-cam-action div img{
height: 22px;
width: 30px;
position: relative;
cursor: url('./images/cursor_pointer.png'), pointer;
}
/* Spinner */ /* Spinner */
.connecting-spinner { .connecting-spinner {
/*display: inline-block;*/ /*display: inline-block;*/
@ -369,26 +259,6 @@ body {
position: absolute; position: absolute;
} }
@media (min-aspect-ratio: 1/1) {
.game-overlay {
flex-direction: row;
}
.sidebar {
flex-direction: column;
}
.sidebar > div {
max-height: 21%;
}
.sidebar > div:hover {
max-height: 25%;
}
}
#game { #game {
position: relative; /* Position relative is needed for the game-overlay. */ position: relative; /* Position relative is needed for the game-overlay. */
@ -486,96 +356,6 @@ input[type=range]:focus::-ms-fill-upper {
background: #FFFFFF; background: #FFFFFF;
} }
.game-overlay {
display: none;
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
/* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */
}
.game-overlay + div {
pointer-events: none;
}
.game-overlay + div > div {
pointer-events: auto;
}
.game-overlay.active {
display: flex;
}
.game-overlay video {
width: 100%;
}
.main-section {
flex: 0 0 75%;
display: flex;
justify-content: start;
align-items: flex-start;
flex-wrap: wrap;
}
.main-section > div {
margin: 2%;
flex-basis: 96%;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s;
cursor: url('./images/cursor_pointer.png'), pointer;
pointer-events: auto;
/*flex-shrink: 2;*/
}
.main-section > div:hover {
margin: 0%;
flex-basis: 100%;
}
.sidebar {
flex: 0 0 25%;
display: flex;
}
.sidebar > div {
margin: 2%;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s;
cursor: url('./images/cursor_pointer.png'), pointer;
border-radius: 15px 15px 15px 15px;
pointer-events: auto;
video {
max-height: 21vh;
}
}
.sidebar > div:hover {
margin: 0%;
}
.sidebar > div video {
cursor: url('./images/cursor_pointer.png'), pointer;
}
/* Let's make sure videos are vertically centered if they need to be cropped */
.media-container {
display: flex;
flex-direction: column;
overflow: hidden;
border-radius: 15px;
}
.chat-mode {
display: grid;
width: 100%;
align-items: flex-start;
padding: 1%;
}
.chat-mode > div { .chat-mode > div {
margin: 1%; margin: 1%;
max-height: 96%; max-height: 96%;
@ -1061,13 +841,22 @@ div.action.danger p.action-body{
} }
#svelte-overlay { #game-overlay {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
user-select: none; user-select: none;
& + div {
pointer-events: none;
z-index: 2;
}
& + div > div {
pointer-events: auto;
}
& > div { & > div {
position: relative; position: relative;
width: 100%; width: 100%;
@ -1085,22 +874,10 @@ div.action.danger p.action-body{
pointer-events: auto; pointer-events: auto;
} }
} }
}
div.is-silent { &.hide {
position: absolute; visibility: hidden;
bottom: 40px; }
border-radius: 15px 15px 15px 15px;
max-height: 20%;
transition: right 350ms;
right: -300px;
background-color: black;
font-size: 20px;
color: white;
padding: 30px 20px;
}
div.is-silent.hide {
right: 15px;
} }
div.emoji-picker { div.emoji-picker {
@ -1121,8 +898,3 @@ div.emoji-picker {
user-select: none; /* Non-prefixed version, currently user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */ supported by Chrome, Edge, Opera and Firefox */
} }
.pixel {
height: 1px !important;
width: 1px !important;
}