Merge branch 'develop' of github.com:thecodingmachine/workadventure
This commit is contained in:
@@ -6,13 +6,14 @@ export const isOpenCoWebsiteEvent = new tg.IsInterface()
|
||||
allowApi: tg.isOptional(tg.isBoolean),
|
||||
allowPolicy: tg.isOptional(tg.isString),
|
||||
position: tg.isOptional(tg.isNumber),
|
||||
closable: tg.isOptional(tg.isBoolean),
|
||||
lazy: tg.isOptional(tg.isBoolean),
|
||||
})
|
||||
.get();
|
||||
|
||||
export const isCoWebsite = new tg.IsInterface()
|
||||
.withProperties({
|
||||
id: tg.isString,
|
||||
position: tg.isNumber,
|
||||
})
|
||||
.get();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IframeApiContribution, sendToWorkadventure, queryWorkadventure } from "./IframeApiContribution";
|
||||
|
||||
export class CoWebsite {
|
||||
constructor(private readonly id: string, public readonly position: number) {}
|
||||
constructor(private readonly id: string) {}
|
||||
|
||||
close() {
|
||||
return queryWorkadventure({
|
||||
@@ -41,7 +41,14 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
|
||||
});
|
||||
}
|
||||
|
||||
async openCoWebSite(url: string, allowApi?: boolean, allowPolicy?: string, position?: number): Promise<CoWebsite> {
|
||||
async openCoWebSite(
|
||||
url: string,
|
||||
allowApi?: boolean,
|
||||
allowPolicy?: string,
|
||||
position?: number,
|
||||
closable?: boolean,
|
||||
lazy?: boolean
|
||||
): Promise<CoWebsite> {
|
||||
const result = await queryWorkadventure({
|
||||
type: "openCoWebsite",
|
||||
data: {
|
||||
@@ -49,9 +56,11 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
|
||||
allowApi,
|
||||
allowPolicy,
|
||||
position,
|
||||
closable,
|
||||
lazy,
|
||||
},
|
||||
});
|
||||
return new CoWebsite(result.id, result.position);
|
||||
return new CoWebsite(result.id);
|
||||
}
|
||||
|
||||
async getCoWebSites(): Promise<CoWebsite[]> {
|
||||
@@ -59,7 +68,7 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
|
||||
type: "getCoWebsites",
|
||||
data: undefined,
|
||||
});
|
||||
return result.map((cowebsiteEvent) => new CoWebsite(cowebsiteEvent.id, cowebsiteEvent.position));
|
||||
return result.map((cowebsiteEvent) => new CoWebsite(cowebsiteEvent.id));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+41
-155
@@ -1,166 +1,52 @@
|
||||
<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 { chatVisibilityStore } from "../Stores/ChatStore";
|
||||
import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore";
|
||||
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte";
|
||||
import { showLimitRoomModalStore, showShareLinkMapModalStore } from "../Stores/ModalStore";
|
||||
import LimitRoomModal from "./Modal/LimitRoomModal.svelte";
|
||||
import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
|
||||
import AudioPlaying from "./UI/AudioPlaying.svelte";
|
||||
import { soundPlayingStore } from "../Stores/SoundPlayingStore";
|
||||
import { customCharacterSceneVisibleStore } from "../Stores/CustomCharacterStore";
|
||||
import { errorStore } from "../Stores/ErrorStore";
|
||||
import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
|
||||
import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
|
||||
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
||||
import { selectCompanionSceneVisibleStore } from "../Stores/SelectCompanionStore";
|
||||
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 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;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if $loginSceneVisibleStore}
|
||||
<div class="scrollable">
|
||||
<LoginScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $selectCharacterSceneVisibleStore}
|
||||
<div>
|
||||
<SelectCharacterScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $customCharacterSceneVisibleStore}
|
||||
<div>
|
||||
<CustomCharacterScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $selectCompanionSceneVisibleStore}
|
||||
<div>
|
||||
<SelectCompanionScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $enableCameraSceneVisibilityStore}
|
||||
<div class="scrollable">
|
||||
<EnableCameraScene {game} />
|
||||
</div>
|
||||
{/if}
|
||||
{#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 $errorStore.length > 0}
|
||||
<div>
|
||||
<ErrorDialog />
|
||||
</div>
|
||||
{:else if $loginSceneVisibleStore}
|
||||
<div class="scrollable">
|
||||
<LoginScene {game} />
|
||||
</div>
|
||||
{:else if $selectCharacterSceneVisibleStore}
|
||||
<div>
|
||||
<SelectCharacterScene {game} />
|
||||
</div>
|
||||
{:else if $customCharacterSceneVisibleStore}
|
||||
<div>
|
||||
<CustomCharacterScene {game} />
|
||||
</div>
|
||||
{:else if $selectCompanionSceneVisibleStore}
|
||||
<div>
|
||||
<SelectCompanionScene {game} />
|
||||
</div>
|
||||
{:else if $enableCameraSceneVisibilityStore}
|
||||
<div class="scrollable">
|
||||
<EnableCameraScene {game} />
|
||||
</div>
|
||||
{:else}
|
||||
<MainLayout />
|
||||
|
||||
{#if $chatVisibilityStore}
|
||||
<Chat />
|
||||
{/if}
|
||||
{#if $warningContainerStore}
|
||||
<WarningContainer />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -157,13 +157,16 @@
|
||||
|
||||
<style lang="scss">
|
||||
div.main-audio-manager.nes-container.is-rounded {
|
||||
position: relative;
|
||||
top: 0.5rem;
|
||||
position: absolute;
|
||||
top: 1%;
|
||||
max-height: clamp(150px, 10vh, 15vh); //replace @media for small screen
|
||||
width: clamp(200px, 15vw, 15vw);
|
||||
padding: 3px 3px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 550;
|
||||
|
||||
background-color: rgb(0, 0, 0, 0.5);
|
||||
display: grid;
|
||||
|
||||
@@ -9,10 +9,15 @@
|
||||
import microphoneCloseImg from "./images/microphone-close.svg";
|
||||
import layoutPresentationImg from "./images/layout-presentation.svg";
|
||||
import layoutChatImg from "./images/layout-chat.svg";
|
||||
import { layoutModeStore } from "../Stores/StreamableCollectionStore";
|
||||
import followImg from "./images/follow.svg";
|
||||
import { LayoutMode } from "../WebRtc/LayoutManager";
|
||||
import { peerStore } from "../Stores/PeerStore";
|
||||
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 {
|
||||
if (isSilent) return;
|
||||
@@ -42,10 +47,26 @@
|
||||
}
|
||||
|
||||
function switchLayoutMode() {
|
||||
if ($layoutModeStore === LayoutMode.Presentation) {
|
||||
$layoutModeStore = LayoutMode.VideoChat;
|
||||
if ($embedScreenLayout === LayoutMode.Presentation) {
|
||||
$embedScreenLayout = LayoutMode.VideoChat;
|
||||
} 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,41 +77,156 @@
|
||||
onDestroy(unsubscribeIsSilent);
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="btn-cam-action">
|
||||
<div class="btn-layout nes-btn is-dark" on:click={switchLayoutMode} class:hide={$peerStore.size === 0}>
|
||||
{#if $layoutModeStore === LayoutMode.Presentation}
|
||||
<img src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode" />
|
||||
{:else}
|
||||
<img src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode" />
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
class="btn-monitor nes-btn is-dark"
|
||||
on:click={screenSharingClick}
|
||||
class:hide={!$screenSharingAvailableStore || isSilent}
|
||||
class:enabled={$requestedScreenSharingState}
|
||||
>
|
||||
{#if $requestedScreenSharingState && !isSilent}
|
||||
<img src={monitorImg} alt="Start screen sharing" />
|
||||
{:else}
|
||||
<img src={monitorCloseImg} alt="Stop screen sharing" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="btn-video nes-btn is-dark" on:click={cameraClick} class:disabled={!$requestedCameraState ||
|
||||
isSilent}>
|
||||
{#if $requestedCameraState && !isSilent}
|
||||
<img src={cinemaImg} alt="Turn on webcam" />
|
||||
{:else}
|
||||
<img src={cinemaCloseImg} alt="Turn off webcam" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="btn-micro nes-btn is-dark" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || isSilent}>
|
||||
{#if $requestedMicrophoneState && !isSilent}
|
||||
<img src={microphoneImg} alt="Turn on microphone" />
|
||||
{:else}
|
||||
<img src={microphoneCloseImg} alt="Turn off microphone" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="btn-cam-action">
|
||||
<div class="btn-layout nes-btn is-dark" on:click={switchLayoutMode} class:hide={$peerStore.size === 0}>
|
||||
{#if $embedScreenLayout === LayoutMode.Presentation}
|
||||
<img class="noselect" src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode" />
|
||||
{:else}
|
||||
<img class="noselect" src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="btn-follow nes-btn is-dark"
|
||||
class:hide={($peerStore.size === 0 && $followStateStore === "off") || isSilent}
|
||||
class:disabled={$followStateStore !== "off"}
|
||||
on:click={followClick}
|
||||
>
|
||||
<img class="noselect" src={followImg} alt="" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="btn-monitor nes-btn is-dark"
|
||||
on:click={screenSharingClick}
|
||||
class:hide={!$screenSharingAvailableStore || isSilent}
|
||||
class:enabled={$requestedScreenSharingState}
|
||||
>
|
||||
{#if $requestedScreenSharingState && !isSilent}
|
||||
<img class="noselect" src={monitorImg} alt="Start screen sharing" />
|
||||
{:else}
|
||||
<img class="noselect" src={monitorCloseImg} alt="Stop screen sharing" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="btn-video nes-btn is-dark" on:click={cameraClick} class:disabled={!$requestedCameraState || isSilent}>
|
||||
{#if $requestedCameraState && !isSilent}
|
||||
<img class="noselect" src={cinemaImg} alt="Turn on webcam" />
|
||||
{:else}
|
||||
<img class="noselect" src={cinemaCloseImg} alt="Turn off webcam" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="btn-micro nes-btn is-dark" 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>
|
||||
|
||||
<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;
|
||||
z-index: 251;
|
||||
|
||||
&:hover {
|
||||
div.hide {
|
||||
transform: translateY(60px);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*btn animation*/
|
||||
.btn-cam-action div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
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;
|
||||
}
|
||||
|
||||
.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>
|
||||
|
||||
@@ -42,9 +42,8 @@
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} on:click={onClick} />
|
||||
|
||||
<aside class="chatWindow nes-container is-rounded is-dark" transition:fly={{ x: -1000, duration: 500 }}
|
||||
bind:this={chatWindowElement}>
|
||||
<p class="close-icon" on:click={closeChat}>×</p>
|
||||
<aside class="chatWindow nes-container is-rounded is-dark" transition:fly={{ x: -1000, duration: 500 }} bind:this={chatWindowElement}>
|
||||
<p class="close-icon noselect" on:click={closeChat}>×</p>
|
||||
<section class="messagesList" bind:this={listDom}>
|
||||
<ul>
|
||||
<li><p class="system-text">{$LL.chat.intro()}</p></li>
|
||||
@@ -78,7 +77,7 @@
|
||||
}
|
||||
|
||||
aside.chatWindow {
|
||||
z-index: 100;
|
||||
z-index: 1000;
|
||||
pointer-events: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
form.customCharacterScene {
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
@@ -129,7 +131,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
@include media-breakpoint-up(md) {
|
||||
form.customCharacterScene button.customCharacterSceneButtonLeft {
|
||||
left: 5vw;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<script lang="typescript">
|
||||
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import { streamableCollectionStore } from "../../Stores/StreamableCollectionStore";
|
||||
import MediaBox from "../Video/MediaBox.svelte";
|
||||
|
||||
export let highlightedEmbedScreen: EmbedScreen | null;
|
||||
export let full = false;
|
||||
$: clickable = !full;
|
||||
</script>
|
||||
|
||||
<aside class="cameras-container" class:full>
|
||||
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
|
||||
{#if !highlightedEmbedScreen || highlightedEmbedScreen.type !== "streamable" || (highlightedEmbedScreen.type === "streamable" && highlightedEmbedScreen.embed !== peer)}
|
||||
<MediaBox streamable={peer} isClickable={clickable} />
|
||||
{/if}
|
||||
{/each}
|
||||
</aside>
|
||||
|
||||
<style lang="scss">
|
||||
.cameras-container {
|
||||
flex: 0 0 25%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
&:first-child {
|
||||
margin-top: 2%;
|
||||
}
|
||||
|
||||
&.full {
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,268 @@
|
||||
<script lang="typescript">
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { ICON_URL } from "../../Enum/EnvironmentVariable";
|
||||
import { coWebsitesNotAsleep, mainCoWebsite } from "../../Stores/CoWebsiteStore";
|
||||
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import type { CoWebsite } from "../../WebRtc/CoWebsiteManager";
|
||||
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
|
||||
|
||||
export let index: number;
|
||||
export let coWebsite: CoWebsite;
|
||||
export let vertical: boolean;
|
||||
|
||||
let icon: HTMLImageElement;
|
||||
let iconLoaded = false;
|
||||
let state = coWebsite.state;
|
||||
|
||||
const coWebsiteUrl = coWebsite.iframe.src;
|
||||
const urlObject = new URL(coWebsiteUrl);
|
||||
|
||||
onMount(() => {
|
||||
iconLoaded = true;
|
||||
});
|
||||
|
||||
async function onClick() {
|
||||
if (vertical) {
|
||||
coWebsiteManager.goToMain(coWebsite);
|
||||
} else if ($mainCoWebsite) {
|
||||
if ($mainCoWebsite.iframe.id === coWebsite.iframe.id) {
|
||||
const coWebsites = $coWebsitesNotAsleep;
|
||||
const newMain = $highlightedEmbedScreen ?? coWebsites.length > 1 ? coWebsites[1] : undefined;
|
||||
if (newMain) {
|
||||
coWebsiteManager.goToMain(coWebsite);
|
||||
}
|
||||
} else {
|
||||
highlightedEmbedScreen.toggleHighlight({
|
||||
type: "cowebsite",
|
||||
embed: coWebsite,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ($state === "asleep") {
|
||||
await coWebsiteManager.loadCoWebsite(coWebsite);
|
||||
}
|
||||
|
||||
coWebsiteManager.resizeAllIframes();
|
||||
}
|
||||
|
||||
function noDrag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let isHighlight: boolean = false;
|
||||
let isMain: boolean = false;
|
||||
$: {
|
||||
isMain = $mainCoWebsite !== undefined && $mainCoWebsite.iframe === coWebsite.iframe;
|
||||
isHighlight =
|
||||
$highlightedEmbedScreen !== null &&
|
||||
$highlightedEmbedScreen.type === "cowebsite" &&
|
||||
$highlightedEmbedScreen.embed.iframe === coWebsite.iframe;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
id={"cowebsite-thumbnail-" + index}
|
||||
class="cowebsite-thumbnail nes-pointer"
|
||||
class:asleep={$state === "asleep"}
|
||||
class:loading={$state === "loading"}
|
||||
class:ready={$state === "ready"}
|
||||
class:displayed={isMain || isHighlight}
|
||||
class:vertical
|
||||
on:click={onClick}
|
||||
>
|
||||
<img
|
||||
class="cowebsite-icon noselect nes-pointer"
|
||||
class:hide={!iconLoaded}
|
||||
bind:this={icon}
|
||||
on:dragstart|preventDefault={noDrag}
|
||||
alt=""
|
||||
/>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
class="cowebsite-icon"
|
||||
class:hide={iconLoaded}
|
||||
style="margin: auto; background: rgba(0, 0, 0, 0) none repeat scroll 0% 0%; shape-rendering: auto;"
|
||||
viewBox="0 0 100 100"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
>
|
||||
<rect x="19" y="19" width="20" height="20" fill="#14304c">
|
||||
<animate
|
||||
attributeName="fill"
|
||||
values="#365dff;#14304c;#14304c"
|
||||
keyTimes="0;0.125;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
begin="0s"
|
||||
calcMode="discrete"
|
||||
/>
|
||||
</rect><rect x="40" y="19" width="20" height="20" fill="#14304c">
|
||||
<animate
|
||||
attributeName="fill"
|
||||
values="#365dff;#14304c;#14304c"
|
||||
keyTimes="0;0.125;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
begin="0.125s"
|
||||
calcMode="discrete"
|
||||
/>
|
||||
</rect><rect x="61" y="19" width="20" height="20" fill="#14304c">
|
||||
<animate
|
||||
attributeName="fill"
|
||||
values="#365dff;#14304c;#14304c"
|
||||
keyTimes="0;0.125;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
begin="0.25s"
|
||||
calcMode="discrete"
|
||||
/>
|
||||
</rect><rect x="19" y="40" width="20" height="20" fill="#14304c">
|
||||
<animate
|
||||
attributeName="fill"
|
||||
values="#365dff;#14304c;#14304c"
|
||||
keyTimes="0;0.125;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
begin="0.875s"
|
||||
calcMode="discrete"
|
||||
/>
|
||||
</rect><rect x="61" y="40" width="20" height="20" fill="#14304c">
|
||||
<animate
|
||||
attributeName="fill"
|
||||
values="#365dff;#14304c;#14304c"
|
||||
keyTimes="0;0.125;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
begin="0.375s"
|
||||
calcMode="discrete"
|
||||
/>
|
||||
</rect><rect x="19" y="61" width="20" height="20" fill="#14304c">
|
||||
<animate
|
||||
attributeName="fill"
|
||||
values="#365dff;#14304c;#14304c"
|
||||
keyTimes="0;0.125;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
begin="0.75s"
|
||||
calcMode="discrete"
|
||||
/>
|
||||
</rect><rect x="40" y="61" width="20" height="20" fill="#14304c">
|
||||
<animate
|
||||
attributeName="fill"
|
||||
values="#365dff;#14304c;#14304c"
|
||||
keyTimes="0;0.125;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
begin="0.625s"
|
||||
calcMode="discrete"
|
||||
/>
|
||||
</rect><rect x="61" y="61" width="20" height="20" fill="#14304c">
|
||||
<animate
|
||||
attributeName="fill"
|
||||
values="#365dff;#14304c;#14304c"
|
||||
keyTimes="0;0.125;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
begin="0.5s"
|
||||
calcMode="discrete"
|
||||
/>
|
||||
</rect>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.cowebsite-thumbnail {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
background-color: rgba(#000000, 0.6);
|
||||
margin: 12px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
left: -8px;
|
||||
top: -8px;
|
||||
|
||||
margin: 4px;
|
||||
|
||||
border-style: solid;
|
||||
border-width: 4px;
|
||||
border-image-slice: 3;
|
||||
border-image-width: 3;
|
||||
border-image-repeat: stretch;
|
||||
border-image-source: url('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8" ?><svg version="1.1" width="8" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M3 1 h1 v1 h-1 z M4 1 h1 v1 h-1 z M2 2 h1 v1 h-1 z M5 2 h1 v1 h-1 z M1 3 h1 v1 h-1 z M6 3 h1 v1 h-1 z M1 4 h1 v1 h-1 z M6 4 h1 v1 h-1 z M2 5 h1 v1 h-1 z M5 5 h1 v1 h-1 z M3 6 h1 v1 h-1 z M4 6 h1 v1 h-1 z" fill="rgb(33,37,41)" /></svg>');
|
||||
border-image-outset: 1;
|
||||
}
|
||||
|
||||
&.vertical {
|
||||
margin: 7px;
|
||||
|
||||
&::before {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.cowebsite-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
&.displayed {
|
||||
&:not(.vertical) {
|
||||
animation: activeThumbnail 300ms ease-in 0s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
&.asleep {
|
||||
filter: grayscale(100%);
|
||||
--webkit-filter: grayscale(100%);
|
||||
}
|
||||
|
||||
&.loading {
|
||||
animation: 2500ms ease-in-out 0s infinite alternate backgroundLoading;
|
||||
}
|
||||
|
||||
&.ready {
|
||||
&::before {
|
||||
border-image-source: url('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8" ?><svg version="1.1" width="8" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M3 1 h1 v1 h-1 z M4 1 h1 v1 h-1 z M2 2 h1 v1 h-1 z M5 2 h1 v1 h-1 z M1 3 h1 v1 h-1 z M6 3 h1 v1 h-1 z M1 4 h1 v1 h-1 z M6 4 h1 v1 h-1 z M2 5 h1 v1 h-1 z M5 5 h1 v1 h-1 z M3 6 h1 v1 h-1 z M4 6 h1 v1 h-1 z" fill="rgb(38, 74, 110)" /></svg>');
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes backgroundLoading {
|
||||
0% {
|
||||
background-color: rgba(#000000, 0.6);
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: #25598e;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes activeThumbnail {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(-15px);
|
||||
}
|
||||
}
|
||||
|
||||
.cowebsite-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
object-fit: cover;
|
||||
|
||||
&.hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<script lang="typescript">
|
||||
import { coWebsites } from "../../Stores/CoWebsiteStore";
|
||||
import CoWebsiteThumbnail from "./CoWebsiteThumbnailSlot.svelte";
|
||||
|
||||
export let vertical = false;
|
||||
</script>
|
||||
|
||||
{#if $coWebsites.length > 0}
|
||||
<div id="cowebsite-thumbnail-container" class:vertical>
|
||||
{#each [...$coWebsites.values()] as coWebsite, index (coWebsite.iframe.id)}
|
||||
<CoWebsiteThumbnail {index} {coWebsite} {vertical} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
#cowebsite-thumbnail-container {
|
||||
pointer-events: all;
|
||||
height: 100px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 2%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
&.vertical {
|
||||
height: auto !important;
|
||||
width: auto !important;
|
||||
bottom: auto !important;
|
||||
left: auto !important;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script lang="typescript">
|
||||
import PresentationLayout from "./Layouts/PresentationLayout.svelte";
|
||||
import MozaicLayout from "./Layouts/MozaicLayout.svelte";
|
||||
import { LayoutMode } from "../../WebRtc/LayoutManager";
|
||||
import { embedScreenLayout } from "../../Stores/EmbedScreensStore";
|
||||
</script>
|
||||
|
||||
<div id="embedScreensContainer">
|
||||
{#if $embedScreenLayout === LayoutMode.Presentation}
|
||||
<PresentationLayout />
|
||||
{:else}
|
||||
<MozaicLayout />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#embedScreensContainer {
|
||||
display: flex;
|
||||
padding-top: 2%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,61 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { highlightedEmbedScreen } from "../../../Stores/EmbedScreensStore";
|
||||
import { streamableCollectionStore } from "../../../Stores/StreamableCollectionStore";
|
||||
import MediaBox from "../../Video/MediaBox.svelte";
|
||||
|
||||
let layoutDom: HTMLDivElement;
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {});
|
||||
|
||||
onMount(() => {
|
||||
resizeObserver.observe(layoutDom);
|
||||
highlightedEmbedScreen.removeHighlight();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mozaic-layout" bind:this={layoutDom}>
|
||||
<div
|
||||
class="media-container"
|
||||
class:full-width={$streamableCollectionStore.size === 1 || $streamableCollectionStore.size === 2}
|
||||
class:quarter={$streamableCollectionStore.size === 3 || $streamableCollectionStore.size === 4}
|
||||
>
|
||||
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
|
||||
<MediaBox
|
||||
streamable={peer}
|
||||
mozaicFullWidth={$streamableCollectionStore.size === 1 || $streamableCollectionStore.size === 2}
|
||||
mozaicQuarter={$streamableCollectionStore.size === 3 || $streamableCollectionStore.size === 4}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#mozaic-layout {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
.media-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 33.3% 33.3% 33.3%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
&.full-width {
|
||||
grid-template-columns: 100%;
|
||||
grid-template-rows: 50% 50%;
|
||||
}
|
||||
|
||||
&.quarter {
|
||||
grid-template-columns: 50% 50%;
|
||||
grid-template-rows: 50% 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,143 @@
|
||||
<script lang="ts">
|
||||
import { highlightedEmbedScreen } from "../../../Stores/EmbedScreensStore";
|
||||
import CamerasContainer from "../CamerasContainer.svelte";
|
||||
import MediaBox from "../../Video/MediaBox.svelte";
|
||||
import { coWebsiteManager } from "../../../WebRtc/CoWebsiteManager";
|
||||
import { afterUpdate, onMount } from "svelte";
|
||||
import { isMediaBreakpointDown, isMediaBreakpointUp } from "../../../Utils/BreakpointsUtils";
|
||||
import { peerStore } from "../../../Stores/PeerStore";
|
||||
|
||||
function closeCoWebsite() {
|
||||
if ($highlightedEmbedScreen?.type === "cowebsite") {
|
||||
if ($highlightedEmbedScreen.embed.closable) {
|
||||
coWebsiteManager.closeCoWebsite($highlightedEmbedScreen.embed).catch(() => {
|
||||
console.error("Error during co-website highlighted closing");
|
||||
});
|
||||
} else {
|
||||
coWebsiteManager.unloadCoWebsite($highlightedEmbedScreen.embed).catch(() => {
|
||||
console.error("Error during co-website highlighted unloading");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterUpdate(() => {
|
||||
if ($highlightedEmbedScreen) {
|
||||
coWebsiteManager.resizeAllIframes();
|
||||
}
|
||||
});
|
||||
|
||||
let layoutDom: HTMLDivElement;
|
||||
|
||||
let displayCoWebsiteContainer = isMediaBreakpointDown("lg");
|
||||
let displayFullMedias = isMediaBreakpointUp("sm");
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
displayCoWebsiteContainer = isMediaBreakpointDown("lg");
|
||||
displayFullMedias = isMediaBreakpointUp("sm");
|
||||
|
||||
if (!displayCoWebsiteContainer && $highlightedEmbedScreen && $highlightedEmbedScreen.type === "cowebsite") {
|
||||
highlightedEmbedScreen.removeHighlight();
|
||||
}
|
||||
|
||||
if (displayFullMedias) {
|
||||
highlightedEmbedScreen.removeHighlight();
|
||||
}
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
resizeObserver.observe(layoutDom);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="presentation-layout" bind:this={layoutDom} class:full-medias={displayFullMedias}>
|
||||
{#if displayFullMedias}
|
||||
<div id="full-medias">
|
||||
<CamerasContainer full={true} highlightedEmbedScreen={$highlightedEmbedScreen} />
|
||||
</div>
|
||||
{:else}
|
||||
<div id="embed-left-block" class:full={$peerStore.size === 0}>
|
||||
<div id="main-embed-screen">
|
||||
{#if $highlightedEmbedScreen}
|
||||
{#if $highlightedEmbedScreen.type === "streamable"}
|
||||
{#key $highlightedEmbedScreen.embed.uniqueId}
|
||||
<MediaBox
|
||||
isHightlighted={true}
|
||||
isClickable={true}
|
||||
streamable={$highlightedEmbedScreen.embed}
|
||||
/>
|
||||
{/key}
|
||||
{:else if $highlightedEmbedScreen.type === "cowebsite"}
|
||||
{#key $highlightedEmbedScreen.embed.iframe.id}
|
||||
<div
|
||||
id={"cowebsite-slot-" + $highlightedEmbedScreen.embed.iframe.id}
|
||||
class="highlighted-cowebsite nes-container is-rounded"
|
||||
>
|
||||
<div class="actions">
|
||||
<button type="button" class="nes-btn is-error close" on:click={closeCoWebsite}
|
||||
>×</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/key}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $peerStore.size > 0}
|
||||
<CamerasContainer highlightedEmbedScreen={$highlightedEmbedScreen} />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#presentation-layout {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
&.full-medias {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
#embed-left-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0 0 75%;
|
||||
height: 100%;
|
||||
width: 75%;
|
||||
|
||||
&.full {
|
||||
flex: 0 0 98% !important;
|
||||
width: 98% !important;
|
||||
}
|
||||
}
|
||||
|
||||
#main-embed-screen {
|
||||
height: 100%;
|
||||
margin-bottom: 3%;
|
||||
|
||||
.highlighted-cowebsite {
|
||||
height: 100% !important;
|
||||
width: 96%;
|
||||
background-color: rgba(#000000, 0.6);
|
||||
margin: 0 !important;
|
||||
|
||||
.actions {
|
||||
z-index: 200;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: end;
|
||||
gap: 2%;
|
||||
|
||||
button {
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -3,8 +3,8 @@
|
||||
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { EmojiButton } from "@joeattardi/emoji-button";
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
import LL from "../../i18n/i18n-svelte";
|
||||
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
|
||||
|
||||
let emojiContainer: HTMLElement;
|
||||
let picker: EmojiButton;
|
||||
@@ -20,7 +20,7 @@
|
||||
"--secondary-text-color": "whitesmoke",
|
||||
"--category-button-color": "whitesmoke",
|
||||
},
|
||||
emojisPerRow: isMobile() ? 6 : 8,
|
||||
emojisPerRow: isMediaBreakpointUp("md") ? 6 : 8,
|
||||
autoFocusSearch: false,
|
||||
style: "native",
|
||||
showPreview: false,
|
||||
@@ -86,6 +86,8 @@
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
z-index: 300;
|
||||
}
|
||||
|
||||
.emote-menu {
|
||||
|
||||
@@ -127,6 +127,8 @@
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
.enableCameraScene {
|
||||
pointer-events: auto;
|
||||
margin: 20px auto 0;
|
||||
@@ -214,7 +216,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
@include media-breakpoint-up(md) {
|
||||
.enableCameraScene h2 {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<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 {
|
||||
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>
|
||||
@@ -1,9 +1,5 @@
|
||||
<!--
|
||||
vim: ft=typescript
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
import followImg from "../images/follow.svg";
|
||||
import { followStateStore, followRoleStore, followUsersStore } from "../../Stores/FollowStore";
|
||||
import LL from "../../i18n/i18n-svelte";
|
||||
|
||||
@@ -14,10 +10,6 @@ vim: ft=typescript
|
||||
return user ? user.PlayerValue : "";
|
||||
}
|
||||
|
||||
function sendFollowRequest() {
|
||||
gameScene.CurrentPlayer.sendFollowRequest();
|
||||
}
|
||||
|
||||
function acceptFollowRequest() {
|
||||
gameScene.CurrentPlayer.startFollowing();
|
||||
}
|
||||
@@ -83,7 +75,7 @@ vim: ft=typescript
|
||||
|
||||
{#if $followStateStore === "active" || $followStateStore === "ending"}
|
||||
<div class="interact-status nes-container is-rounded">
|
||||
<section class="interact-status">
|
||||
<section>
|
||||
{#if $followRoleStore === "follower"}
|
||||
<p>{$LL.follow.interactStatus.following({ leader: name($followUsersStore[0]) })}</p>
|
||||
{:else if $followUsersStore.length === 0}
|
||||
@@ -109,48 +101,27 @@ vim: ft=typescript
|
||||
</div>
|
||||
{/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">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
.nes-container {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
div.interact-status {
|
||||
.interact-status {
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
position: relative;
|
||||
height: 2.7em;
|
||||
position: absolute;
|
||||
max-height: 2.7em;
|
||||
width: 40vw;
|
||||
top: 87vh;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
z-index: 400;
|
||||
}
|
||||
|
||||
div.interact-menu {
|
||||
@@ -158,10 +129,14 @@ vim: ft=typescript
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
position: relative;
|
||||
position: absolute;
|
||||
width: 60vw;
|
||||
top: 60vh;
|
||||
margin: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
z-index: 150;
|
||||
|
||||
section.interact-menu-title {
|
||||
margin-bottom: 20px;
|
||||
@@ -189,23 +164,16 @@ vim: ft=typescript
|
||||
}
|
||||
}
|
||||
|
||||
.follow-menu-button {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
div.interact-status {
|
||||
width: 100vw;
|
||||
@include media-breakpoint-up(md) {
|
||||
.interact-status {
|
||||
width: 90vw;
|
||||
top: 78vh;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
div.interact-menu {
|
||||
height: 21vh;
|
||||
width: 100vw;
|
||||
max-height: 21vh;
|
||||
width: 90vw;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<form
|
||||
class="helpCameraSettings nes-container"
|
||||
on:submit|preventDefault={close}
|
||||
transition:fly={{ y: -900, duration: 500 }}
|
||||
transition:fly={{ y: -50, duration: 500 }}
|
||||
>
|
||||
<section>
|
||||
<h2>{$LL.camera.help.title()}</h2>
|
||||
@@ -55,9 +55,12 @@
|
||||
background: #eceeee;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10vh;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-top: 4%;
|
||||
max-height: 80vh;
|
||||
max-width: 80vw;
|
||||
z-index: 600;
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
|
||||
|
||||
+7
-1
@@ -35,9 +35,11 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 40px;
|
||||
margin: 0 auto;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding: 0;
|
||||
width: clamp(200px, 20vw, 20vw);
|
||||
z-index: 155;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -45,6 +47,10 @@
|
||||
animation: moveMessage 0.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: ease-in-out;
|
||||
|
||||
div {
|
||||
margin-bottom: 5%;
|
||||
}
|
||||
}
|
||||
|
||||
div.nes-container {
|
||||
@@ -0,0 +1,170 @@
|
||||
<script lang="typescript">
|
||||
import { onMount } from "svelte";
|
||||
import { audioManagerVisibilityStore } from "../Stores/AudioManagerStore";
|
||||
import { embedScreenLayout, 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 { isMediaBreakpointDown, 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";
|
||||
import { LayoutMode } from "../WebRtc/LayoutManager";
|
||||
|
||||
let mainLayout: HTMLDivElement;
|
||||
|
||||
let displayCoWebsiteContainerMd = isMediaBreakpointUp("md");
|
||||
let displayCoWebsiteContainerLg = isMediaBreakpointDown("lg");
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
displayCoWebsiteContainerMd = isMediaBreakpointUp("md");
|
||||
displayCoWebsiteContainerLg = isMediaBreakpointDown("lg");
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
resizeObserver.observe(mainLayout);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="main-layout" bind:this={mainLayout}>
|
||||
<aside id="main-layout-left-aside">
|
||||
{#if $menuIconVisiblilityStore}
|
||||
<MenuIcon />
|
||||
{/if}
|
||||
|
||||
{#if $embedScreenLayout === LayoutMode.VideoChat || displayCoWebsiteContainerMd}
|
||||
<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 $warningContainerStore}
|
||||
<WarningContainer />
|
||||
{/if}
|
||||
|
||||
{#if $showReportScreenStore !== userReportEmpty}
|
||||
<ReportMenu />
|
||||
{/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 displayCoWebsiteContainerLg}
|
||||
<CoWebsitesContainer />
|
||||
{/if}
|
||||
|
||||
{#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: 120px calc(100% - 120px);
|
||||
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>
|
||||
@@ -100,6 +100,8 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
.string-HTML {
|
||||
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 {
|
||||
section.container-overflow {
|
||||
height: calc(100% - 120px);
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
div.global-message-main {
|
||||
height: calc(100% - 50px);
|
||||
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 {
|
||||
height: calc(100% - 5px);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
div.guest-main {
|
||||
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 {
|
||||
section.share-url.not-mobile {
|
||||
display: none;
|
||||
|
||||
@@ -129,6 +129,8 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
.nes-container {
|
||||
padding: 5px;
|
||||
}
|
||||
@@ -140,11 +142,15 @@
|
||||
pointer-events: auto;
|
||||
height: 80%;
|
||||
width: 75%;
|
||||
top: 10%;
|
||||
top: 4%;
|
||||
|
||||
position: relative;
|
||||
z-index: 80;
|
||||
margin: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
position: absolute;
|
||||
z-index: 900;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: var(--size-first-columns-grid) calc(100% - var(--size-first-columns-grid));
|
||||
@@ -177,12 +183,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
@include media-breakpoint-up(md) {
|
||||
div.menu-container-main {
|
||||
--size-first-columns-grid: 120px;
|
||||
height: 70%;
|
||||
top: 55px;
|
||||
width: 100%;
|
||||
width: 95%;
|
||||
font-size: 0.5em;
|
||||
|
||||
div.menu-nav-sidebar {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
function showMenu() {
|
||||
menuVisiblilityStore.set(!get(menuVisiblilityStore));
|
||||
}
|
||||
|
||||
function showChat() {
|
||||
chatVisibilityStore.set(true);
|
||||
}
|
||||
@@ -21,68 +22,104 @@
|
||||
function register() {
|
||||
window.open(`${ADMIN_URL}/second-step-register`, "_self");
|
||||
}
|
||||
|
||||
function showInvite() {
|
||||
showShareLinkMapModalStore.set(true);
|
||||
}
|
||||
|
||||
function noDrag() {
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window />
|
||||
|
||||
<main class="menuIcon">
|
||||
<main class="menuIcon noselect">
|
||||
{#if $limitMapStore}
|
||||
<span class="nes-btn is-dark">
|
||||
<img
|
||||
src={logoInvite}
|
||||
alt={$LL.menu.icon.open.invite()}
|
||||
class="nes-pointer"
|
||||
on:click|preventDefault={showInvite}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span class="nes-btn is-dark">
|
||||
<img
|
||||
src={logoRegister}
|
||||
alt={$LL.menu.icon.open.register()}
|
||||
class="nes-pointer"
|
||||
on:click|preventDefault={register}
|
||||
/>
|
||||
</span>
|
||||
<span class="nes-btn is-dark">
|
||||
<img
|
||||
src={logoInvite}
|
||||
alt={$LL.menu.icon.open.invite()}
|
||||
class="nes-pointer"
|
||||
draggable="false"
|
||||
on:dragstart|preventDefault={noDrag}
|
||||
on:click|preventDefault={showInvite}
|
||||
/>
|
||||
</span>
|
||||
<span class="nes-btn is-dark">
|
||||
<img
|
||||
src={logoRegister}
|
||||
alt={$LL.menu.icon.open.register()}
|
||||
class="nes-pointer"
|
||||
draggable="false"
|
||||
on:dragstart|preventDefault={noDrag}
|
||||
on:click|preventDefault={register}
|
||||
/>
|
||||
</span>
|
||||
{:else}
|
||||
<span class="nes-btn is-dark">
|
||||
<img src={logoWA} alt={$LL.menu.icon.open.menu()} class="nes-pointer" on:click|preventDefault={showMenu} />
|
||||
</span>
|
||||
<span class="nes-btn is-dark">
|
||||
<img src={logoTalk} alt={$LL.menu.icon.open.chat()} class="nes-pointer" on:click|preventDefault={showChat} />
|
||||
</span>
|
||||
<span class="nes-btn is-dark">
|
||||
<img
|
||||
src={logoWA}
|
||||
alt={$LL.menu.icon.open.menu()}
|
||||
class="nes-pointer"
|
||||
draggable="false"
|
||||
on:dragstart|preventDefault={noDrag}
|
||||
on:click|preventDefault={showMenu}
|
||||
/>
|
||||
</span>
|
||||
<span class="nes-btn is-dark">
|
||||
<img
|
||||
src={logoTalk}
|
||||
alt={$LL.menu.icon.open.chat()}
|
||||
class="nes-pointer"
|
||||
draggable="false"
|
||||
on:dragstart|preventDefault={noDrag}
|
||||
on:click|preventDefault={showChat}
|
||||
/>
|
||||
</span>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
.menuIcon {
|
||||
display: inline-grid;
|
||||
z-index: 90;
|
||||
position: relative;
|
||||
margin: 25px;
|
||||
img {
|
||||
pointer-events: auto;
|
||||
width: 24px;
|
||||
padding-top: 0;
|
||||
margin: 3px
|
||||
}
|
||||
}
|
||||
|
||||
.menuIcon img:hover{
|
||||
transform: scale(1.2);
|
||||
}
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
@media only screen and (max-width: 800px),
|
||||
only screen and (max-height: 800px) {
|
||||
.menuIcon {
|
||||
margin: 6px;
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
}
|
||||
display: inline-grid;
|
||||
z-index: 90;
|
||||
position: relative;
|
||||
margin: 25px;
|
||||
img {
|
||||
pointer-events: auto;
|
||||
width: 24px;
|
||||
padding-top: 0;
|
||||
margin: 3px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menuIcon img:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.menuIcon {
|
||||
margin-top: 10%;
|
||||
img {
|
||||
pointer-events: auto;
|
||||
width: 60px;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
.menuIcon img:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
.menuIcon {
|
||||
img {
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
import { EnableCameraScene, EnableCameraSceneName } from "../../Phaser/Login/EnableCameraScene";
|
||||
import { enableCameraSceneVisibilityStore } from "../../Stores/MediaStore";
|
||||
import btnProfileSubMenuCamera from "../images/btn-menu-profile-camera.svg";
|
||||
import btnProfileSubMenuIdentity from "../images/btn-menu-profile-identity.svg";
|
||||
import btnProfileSubMenuCompanion from "../images/btn-menu-profile-companion.svg";
|
||||
import Woka from "../Woka/Woka.svelte";
|
||||
import Companion from "../Companion/Companion.svelte";
|
||||
@@ -104,6 +103,8 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
div.customize-main {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
@@ -163,7 +164,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
@include media-breakpoint-up(md) {
|
||||
div.customize-main.content section button {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { videoConstraintStore } from "../../Stores/MediaStore";
|
||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
import { menuVisiblilityStore } from "../../Stores/MenuStore";
|
||||
import LL, { locale } from "../../i18n/i18n-svelte";
|
||||
import type { Locales } from "../../i18n/i18n-types";
|
||||
import { displayableLocales, setCurrentLocale } from "../../i18n/locales";
|
||||
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
|
||||
|
||||
let fullscreen: boolean = localUserStore.getFullscreen();
|
||||
let notification: boolean = localUserStore.getNotification() === "granted";
|
||||
@@ -85,6 +85,8 @@
|
||||
function closeMenu() {
|
||||
menuVisiblilityStore.set(false);
|
||||
}
|
||||
|
||||
const isMobile = isMediaBreakpointUp("md");
|
||||
</script>
|
||||
|
||||
<div class="settings-main" on:submit|preventDefault={saveSetting}>
|
||||
@@ -93,22 +95,22 @@
|
||||
<div class="nes-select is-dark">
|
||||
<select bind:value={valueGame}>
|
||||
<option value={120}
|
||||
>{isMobile()
|
||||
>{isMobile
|
||||
? $LL.menu.settings.gameQuality.short.high()
|
||||
: $LL.menu.settings.gameQuality.long.high()}</option
|
||||
>
|
||||
<option value={60}
|
||||
>{isMobile()
|
||||
>{isMobile
|
||||
? $LL.menu.settings.gameQuality.short.medium()
|
||||
: $LL.menu.settings.gameQuality.long.medium()}</option
|
||||
>
|
||||
<option value={40}
|
||||
>{isMobile()
|
||||
>{isMobile
|
||||
? $LL.menu.settings.gameQuality.short.small()
|
||||
: $LL.menu.settings.gameQuality.long.small()}</option
|
||||
>
|
||||
<option value={20}
|
||||
>{isMobile()
|
||||
>{isMobile
|
||||
? $LL.menu.settings.gameQuality.short.minimum()
|
||||
: $LL.menu.settings.gameQuality.long.minimum()}</option
|
||||
>
|
||||
@@ -120,22 +122,22 @@
|
||||
<div class="nes-select is-dark">
|
||||
<select bind:value={valueVideo}>
|
||||
<option value={30}
|
||||
>{isMobile()
|
||||
>{isMobile
|
||||
? $LL.menu.settings.videoQuality.short.high()
|
||||
: $LL.menu.settings.videoQuality.long.high()}</option
|
||||
>
|
||||
<option value={20}
|
||||
>{isMobile()
|
||||
>{isMobile
|
||||
? $LL.menu.settings.videoQuality.short.medium()
|
||||
: $LL.menu.settings.videoQuality.long.medium()}</option
|
||||
>
|
||||
<option value={10}
|
||||
>{isMobile()
|
||||
>{isMobile
|
||||
? $LL.menu.settings.videoQuality.short.small()
|
||||
: $LL.menu.settings.videoQuality.long.small()}</option
|
||||
>
|
||||
<option value={5}
|
||||
>{isMobile()
|
||||
>{isMobile
|
||||
? $LL.menu.settings.videoQuality.short.minimum()
|
||||
: $LL.menu.settings.videoQuality.long.minimum()}</option
|
||||
>
|
||||
@@ -199,6 +201,8 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
div.settings-main {
|
||||
height: calc(100% - 40px);
|
||||
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 {
|
||||
section {
|
||||
padding: 0;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
max-width: 80vw;
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
z-index: 500;
|
||||
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
max-width: 80vw;
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
z-index: 450;
|
||||
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
|
||||
import { localStreamStore, isSilentStore } from "../Stores/MediaStore";
|
||||
import SoundMeterWidget from "./SoundMeterWidget.svelte";
|
||||
import { onDestroy } from "svelte";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { srcObject } from "./Video/utils";
|
||||
import LL from "../i18n/i18n-svelte";
|
||||
|
||||
@@ -23,16 +23,75 @@
|
||||
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);
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="video-container nes-container is-rounded is-dark div-myCamVideo"
|
||||
class:hide={!$obtainedMediaConstraintStore.video || isSilent}>
|
||||
{#if $localStreamStore.type === "success" && $localStreamStore.stream}
|
||||
<video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline />
|
||||
<SoundMeterWidget {stream} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="nes-container is-dark is-silent" class:hide={isSilent}>{$LL.camera.my.silentZone()}</div>
|
||||
<div
|
||||
class="nes-container is-rounded my-cam-video-container"
|
||||
class:hide={($localStreamStore.type !== "success" || !$obtainedMediaConstraintStore.video) && !isSilent}
|
||||
bind:this={cameraContainer}
|
||||
>
|
||||
{#if isSilent}
|
||||
<div class="is-silent">{$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>
|
||||
|
||||
<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: rgba(#000000, 0.6);
|
||||
background-clip: content-box;
|
||||
overflow: hidden;
|
||||
line-height: 0;
|
||||
z-index: 250;
|
||||
|
||||
&.nes-container.is-rounded {
|
||||
border-image-outset: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.my-cam-video-container.hide {
|
||||
transform: translateX(200%);
|
||||
}
|
||||
|
||||
.my-cam-video {
|
||||
background-color: #00000099;
|
||||
max-height: 20vh;
|
||||
max-width: max(25vw, 150px);
|
||||
width: 100%;
|
||||
-webkit-transform: scaleX(-1);
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.is-silent {
|
||||
font-size: 2em;
|
||||
color: white;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -108,12 +108,16 @@
|
||||
pointer-events: auto;
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
|
||||
position: relative;
|
||||
z-index: 650;
|
||||
position: absolute;
|
||||
height: 70vh;
|
||||
width: 50vw;
|
||||
top: 10vh;
|
||||
margin: auto;
|
||||
top: 4%;
|
||||
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
section.report-menu-title {
|
||||
display: grid;
|
||||
@@ -137,13 +141,4 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
div.report-menu-main {
|
||||
top: 21vh;
|
||||
height: 60vh;
|
||||
width: 100vw;
|
||||
font-size: 0.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -47,6 +47,8 @@
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
form.selectCompanionScene {
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
@@ -85,7 +87,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
@include media-breakpoint-up(md) {
|
||||
form.selectCompanionScene button.selectCharacterButtonLeft {
|
||||
left: 5vw;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { fly, fade } from "svelte/transition";
|
||||
import { onMount } from "svelte";
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
import type { Message } from "../../Stores/TypeMessageStore/MessageStore";
|
||||
import { banMessageStore } from "../../Stores/TypeMessageStore/BanMessageStore";
|
||||
import LL from "../../i18n/i18n-svelte";
|
||||
@@ -13,6 +14,8 @@
|
||||
|
||||
onMount(() => {
|
||||
timeToRead();
|
||||
const gameScene = gameManager.getCurrentGameScene();
|
||||
gameScene.playSound("audio-report-message");
|
||||
});
|
||||
|
||||
function timeToRead() {
|
||||
@@ -53,18 +56,19 @@
|
||||
on:click|preventDefault={closeBanMessage}>{nameButton}</button
|
||||
>
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<audio id="report-message" autoplay>
|
||||
<source src="/resources/objects/report-message.mp3" type="audio/mp3" />
|
||||
</audio>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.main-ban-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
top: 15vh;
|
||||
position: absolute;
|
||||
top: 4%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
z-index: 850;
|
||||
|
||||
height: 70vh;
|
||||
width: 60vw;
|
||||
|
||||
@@ -11,3 +11,9 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.main-ban-message-container {
|
||||
z-index: 800;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,14 +42,17 @@
|
||||
div.main-text-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
|
||||
max-height: 25vh;
|
||||
width: 80vw;
|
||||
max-height: 25%;
|
||||
width: 60%;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
margin-bottom: 16px;
|
||||
margin-top: 0;
|
||||
top: 6%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-bottom: 0;
|
||||
z-index: 240;
|
||||
|
||||
pointer-events: auto;
|
||||
background-color: #333333;
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.main-text-message-container {
|
||||
.main-text-message-container {
|
||||
padding-top: 16px;
|
||||
z-index: 800;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
background-color: black;
|
||||
border-radius: 30px 0 0 30px;
|
||||
display: inline-flex;
|
||||
z-index: 750;
|
||||
|
||||
img {
|
||||
border-radius: 50%;
|
||||
|
||||
@@ -25,11 +25,17 @@
|
||||
<style lang="scss">
|
||||
div.error-div {
|
||||
pointer-events: auto;
|
||||
margin-top: 10vh;
|
||||
margin-top: 4%;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
width: max-content;
|
||||
max-width: 80vw;
|
||||
z-index: 230;
|
||||
height: auto !important;
|
||||
background-clip: padding-box;
|
||||
|
||||
.button-bar {
|
||||
text-align: center;
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
<script lang="typescript">
|
||||
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import type { ScreenSharingLocalMedia } from "../../Stores/ScreenSharingStore";
|
||||
import { videoFocusStore } from "../../Stores/VideoFocusStore";
|
||||
import type { Streamable } from "../../Stores/StreamableCollectionStore";
|
||||
import { srcObject } from "./utils";
|
||||
|
||||
export let clickable = false;
|
||||
|
||||
export let peer: ScreenSharingLocalMedia;
|
||||
let stream = peer.stream;
|
||||
export let cssClass: string | undefined;
|
||||
let embedScreen: EmbedScreen;
|
||||
|
||||
if (stream) {
|
||||
embedScreen = {
|
||||
type: "streamable",
|
||||
embed: stream as unknown as Streamable,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="video-container {cssClass ? cssClass : ''}" class:hide={!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}
|
||||
</div>
|
||||
|
||||
@@ -7,14 +7,106 @@
|
||||
import type { Streamable } from "../../Stores/StreamableCollectionStore";
|
||||
|
||||
export let streamable: Streamable;
|
||||
export let isHightlighted = false;
|
||||
export let isClickable = false;
|
||||
export let mozaicFullWidth = false;
|
||||
export let mozaicQuarter = false;
|
||||
</script>
|
||||
|
||||
<div class="media-container">
|
||||
{#if streamable instanceof VideoPeer}
|
||||
<VideoMediaBox peer={streamable} />
|
||||
{:else if streamable instanceof ScreenSharingPeer}
|
||||
<ScreenSharingMediaBox peer={streamable} />
|
||||
{:else}
|
||||
<LocalStreamMediaBox peer={streamable} cssClass="" />
|
||||
{/if}
|
||||
<div
|
||||
class="media-container nes-container is-rounded {isHightlighted ? 'hightlighted' : ''}"
|
||||
class:clickable={isClickable}
|
||||
class:mozaic-full-width={mozaicFullWidth}
|
||||
class:mozaic-quarter={mozaicQuarter}
|
||||
>
|
||||
<div>
|
||||
{#if streamable instanceof VideoPeer}
|
||||
<VideoMediaBox peer={streamable} clickable={isClickable} />
|
||||
{:else if streamable instanceof ScreenSharingPeer}
|
||||
<ScreenSharingMediaBox peer={streamable} clickable={isClickable} />
|
||||
{:else}
|
||||
<LocalStreamMediaBox peer={streamable} clickable={isClickable} cssClass="" />
|
||||
{/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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
&.mozaic-full-width {
|
||||
width: 95%;
|
||||
max-width: 95%;
|
||||
margin-left: 3%;
|
||||
margin-right: 3%;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
|
||||
&:hover {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.mozaic-quarter {
|
||||
width: 95%;
|
||||
max-width: 95%;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
|
||||
&:hover {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.nes-container.is-rounded {
|
||||
border-image-outset: 1;
|
||||
}
|
||||
|
||||
> div {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-only(md) {
|
||||
.media-container {
|
||||
margin-top: 10%;
|
||||
margin-bottom: 10%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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>
|
||||
@@ -1,12 +1,26 @@
|
||||
<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 { videoFocusStore } from "../../Stores/VideoFocusStore";
|
||||
import { getColorByString, srcObject } from "./utils";
|
||||
|
||||
export let clickable = false;
|
||||
|
||||
export let peer: ScreenSharingPeer;
|
||||
let streamStore = peer.streamStore;
|
||||
let name = peer.userName;
|
||||
let statusStore = peer.statusStore;
|
||||
|
||||
let embedScreen: EmbedScreen;
|
||||
|
||||
if (peer) {
|
||||
embedScreen = {
|
||||
type: "streamable",
|
||||
embed: peer as unknown as Streamable,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="video-container">
|
||||
@@ -20,7 +34,12 @@
|
||||
<i style="background-color: {getColorByString(name)};">{name}</i>
|
||||
{:else}
|
||||
<!-- 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}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,11 +4,17 @@
|
||||
import microphoneCloseImg from "../images/microphone-close.svg";
|
||||
import reportImg from "./images/report.svg";
|
||||
import blockSignImg from "./images/blockSign.svg";
|
||||
import { videoFocusStore } from "../../Stores/VideoFocusStore";
|
||||
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
|
||||
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 { onMount } from "svelte";
|
||||
import { isMediaBreakpointOnly } from "../../Utils/BreakpointsUtils";
|
||||
|
||||
export let clickable = false;
|
||||
|
||||
export let peer: VideoPeer;
|
||||
let streamStore = peer.streamStore;
|
||||
@@ -19,9 +25,32 @@
|
||||
function openReport(peer: VideoPeer): void {
|
||||
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>
|
||||
|
||||
<div class="video-container nes-container is-rounded is-dark">
|
||||
<div class="video-container" class:no-clikable={!clickable} bind:this={videoContainer}>
|
||||
{#if $statusStore === "connecting"}
|
||||
<div class="connecting-spinner" />
|
||||
{/if}
|
||||
@@ -30,42 +59,64 @@
|
||||
{/if}
|
||||
<!-- {#if !$constraintStore || $constraintStore.video === false} -->
|
||||
<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)};"
|
||||
>
|
||||
<span>{peer.userName}</span>
|
||||
<span style="noselect">{peer.userName}</span>
|
||||
<div class="woka-icon"><Woka userId={peer.userId} placeholderSrc={""} /></div>
|
||||
</i>
|
||||
<!-- {/if} -->
|
||||
{#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}
|
||||
<button class="report nes-button is-dark" on:click={() => openReport(peer)}>
|
||||
<img alt="Report this user" src={reportImg} />
|
||||
<span>Report/Block</span>
|
||||
<img alt="Report this user" draggable="false" on:dragstart|preventDefault={noDrag} src={reportImg} />
|
||||
<span class="noselect">Report/Block</span>
|
||||
</button>
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)} />
|
||||
<img src={blockSignImg} class="block-logo" alt="Block" />
|
||||
<video
|
||||
class:no-video={!$constraintStore || $constraintStore.video === false}
|
||||
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}
|
||||
<SoundMeterWidget stream={$streamStore} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.minimized {
|
||||
left: auto;
|
||||
transform: scale(0.5);
|
||||
opacity: 0.5;
|
||||
}
|
||||
&.has-video {
|
||||
left: auto;
|
||||
transform: scale(0.5);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.woka-icon {
|
||||
margin-right: 3px;
|
||||
&.minimized {
|
||||
transform: scale(0.5);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.woka-icon {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
video.no-video {
|
||||
visibility: collapse;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { LayoutMode } from "../../WebRtc/LayoutManager";
|
||||
import { layoutModeStore } from "../../Stores/StreamableCollectionStore";
|
||||
import PresentationLayout from "./PresentationLayout.svelte";
|
||||
import ChatLayout from "./ChatLayout.svelte";
|
||||
// import {LayoutMode} from "../../WebRtc/LayoutManager";
|
||||
// import {layoutModeStore} from "../../Stores/StreamableCollectionStore";
|
||||
// import PresentationLayout from "./PresentationLayout.svelte";
|
||||
// import ChatLayout from "./ChatLayout.svelte";
|
||||
</script>
|
||||
|
||||
<div class="video-overlay">
|
||||
{#if $layoutModeStore === LayoutMode.Presentation}
|
||||
<!-- {#if $layoutModeStore === LayoutMode.Presentation }
|
||||
<PresentationLayout />
|
||||
{:else}
|
||||
<ChatLayout />
|
||||
{/if}
|
||||
{/if} -->
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
height: 120px;
|
||||
margin: auto;
|
||||
animation: spin 2s linear infinite;
|
||||
z-index: 350;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
|
||||
@@ -27,18 +27,22 @@
|
||||
<style lang="scss">
|
||||
main.warningMain {
|
||||
pointer-events: auto;
|
||||
width: 100vw;
|
||||
width: 80%;
|
||||
background-color: #f9e81e;
|
||||
color: #14304c;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
|
||||
top: 4%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
transform: translate(-50%, 0);
|
||||
font-family: Lato;
|
||||
min-width: 300px;
|
||||
opacity: 0.9;
|
||||
z-index: 2;
|
||||
|
||||
z-index: 700;
|
||||
h2 {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
@@ -22,10 +22,21 @@
|
||||
src = source ?? placeholderSrc;
|
||||
});
|
||||
|
||||
function noDrag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
onDestroy(unsubscribe);
|
||||
</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>
|
||||
img {
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
form.selectCharacterScene {
|
||||
font-family: "Press Start 2P";
|
||||
pointer-events: auto;
|
||||
@@ -91,7 +93,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
@include media-breakpoint-up(md) {
|
||||
form.selectCharacterScene button.selectCharacterButtonLeft {
|
||||
left: 5vw;
|
||||
}
|
||||
|
||||
@@ -1,33 +1,46 @@
|
||||
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true";
|
||||
const START_ROOM_URL: string =
|
||||
process.env.START_ROOM_URL || "/_/global/maps.workadventure.localhost/Floor1/floor1.json";
|
||||
const PUSHER_URL = process.env.PUSHER_URL || "//pusher.workadventure.localhost";
|
||||
export const ADMIN_URL = process.env.ADMIN_URL || "//workadventu.re";
|
||||
const UPLOADER_URL = process.env.UPLOADER_URL || "//uploader.workadventure.localhost";
|
||||
const ICON_URL = process.env.ICON_URL || "//icon.workadventure.localhost";
|
||||
const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302";
|
||||
const TURN_SERVER: string = process.env.TURN_SERVER || "";
|
||||
const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true";
|
||||
const DISABLE_NOTIFICATIONS: boolean = process.env.DISABLE_NOTIFICATIONS == "true";
|
||||
const TURN_USER: string = process.env.TURN_USER || "";
|
||||
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || "";
|
||||
const JITSI_URL: string | undefined = process.env.JITSI_URL === "" ? undefined : process.env.JITSI_URL;
|
||||
const JITSI_PRIVATE_MODE: boolean = process.env.JITSI_PRIVATE_MODE == "true";
|
||||
declare global {
|
||||
interface Window {
|
||||
env?: Record<string, string>;
|
||||
}
|
||||
}
|
||||
|
||||
const getEnv = (key: string): string | undefined => {
|
||||
if (global.window?.env) {
|
||||
return global.window.env[key];
|
||||
}
|
||||
if (global.process?.env) {
|
||||
return global.process.env[key];
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
const DEBUG_MODE: boolean = getEnv("DEBUG_MODE") == "true";
|
||||
const START_ROOM_URL: string = getEnv("START_ROOM_URL") || "/_/global/maps.workadventure.localhost/Floor1/floor1.json";
|
||||
const PUSHER_URL = getEnv("PUSHER_URL") || "//pusher.workadventure.localhost";
|
||||
export const ADMIN_URL = getEnv("ADMIN_URL") || "//workadventu.re";
|
||||
const UPLOADER_URL = getEnv("UPLOADER_URL") || "//uploader.workadventure.localhost";
|
||||
const ICON_URL = getEnv("ICON_URL") || "//icon.workadventure.localhost";
|
||||
const STUN_SERVER: string = getEnv("STUN_SERVER") || "stun:stun.l.google.com:19302";
|
||||
const TURN_SERVER: string = getEnv("TURN_SERVER") || "";
|
||||
const SKIP_RENDER_OPTIMIZATIONS: boolean = getEnv("SKIP_RENDER_OPTIMIZATIONS") == "true";
|
||||
const DISABLE_NOTIFICATIONS: boolean = getEnv("DISABLE_NOTIFICATIONS") == "true";
|
||||
const TURN_USER: string = getEnv("TURN_USER") || "";
|
||||
const TURN_PASSWORD: string = getEnv("TURN_PASSWORD") || "";
|
||||
const JITSI_URL: string | undefined = getEnv("JITSI_URL") === "" ? undefined : getEnv("JITSI_URL");
|
||||
const JITSI_PRIVATE_MODE: boolean = getEnv("JITSI_PRIVATE_MODE") == "true";
|
||||
const POSITION_DELAY = 200; // Wait 200ms between sending position events
|
||||
const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player
|
||||
export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || "") || 8;
|
||||
export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || "4");
|
||||
export const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == "true";
|
||||
export const NODE_ENV = process.env.NODE_ENV || "development";
|
||||
export const CONTACT_URL = process.env.CONTACT_URL || undefined;
|
||||
export const PROFILE_URL = process.env.PROFILE_URL || undefined;
|
||||
export const POSTHOG_API_KEY: string = (process.env.POSTHOG_API_KEY as string) || "";
|
||||
export const POSTHOG_URL = process.env.POSTHOG_URL || undefined;
|
||||
export const DISABLE_ANONYMOUS: boolean = process.env.DISABLE_ANONYMOUS === "true";
|
||||
export const OPID_LOGIN_SCREEN_PROVIDER = process.env.OPID_LOGIN_SCREEN_PROVIDER;
|
||||
const FALLBACK_LOCALE = process.env.FALLBACK_LOCALE || undefined;
|
||||
|
||||
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;
|
||||
export const MAX_USERNAME_LENGTH = parseInt(getEnv("MAX_USERNAME_LENGTH") || "") || 8;
|
||||
export const MAX_PER_GROUP = parseInt(getEnv("MAX_PER_GROUP") || "4");
|
||||
export const DISPLAY_TERMS_OF_USE = getEnv("DISPLAY_TERMS_OF_USE") == "true";
|
||||
export const NODE_ENV = getEnv("NODE_ENV") || "development";
|
||||
export const CONTACT_URL = getEnv("CONTACT_URL") || undefined;
|
||||
export const PROFILE_URL = getEnv("PROFILE_URL") || undefined;
|
||||
export const POSTHOG_API_KEY: string = (getEnv("POSTHOG_API_KEY") as string) || "";
|
||||
export const POSTHOG_URL = getEnv("POSTHOG_URL") || undefined;
|
||||
export const DISABLE_ANONYMOUS: boolean = getEnv("DISABLE_ANONYMOUS") === "true";
|
||||
export const OPID_LOGIN_SCREEN_PROVIDER = getEnv("OPID_LOGIN_SCREEN_PROVIDER");
|
||||
const FALLBACK_LOCALE = getEnv("FALLBACK_LOCALE") || undefined;
|
||||
|
||||
export {
|
||||
DEBUG_MODE,
|
||||
|
||||
@@ -63,8 +63,6 @@ export class SoundMeter {
|
||||
//this.slow = 0.95 * that.slow + 0.05 * that.instant;
|
||||
//this.clip = clipcount / input.length;
|
||||
|
||||
//console.log('instant', this.instant, 'clip', this.clip);
|
||||
|
||||
return this.instant;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ export enum GameMapProperties {
|
||||
OPEN_WEBSITE = "openWebsite",
|
||||
OPEN_WEBSITE_ALLOW_API = "openWebsiteAllowApi",
|
||||
OPEN_WEBSITE_POLICY = "openWebsitePolicy",
|
||||
OPEN_WEBSITE_WIDTH = "openWebsiteWidth",
|
||||
OPEN_WEBSITE_POSITION = "openWebsitePosition",
|
||||
OPEN_WEBSITE_TRIGGER = "openWebsiteTrigger",
|
||||
OPEN_WEBSITE_TRIGGER_MESSAGE = "openWebsiteTriggerMessage",
|
||||
|
||||
@@ -2,33 +2,29 @@ import type { GameScene } from "./GameScene";
|
||||
import type { GameMap } from "./GameMap";
|
||||
import { scriptUtils } from "../../Api/ScriptUtils";
|
||||
import type { CoWebsite } from "../../WebRtc/CoWebsiteManager";
|
||||
import { coWebsiteManager, CoWebsiteState } from "../../WebRtc/CoWebsiteManager";
|
||||
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
|
||||
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { get } from "svelte/store";
|
||||
import { ON_ACTION_TRIGGER_BUTTON, ON_ACTION_TRIGGER_DISABLE } from "../../WebRtc/LayoutManager";
|
||||
import { ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
|
||||
import type { ITiledMapLayer } from "../Map/ITiledMap";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
import { iframeListener } from "../../Api/IframeListener";
|
||||
import type { Subscription } from "rxjs";
|
||||
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import { LL } from "../../i18n/i18n-svelte";
|
||||
|
||||
enum OpenCoWebsiteState {
|
||||
LOADING,
|
||||
ASLEEP,
|
||||
OPENED,
|
||||
MUST_BE_CLOSE,
|
||||
TRIGGER,
|
||||
}
|
||||
|
||||
interface OpenCoWebsite {
|
||||
coWebsite: CoWebsite | undefined;
|
||||
coWebsite: CoWebsite;
|
||||
state: OpenCoWebsiteState;
|
||||
}
|
||||
|
||||
export class GameMapPropertiesListener {
|
||||
private coWebsitesOpenByLayer = new Map<ITiledMapLayer, OpenCoWebsite>();
|
||||
private coWebsitesIframeListeners = new Map<ITiledMapLayer, Subscription>();
|
||||
private coWebsitesActionTriggerByLayer = new Map<ITiledMapLayer, string>();
|
||||
|
||||
constructor(private scene: GameScene, private gameMap: GameMap) {}
|
||||
|
||||
@@ -69,10 +65,8 @@ export class GameMapPropertiesListener {
|
||||
let openWebsiteProperty: string | undefined;
|
||||
let allowApiProperty: boolean | undefined;
|
||||
let websitePolicyProperty: string | undefined;
|
||||
let websiteWidthProperty: number | undefined;
|
||||
let websitePositionProperty: number | undefined;
|
||||
let websiteTriggerProperty: string | undefined;
|
||||
let websiteTriggerMessageProperty: string | undefined;
|
||||
|
||||
layer.properties.forEach((property) => {
|
||||
switch (property.name) {
|
||||
@@ -85,18 +79,12 @@ export class GameMapPropertiesListener {
|
||||
case GameMapProperties.OPEN_WEBSITE_POLICY:
|
||||
websitePolicyProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_WIDTH:
|
||||
websiteWidthProperty = property.value as number | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_POSITION:
|
||||
websitePositionProperty = property.value as number | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER:
|
||||
websiteTriggerProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE:
|
||||
websiteTriggerMessageProperty = property.value as string | undefined;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -110,27 +98,30 @@ export class GameMapPropertiesListener {
|
||||
return;
|
||||
}
|
||||
|
||||
const coWebsite = coWebsiteManager.addCoWebsite(
|
||||
openWebsiteProperty,
|
||||
this.scene.MapUrlFile,
|
||||
allowApiProperty,
|
||||
websitePolicyProperty,
|
||||
websitePositionProperty,
|
||||
false
|
||||
);
|
||||
|
||||
this.coWebsitesOpenByLayer.set(layer, {
|
||||
coWebsite: undefined,
|
||||
state: OpenCoWebsiteState.LOADING,
|
||||
coWebsite: coWebsite,
|
||||
state: OpenCoWebsiteState.ASLEEP,
|
||||
});
|
||||
|
||||
const openWebsiteFunction = () => {
|
||||
coWebsiteManager
|
||||
.loadCoWebsite(
|
||||
openWebsiteProperty as string,
|
||||
this.scene.MapUrlFile,
|
||||
allowApiProperty,
|
||||
websitePolicyProperty,
|
||||
websiteWidthProperty,
|
||||
websitePositionProperty
|
||||
)
|
||||
.loadCoWebsite(coWebsite)
|
||||
.then((coWebsite) => {
|
||||
const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer);
|
||||
if (coWebsiteOpen && coWebsiteOpen.state === OpenCoWebsiteState.MUST_BE_CLOSE) {
|
||||
coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => console.error(e));
|
||||
coWebsiteManager.closeCoWebsite(coWebsite).catch(() => {
|
||||
console.error("Error during a co-website closing");
|
||||
});
|
||||
this.coWebsitesOpenByLayer.delete(layer);
|
||||
this.coWebsitesActionTriggerByLayer.delete(layer);
|
||||
} else {
|
||||
this.coWebsitesOpenByLayer.set(layer, {
|
||||
coWebsite,
|
||||
@@ -138,57 +129,17 @@ export class GameMapPropertiesListener {
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
.catch(() => {
|
||||
console.error("Error during loading a co-website: " + coWebsite.url);
|
||||
});
|
||||
|
||||
layoutManagerActionStore.removeAction(actionUuid);
|
||||
};
|
||||
|
||||
const createWebsiteTrigger = () => {
|
||||
if (!websiteTriggerMessageProperty) {
|
||||
websiteTriggerMessageProperty = get(LL).message.openWebsiteTrigger();
|
||||
}
|
||||
|
||||
this.coWebsitesOpenByLayer.set(layer, {
|
||||
coWebsite: undefined,
|
||||
state: OpenCoWebsiteState.TRIGGER,
|
||||
});
|
||||
|
||||
this.coWebsitesActionTriggerByLayer.set(layer, actionUuid);
|
||||
|
||||
layoutManagerActionStore.addAction({
|
||||
uuid: actionUuid,
|
||||
type: "message",
|
||||
message: websiteTriggerMessageProperty,
|
||||
callback: () => openWebsiteFunction(),
|
||||
userInputManager: this.scene.userInputManager,
|
||||
});
|
||||
};
|
||||
|
||||
this.coWebsitesIframeListeners.set(
|
||||
layer,
|
||||
iframeListener.unregisterIFrameStream.subscribe(() => {
|
||||
const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer);
|
||||
if (
|
||||
coWebsiteOpen?.coWebsite?.state == CoWebsiteState.CLOSED &&
|
||||
(!websiteTriggerProperty || websiteTriggerProperty !== ON_ACTION_TRIGGER_DISABLE)
|
||||
) {
|
||||
createWebsiteTrigger();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const forceTrigger = localUserStore.getForceCowebsiteTrigger();
|
||||
if (
|
||||
forceTrigger ||
|
||||
(websiteTriggerProperty && websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON)
|
||||
!localUserStore.getForceCowebsiteTrigger() &&
|
||||
websiteTriggerProperty !== ON_ACTION_TRIGGER_BUTTON
|
||||
) {
|
||||
createWebsiteTrigger();
|
||||
} else {
|
||||
this.coWebsitesOpenByLayer.set(layer, {
|
||||
coWebsite: undefined,
|
||||
state: OpenCoWebsiteState.LOADING,
|
||||
});
|
||||
|
||||
openWebsiteFunction();
|
||||
}
|
||||
});
|
||||
@@ -223,29 +174,14 @@ export class GameMapPropertiesListener {
|
||||
return;
|
||||
}
|
||||
|
||||
const coWebsiteIframeListener = this.coWebsitesIframeListeners.get(layer);
|
||||
|
||||
if (coWebsiteIframeListener) {
|
||||
coWebsiteIframeListener.unsubscribe();
|
||||
this.coWebsitesIframeListeners.delete(layer);
|
||||
}
|
||||
|
||||
const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer);
|
||||
|
||||
if (!coWebsiteOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (coWebsiteOpen.state === OpenCoWebsiteState.LOADING) {
|
||||
if (coWebsiteOpen.state === OpenCoWebsiteState.ASLEEP) {
|
||||
coWebsiteOpen.state = OpenCoWebsiteState.MUST_BE_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
coWebsiteOpen.state !== OpenCoWebsiteState.OPENED &&
|
||||
coWebsiteOpen.state !== OpenCoWebsiteState.TRIGGER
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (coWebsiteOpen.coWebsite !== undefined) {
|
||||
@@ -253,22 +189,6 @@ export class GameMapPropertiesListener {
|
||||
}
|
||||
|
||||
this.coWebsitesOpenByLayer.delete(layer);
|
||||
|
||||
const actionStore = get(layoutManagerActionStore);
|
||||
const actionTriggerUuid = this.coWebsitesActionTriggerByLayer.get(layer);
|
||||
|
||||
if (!actionTriggerUuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const action =
|
||||
actionStore && actionStore.length > 0
|
||||
? actionStore.find((action) => action.uuid === actionTriggerUuid)
|
||||
: undefined;
|
||||
|
||||
if (action) {
|
||||
layoutManagerActionStore.removeAction(actionTriggerUuid);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
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-report-message', '/resources/objects/report-message.mp3');
|
||||
this.load.audio("audio-report-message", "/resources/objects/report-message.mp3");
|
||||
this.sound.pauseOnBlur = false;
|
||||
|
||||
this.load.on(FILE_LOAD_ERROR, (file: { src: string }) => {
|
||||
@@ -650,13 +650,9 @@ export class GameScene extends DirtyScene {
|
||||
this.peerStoreUnsubscribe = peerStore.subscribe((peers) => {
|
||||
const newPeerNumber = peers.size;
|
||||
if (newPeerNumber > oldPeerNumber) {
|
||||
this.sound.play("audio-webrtc-in", {
|
||||
volume: 0.2,
|
||||
});
|
||||
this.playSound("audio-webrtc-in");
|
||||
} else if (newPeerNumber < oldPeerNumber) {
|
||||
this.sound.play("audio-webrtc-out", {
|
||||
volume: 0.2,
|
||||
});
|
||||
this.playSound("audio-webrtc-out");
|
||||
}
|
||||
oldPeerNumber = newPeerNumber;
|
||||
});
|
||||
@@ -1296,21 +1292,21 @@ export class GameScene extends DirtyScene {
|
||||
throw new Error("Unknown query source");
|
||||
}
|
||||
|
||||
const coWebsite = await coWebsiteManager.loadCoWebsite(
|
||||
const coWebsite = coWebsiteManager.addCoWebsite(
|
||||
openCoWebsite.url,
|
||||
iframeListener.getBaseUrlFromSource(source),
|
||||
openCoWebsite.allowApi,
|
||||
openCoWebsite.allowPolicy,
|
||||
openCoWebsite.position
|
||||
openCoWebsite.position,
|
||||
openCoWebsite.closable ?? true
|
||||
);
|
||||
|
||||
if (!coWebsite) {
|
||||
throw new Error("Error on opening co-website");
|
||||
if (openCoWebsite.lazy !== undefined && !openCoWebsite.lazy) {
|
||||
await coWebsiteManager.loadCoWebsite(coWebsite);
|
||||
}
|
||||
|
||||
return {
|
||||
id: coWebsite.iframe.id,
|
||||
position: coWebsite.position,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1320,7 +1316,6 @@ export class GameScene extends DirtyScene {
|
||||
return coWebsites.map((coWebsite: CoWebsite) => {
|
||||
return {
|
||||
id: coWebsite.iframe.id,
|
||||
position: coWebsite.position,
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -1610,6 +1605,12 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
}
|
||||
|
||||
public playSound(sound: string) {
|
||||
this.sound.play(sound, {
|
||||
volume: 0.2,
|
||||
});
|
||||
}
|
||||
|
||||
public cleanupClosingScene(): void {
|
||||
// stop playing audio, close any open website, stop any open Jitsi
|
||||
coWebsiteManager.closeCoWebsites().catch((e) => console.error(e));
|
||||
@@ -1647,7 +1648,7 @@ export class GameScene extends DirtyScene {
|
||||
this.sharedVariablesManager?.close();
|
||||
this.embeddedWebsiteManager?.close();
|
||||
|
||||
mediaManager.hideGameOverlay();
|
||||
mediaManager.hideMyCamera();
|
||||
|
||||
for (const iframeEvents of this.iframeSubscriptionList) {
|
||||
iframeEvents.unsubscribe();
|
||||
@@ -2156,6 +2157,17 @@ export class GameScene extends DirtyScene {
|
||||
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 {
|
||||
const allProps = this.gameMap.getCurrentProperties();
|
||||
const jitsiConfig = this.safeParseJSONstring(
|
||||
@@ -2167,71 +2179,26 @@ export class GameScene extends DirtyScene {
|
||||
GameMapProperties.JITSI_INTERFACE_CONFIG
|
||||
);
|
||||
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
|
||||
const jitsiWidth = allProps.get(GameMapProperties.JITSI_WIDTH) as number | undefined;
|
||||
|
||||
const jitsiKeepCircle = allProps.get("jitsiKeepCircle") as boolean | false;
|
||||
|
||||
jitsiFactory
|
||||
.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl, jitsiWidth)
|
||||
.catch((e) => console.error(e));
|
||||
this.connection?.setSilent(true);
|
||||
mediaManager.hideGameOverlay();
|
||||
|
||||
if (jitsiKeepCircle) {
|
||||
const silent = this.gameMap.getCurrentProperties().get("silent");
|
||||
this.connection?.setSilent(!!silent);
|
||||
mediaManager.showGameOverlay();
|
||||
}
|
||||
|
||||
analyticsClient.enteredJitsi(roomName, this.room.id);
|
||||
|
||||
//permit to stop jitsi when user close iframe
|
||||
mediaManager.addTriggerCloseJitsiFrameButton("close-jitsi", () => {
|
||||
this.stopJitsi();
|
||||
jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl).catch(() => {
|
||||
console.error("Cannot start a Jitsi co-website");
|
||||
});
|
||||
|
||||
if (allProps.get("jitsiKeepCircle") as boolean | false) {
|
||||
this.enableMediaBehaviors();
|
||||
} else {
|
||||
this.disableMediaBehaviors();
|
||||
}
|
||||
analyticsClient.enteredJitsi(roomName, this.room.id);
|
||||
}
|
||||
|
||||
public stopJitsi(): void {
|
||||
const silent = this.gameMap.getCurrentProperties().get(GameMapProperties.SILENT);
|
||||
this.connection?.setSilent(!!silent);
|
||||
jitsiFactory.stop();
|
||||
mediaManager.showGameOverlay();
|
||||
|
||||
const allProps = this.gameMap.getCurrentProperties();
|
||||
|
||||
if (allProps.get("jitsiRoom") === undefined) {
|
||||
layoutManagerActionStore.removeAction("jitsi");
|
||||
} else {
|
||||
const openJitsiRoomFunction = () => {
|
||||
const roomName = jitsiFactory.getRoomName(
|
||||
allProps.get(GameMapProperties.JITSI_ROOM) as string,
|
||||
this.instance
|
||||
);
|
||||
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
|
||||
if (JITSI_PRIVATE_MODE && !jitsiUrl) {
|
||||
const adminTag = allProps.get(GameMapProperties.JITSI_ADMIN_ROOM_TAG) as string | undefined;
|
||||
|
||||
this.connection && this.connection.emitQueryJitsiJwtMessage(roomName, adminTag);
|
||||
} else {
|
||||
this.startJitsi(roomName, undefined);
|
||||
}
|
||||
layoutManagerActionStore.removeAction("jitsi");
|
||||
};
|
||||
|
||||
let message = allProps.get(GameMapProperties.JITSI_TRIGGER_MESSAGE);
|
||||
if (message === undefined) {
|
||||
message = "Press SPACE or touch here to enter Jitsi Meet room";
|
||||
}
|
||||
layoutManagerActionStore.addAction({
|
||||
uuid: "jitsi",
|
||||
type: "message",
|
||||
message: message,
|
||||
callback: () => openJitsiRoomFunction(),
|
||||
userInputManager: this.userInputManager,
|
||||
const coWebsite = coWebsiteManager.searchJitsi();
|
||||
if (coWebsite) {
|
||||
coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => {
|
||||
console.error("Error during Jitsi co-website closing", e);
|
||||
});
|
||||
}
|
||||
|
||||
mediaManager.removeTriggerCloseJitsiFrameButton("close-jitsi");
|
||||
}
|
||||
|
||||
//todo: put this into an 'orchestrator' scene (EntryScene?)
|
||||
|
||||
@@ -11,10 +11,10 @@ import { areCharacterLayersValid } from "../../Connexion/LocalUser";
|
||||
import { SelectCharacterSceneName } from "./SelectCharacterScene";
|
||||
import { activeRowStore, customCharacterSceneVisibleStore } from "../../Stores/CustomCharacterStore";
|
||||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
import { CustomizedCharacter } from "../Entity/CustomizedCharacter";
|
||||
import { get } from "svelte/store";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
|
||||
|
||||
export const CustomizeSceneName = "CustomizeScene";
|
||||
|
||||
@@ -67,12 +67,12 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
customCharacterSceneVisibleStore.set(true);
|
||||
this.events.addListener("wake", () => {
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMobile() ? 3 : 1;
|
||||
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
|
||||
customCharacterSceneVisibleStore.set(true);
|
||||
});
|
||||
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMobile() ? 3 : 1;
|
||||
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
|
||||
|
||||
this.Rectangle = this.add.rectangle(
|
||||
this.cameras.main.worldView.x + this.cameras.main.width / 2,
|
||||
|
||||
@@ -12,8 +12,8 @@ import { touchScreenManager } from "../../Touch/TouchScreenManager";
|
||||
import { PinchManager } from "../UserInput/PinchManager";
|
||||
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
|
||||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
|
||||
|
||||
//todo: put this constants in a dedicated file
|
||||
export const SelectCharacterSceneName = "SelectCharacterScene";
|
||||
@@ -60,7 +60,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
||||
selectCharacterSceneVisibleStore.set(true);
|
||||
this.events.addListener("wake", () => {
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMobile() ? 2 : 1;
|
||||
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
|
||||
selectCharacterSceneVisibleStore.set(true);
|
||||
});
|
||||
|
||||
@@ -69,7 +69,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
||||
}
|
||||
|
||||
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;
|
||||
this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xffffff);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { touchScreenManager } from "../../Touch/TouchScreenManager";
|
||||
import { PinchManager } from "../UserInput/PinchManager";
|
||||
import { selectCompanionSceneVisibleStore } from "../../Stores/SelectCompanionStore";
|
||||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
|
||||
|
||||
export const SelectCompanionSceneName = "SelectCompanionScene";
|
||||
|
||||
@@ -44,7 +44,7 @@ export class SelectCompanionScene extends ResizableScene {
|
||||
selectCompanionSceneVisibleStore.set(true);
|
||||
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMobile() ? 2 : 1;
|
||||
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
|
||||
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
new PinchManager(this);
|
||||
|
||||
@@ -37,19 +37,17 @@ export class WaScaleManager {
|
||||
height: height * devicePixelRatio,
|
||||
});
|
||||
|
||||
if (gameSize.width == 0) {
|
||||
return;
|
||||
if (realSize.width !== 0 && gameSize.width !== 0 && devicePixelRatio !== 0) {
|
||||
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
|
||||
}
|
||||
|
||||
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
|
||||
|
||||
this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio);
|
||||
this.scaleManager.setZoom(this.actualZoom);
|
||||
this.scaleManager.resize(gameSize.width, gameSize.height);
|
||||
|
||||
// Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves
|
||||
const style = this.scaleManager.canvas.style;
|
||||
style.width = Math.ceil(realSize.width / devicePixelRatio) + "px";
|
||||
style.height = Math.ceil(realSize.height / devicePixelRatio) + "px";
|
||||
style.width = Math.ceil(realSize.width !== 0 ? realSize.width / devicePixelRatio : 0) + "px";
|
||||
style.height = Math.ceil(realSize.height !== 0 ? realSize.height / devicePixelRatio : 0) + "px";
|
||||
|
||||
// Resize the game element at the same size at the canvas
|
||||
const gameStyle = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("game").style;
|
||||
|
||||
@@ -2,14 +2,14 @@ import { get, writable } from "svelte/store";
|
||||
import type { Box } from "../WebRtc/LayoutManager";
|
||||
import { HtmlUtils } from "../WebRtc/HtmlUtils";
|
||||
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)
|
||||
*/
|
||||
function findBiggestAvailableArea(): Box {
|
||||
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 htmlChildren = Array.from(children.values());
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { derived, get, writable } from "svelte/store";
|
||||
import type { CoWebsite } from "../WebRtc/CoWebsiteManager";
|
||||
|
||||
function createCoWebsiteStore() {
|
||||
const { subscribe, set, update } = writable(Array<CoWebsite>());
|
||||
|
||||
set(Array<CoWebsite>());
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
add: (coWebsite: CoWebsite, position?: number) => {
|
||||
coWebsite.state.subscribe((value) => {
|
||||
update((currentArray) => currentArray);
|
||||
});
|
||||
|
||||
if (position || position === 0) {
|
||||
update((currentArray) => {
|
||||
if (position === 0) {
|
||||
return [coWebsite, ...currentArray];
|
||||
} else if (currentArray.length > position) {
|
||||
const test = [...currentArray.splice(position, 0, coWebsite)];
|
||||
return [...currentArray.splice(position, 0, coWebsite)];
|
||||
}
|
||||
|
||||
return [...currentArray, coWebsite];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
update((currentArray) => [...currentArray, coWebsite]);
|
||||
},
|
||||
remove: (coWebsite: CoWebsite) => {
|
||||
update((currentArray) => [
|
||||
...currentArray.filter((currentCoWebsite) => currentCoWebsite.iframe.id !== coWebsite.iframe.id),
|
||||
]);
|
||||
},
|
||||
empty: () => {
|
||||
set(Array<CoWebsite>());
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const coWebsites = createCoWebsiteStore();
|
||||
|
||||
export const coWebsitesNotAsleep = derived([coWebsites], ([$coWebsites]) =>
|
||||
$coWebsites.filter((coWebsite) => get(coWebsite.state) !== "asleep")
|
||||
);
|
||||
|
||||
export const mainCoWebsite = derived([coWebsites], ([$coWebsites]) =>
|
||||
$coWebsites.find((coWebsite) => get(coWebsite.state) !== "asleep")
|
||||
);
|
||||
@@ -0,0 +1,51 @@
|
||||
import { derived, get, writable } from "svelte/store";
|
||||
import type { CoWebsite } from "../WebRtc/CoWebsiteManager";
|
||||
import { LayoutMode } from "../WebRtc/LayoutManager";
|
||||
import { coWebsites } from "./CoWebsiteStore";
|
||||
import { Streamable, streamableCollectionStore } from "./StreamableCollectionStore";
|
||||
|
||||
export type EmbedScreen =
|
||||
| {
|
||||
type: "streamable";
|
||||
embed: Streamable;
|
||||
}
|
||||
| {
|
||||
type: "cowebsite";
|
||||
embed: CoWebsite;
|
||||
};
|
||||
|
||||
function createHighlightedEmbedScreenStore() {
|
||||
const { subscribe, set, update } = writable<EmbedScreen | null>(null);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
highlight: (embedScreen: EmbedScreen) => {
|
||||
set(embedScreen);
|
||||
},
|
||||
removeHighlight: () => {
|
||||
set(null);
|
||||
},
|
||||
toggleHighlight: (embedScreen: EmbedScreen) => {
|
||||
update((currentEmbedScreen) =>
|
||||
!currentEmbedScreen ||
|
||||
embedScreen.type !== currentEmbedScreen.type ||
|
||||
(embedScreen.type === "cowebsite" &&
|
||||
currentEmbedScreen.type === "cowebsite" &&
|
||||
embedScreen.embed.iframe.id !== currentEmbedScreen.embed.iframe.id) ||
|
||||
(embedScreen.type === "streamable" &&
|
||||
currentEmbedScreen.type === "streamable" &&
|
||||
embedScreen.embed.uniqueId !== currentEmbedScreen.embed.uniqueId)
|
||||
? embedScreen
|
||||
: null
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const highlightedEmbedScreen = createHighlightedEmbedScreenStore();
|
||||
export const embedScreenLayout = writable<LayoutMode>(LayoutMode.Presentation);
|
||||
|
||||
export const hasEmbedScreen = derived(
|
||||
[streamableCollectionStore],
|
||||
($values) => get(streamableCollectionStore).size + get(coWebsites).length > 0
|
||||
);
|
||||
@@ -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();
|
||||
@@ -51,6 +51,6 @@ function createLayoutManagerAction() {
|
||||
|
||||
export const layoutManagerActionStore = createLayoutManagerAction();
|
||||
|
||||
export const layoutManagerVisibilityStore = derived(layoutManagerActionStore, ($layoutManagerActionStore) => {
|
||||
export const layoutManagerActionVisibilityStore = derived(layoutManagerActionStore, ($layoutManagerActionStore) => {
|
||||
return !!$layoutManagerActionStore.length;
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import { BrowserTooOldError } from "./Errors/BrowserTooOldError";
|
||||
import { errorStore } from "./ErrorStore";
|
||||
import { getNavigatorType, isIOS, NavigatorType } from "../WebRtc/DeviceUtils";
|
||||
import { WebviewOnOldIOS } from "./Errors/WebviewOnOldIOS";
|
||||
import { gameOverlayVisibilityStore } from "./GameOverlayStoreVisibility";
|
||||
import { myCameraVisibilityStore } from "./MyCameraStoreVisibility";
|
||||
import { peerStore } from "./PeerStore";
|
||||
import { privacyShutdownStore } from "./PrivacyShutdownStore";
|
||||
import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError";
|
||||
@@ -233,7 +233,7 @@ export const mediaStreamConstraintsStore = derived(
|
||||
[
|
||||
requestedCameraState,
|
||||
requestedMicrophoneState,
|
||||
gameOverlayVisibilityStore,
|
||||
myCameraVisibilityStore,
|
||||
enableCameraSceneVisibilityStore,
|
||||
videoConstraintStore,
|
||||
audioConstraintStore,
|
||||
@@ -245,7 +245,7 @@ export const mediaStreamConstraintsStore = derived(
|
||||
[
|
||||
$requestedCameraState,
|
||||
$requestedMicrophoneState,
|
||||
$gameOverlayVisibilityStore,
|
||||
$myCameraVisibilityStore,
|
||||
$enableCameraSceneVisibilityStore,
|
||||
$videoConstraintStore,
|
||||
$audioConstraintStore,
|
||||
@@ -283,7 +283,7 @@ export const mediaStreamConstraintsStore = derived(
|
||||
}
|
||||
|
||||
// Disable webcam and microphone when in a Jitsi
|
||||
if ($gameOverlayVisibilityStore === false) {
|
||||
if ($myCameraVisibilityStore === false) {
|
||||
currentVideoConstraint = false;
|
||||
currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { derived, Readable, readable, writable } from "svelte/store";
|
||||
import { peerStore } from "./PeerStore";
|
||||
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
|
||||
|
||||
@@ -41,8 +41,8 @@ let previousComputedAudioConstraint: boolean | MediaTrackConstraints = false;
|
||||
* A store containing the media constraints we want to apply.
|
||||
*/
|
||||
export const screenSharingConstraintsStore = derived(
|
||||
[requestedScreenSharingState, gameOverlayVisibilityStore, peerStore],
|
||||
([$requestedScreenSharingState, $gameOverlayVisibilityStore, $peerStore], set) => {
|
||||
[requestedScreenSharingState, myCameraVisibilityStore, peerStore],
|
||||
([$requestedScreenSharingState, $myCameraVisibilityStore, $peerStore], set) => {
|
||||
let currentVideoConstraint: boolean | MediaTrackConstraints = true;
|
||||
let currentAudioConstraint: boolean | MediaTrackConstraints = false;
|
||||
|
||||
@@ -53,7 +53,7 @@ export const screenSharingConstraintsStore = derived(
|
||||
}
|
||||
|
||||
// Disable screen sharing when in a Jitsi
|
||||
if (!$gameOverlayVisibilityStore) {
|
||||
if (!$myCameraVisibilityStore) {
|
||||
currentVideoConstraint = false;
|
||||
currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
@@ -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 { peerStore, screenSharingStreamStore } from "./PeerStore";
|
||||
import type { RemotePeer } from "../WebRtc/SimplePeer";
|
||||
import { LayoutMode } from "../WebRtc/LayoutManager";
|
||||
|
||||
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)
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
type InternalBreakpoint = {
|
||||
beforeBreakpoint: InternalBreakpoint | undefined;
|
||||
nextBreakpoint: InternalBreakpoint | undefined;
|
||||
pixels: number;
|
||||
};
|
||||
|
||||
function generateBreakpointsMap(): Map<string, InternalBreakpoint> {
|
||||
// If is changed don't forget to also change it on SASS.
|
||||
const breakpoints: { [key: string]: number } = {
|
||||
xs: 0,
|
||||
sm: 576,
|
||||
md: 768,
|
||||
lg: 992,
|
||||
xl: 1200,
|
||||
xxl: 1400,
|
||||
};
|
||||
|
||||
let beforeBreakpoint: InternalBreakpoint | undefined;
|
||||
let beforeBreakpointTag: string | undefined;
|
||||
const mapRender = new Map<string, InternalBreakpoint>();
|
||||
|
||||
for (const breakpoint in breakpoints) {
|
||||
const newBreakpoint = {
|
||||
beforeBreakpoint: beforeBreakpoint,
|
||||
nextBreakpoint: undefined,
|
||||
pixels: breakpoints[breakpoint],
|
||||
};
|
||||
|
||||
if (beforeBreakpointTag && beforeBreakpoint) {
|
||||
beforeBreakpoint.nextBreakpoint = newBreakpoint;
|
||||
mapRender.set(beforeBreakpointTag, beforeBreakpoint);
|
||||
}
|
||||
|
||||
mapRender.set(breakpoint, {
|
||||
beforeBreakpoint: beforeBreakpoint,
|
||||
nextBreakpoint: undefined,
|
||||
pixels: breakpoints[breakpoint],
|
||||
});
|
||||
|
||||
beforeBreakpointTag = breakpoint;
|
||||
beforeBreakpoint = newBreakpoint;
|
||||
}
|
||||
|
||||
return mapRender;
|
||||
}
|
||||
|
||||
const breakpoints = generateBreakpointsMap();
|
||||
|
||||
export type Breakpoint = "xs" | "sm" | "md" | "lg" | "xl" | "xxl";
|
||||
|
||||
export function isMediaBreakpointUp(breakpoint: Breakpoint): boolean {
|
||||
if (breakpoint === "xxl") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const breakpointObject = breakpoints.get(breakpoint);
|
||||
|
||||
if (!breakpointObject) {
|
||||
throw new Error(`Unknown breakpoint: ${breakpoint}`);
|
||||
}
|
||||
|
||||
if (!breakpointObject.nextBreakpoint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return breakpointObject.nextBreakpoint.pixels - 1 >= window.innerWidth;
|
||||
}
|
||||
|
||||
export function isMediaBreakpointDown(breakpoint: Breakpoint): boolean {
|
||||
if (breakpoint === "xs") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const breakpointObject = breakpoints.get(breakpoint);
|
||||
|
||||
if (!breakpointObject) {
|
||||
throw new Error(`Unknown breakpoint: ${breakpoint}`);
|
||||
}
|
||||
|
||||
return breakpointObject.pixels <= window.innerWidth;
|
||||
}
|
||||
|
||||
export function isMediaBreakpointOnly(breakpoint: Breakpoint): boolean {
|
||||
const breakpointObject = breakpoints.get(breakpoint);
|
||||
|
||||
if (!breakpointObject) {
|
||||
throw new Error(`Unknown breakpoint: ${breakpoint}`);
|
||||
}
|
||||
|
||||
return (
|
||||
breakpointObject.pixels <= window.innerWidth &&
|
||||
(!breakpointObject.nextBreakpoint || breakpointObject.nextBreakpoint.pixels - 1 >= window.innerWidth)
|
||||
);
|
||||
}
|
||||
|
||||
export function isMediaBreakpointBetween(startBreakpoint: Breakpoint, endBreakpoint: Breakpoint): boolean {
|
||||
const startBreakpointObject = breakpoints.get(startBreakpoint);
|
||||
const endBreakpointObject = breakpoints.get(endBreakpoint);
|
||||
|
||||
if (!startBreakpointObject) {
|
||||
throw new Error(`Unknown start breakpoint: ${startBreakpointObject}`);
|
||||
}
|
||||
|
||||
if (!endBreakpointObject) {
|
||||
throw new Error(`Unknown end breakpoint: ${endBreakpointObject}`);
|
||||
}
|
||||
|
||||
return (
|
||||
startBreakpointObject.pixels <= innerWidth &&
|
||||
(!endBreakpointObject.nextBreakpoint || endBreakpointObject.nextBreakpoint.pixels - 1 >= window.innerWidth)
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,11 @@ export class HtmlUtils {
|
||||
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 {
|
||||
const elem = document.querySelector<T>(selector);
|
||||
if (HtmlUtils.isHtmlElement<T>(elem)) {
|
||||
|
||||
@@ -7,6 +7,7 @@ interface jitsiConfigInterface {
|
||||
startWithAudioMuted: boolean;
|
||||
startWithVideoMuted: boolean;
|
||||
prejoinPageEnabled: boolean;
|
||||
disableDeepLinking: boolean;
|
||||
}
|
||||
|
||||
interface JitsiOptions {
|
||||
@@ -40,6 +41,7 @@ const getDefaultConfig = (): jitsiConfigInterface => {
|
||||
startWithAudioMuted: !get(requestedMicrophoneState),
|
||||
startWithVideoMuted: !get(requestedCameraState),
|
||||
prejoinPageEnabled: false,
|
||||
disableDeepLinking: false,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -135,64 +137,112 @@ class JitsiFactory {
|
||||
return slugify(instance.replace("/", "-") + "-" + roomName);
|
||||
}
|
||||
|
||||
public start(
|
||||
public async start(
|
||||
roomName: string,
|
||||
playerName: string,
|
||||
jwt?: string,
|
||||
config?: object,
|
||||
interfaceConfig?: object,
|
||||
jitsiUrl?: string,
|
||||
jitsiWidth?: number
|
||||
): Promise<CoWebsite> {
|
||||
return coWebsiteManager.addCoWebsite(
|
||||
async (cowebsiteDiv) => {
|
||||
// Jitsi meet external API maintains some data in local storage
|
||||
// which is sent via the appData URL parameter when joining a
|
||||
// conference. Problem is that this data grows indefinitely. Thus
|
||||
// after some time the URLs get so huge that loading the iframe
|
||||
// becomes slow and eventually breaks completely. Thus lets just
|
||||
// clear jitsi local storage before starting a new conference.
|
||||
window.localStorage.removeItem("jitsiLocalStorage");
|
||||
jitsiUrl?: string
|
||||
) {
|
||||
const coWebsite = coWebsiteManager.searchJitsi();
|
||||
|
||||
const domain = jitsiUrl || JITSI_URL;
|
||||
if (domain === undefined) {
|
||||
throw new Error("Missing JITSI_URL environment variable or jitsiUrl parameter in the map.");
|
||||
}
|
||||
await this.loadJitsiScript(domain);
|
||||
if (coWebsite) {
|
||||
await coWebsiteManager.closeCoWebsite(coWebsite);
|
||||
}
|
||||
|
||||
const options: JitsiOptions = {
|
||||
roomName: roomName,
|
||||
jwt: jwt,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
parentNode: cowebsiteDiv,
|
||||
configOverwrite: mergeConfig(config),
|
||||
interfaceConfigOverwrite: { ...defaultInterfaceConfig, ...interfaceConfig },
|
||||
};
|
||||
if (!options.jwt) {
|
||||
delete options.jwt;
|
||||
}
|
||||
// Jitsi meet external API maintains some data in local storage
|
||||
// which is sent via the appData URL parameter when joining a
|
||||
// conference. Problem is that this data grows indefinitely. Thus
|
||||
// after some time the URLs get so huge that loading the iframe
|
||||
// becomes slow and eventually breaks completely. Thus lets just
|
||||
// clear jitsi local storage before starting a new conference.
|
||||
window.localStorage.removeItem("jitsiLocalStorage");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const doResolve = (): void => {
|
||||
const iframe = cowebsiteDiv.querySelector<HTMLIFrameElement>('[id*="jitsi" i]');
|
||||
if (iframe === null) {
|
||||
throw new Error("Could not find Jitsi Iframe");
|
||||
}
|
||||
resolve(iframe);
|
||||
};
|
||||
options.onload = () => doResolve(); //we want for the iframe to be loaded before triggering animations.
|
||||
setTimeout(() => doResolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load
|
||||
this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options);
|
||||
this.jitsiApi.executeCommand("displayName", playerName);
|
||||
const domain = jitsiUrl || JITSI_URL;
|
||||
if (domain === undefined) {
|
||||
throw new Error("Missing JITSI_URL environment variable or jitsiUrl parameter in the map.");
|
||||
}
|
||||
await this.loadJitsiScript(domain);
|
||||
|
||||
this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback);
|
||||
this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback);
|
||||
const options: JitsiOptions = {
|
||||
roomName: roomName,
|
||||
jwt: jwt,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
parentNode: coWebsiteManager.getCoWebsiteBuffer(),
|
||||
configOverwrite: mergeConfig(config),
|
||||
interfaceConfigOverwrite: { ...defaultInterfaceConfig, ...interfaceConfig },
|
||||
};
|
||||
|
||||
if (!options.jwt) {
|
||||
delete options.jwt;
|
||||
}
|
||||
|
||||
const doResolve = (): void => {
|
||||
const iframe = coWebsiteManager.getCoWebsiteBuffer().querySelector<HTMLIFrameElement>('[id*="jitsi" i]');
|
||||
if (iframe && this.jitsiApi) {
|
||||
const coWebsite = coWebsiteManager.addCoWebsiteFromIframe(iframe, false, undefined, 0, false, true);
|
||||
|
||||
this.jitsiApi.addListener("videoConferenceLeft", () => {
|
||||
this.closeOrUnload(coWebsite);
|
||||
});
|
||||
},
|
||||
jitsiWidth,
|
||||
0
|
||||
);
|
||||
|
||||
this.jitsiApi.addListener("readyToClose", () => {
|
||||
this.closeOrUnload(coWebsite);
|
||||
});
|
||||
}
|
||||
|
||||
coWebsiteManager.resizeAllIframes();
|
||||
};
|
||||
|
||||
this.jitsiApi = undefined;
|
||||
|
||||
options.onload = () => doResolve(); //we want for the iframe to be loaded before triggering animations.
|
||||
setTimeout(() => doResolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load
|
||||
this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options);
|
||||
this.jitsiApi.executeCommand("displayName", playerName);
|
||||
|
||||
this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback);
|
||||
this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback);
|
||||
}
|
||||
|
||||
private closeOrUnload = function (coWebsite: CoWebsite) {
|
||||
if (coWebsite.closable) {
|
||||
coWebsiteManager.closeCoWebsite(coWebsite).catch(() => {
|
||||
console.error("Error during closing a Jitsi Meet");
|
||||
});
|
||||
} else {
|
||||
coWebsiteManager.unloadCoWebsite(coWebsite).catch(() => {
|
||||
console.error("Error during unloading a Jitsi Meet");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public restart() {
|
||||
if (!this.jitsiApi) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback);
|
||||
this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback);
|
||||
|
||||
const coWebsite = coWebsiteManager.searchJitsi();
|
||||
console.log("jitsi api ", this.jitsiApi);
|
||||
console.log("iframe cowebsite", coWebsite?.iframe);
|
||||
|
||||
if (!coWebsite) {
|
||||
this.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
this.jitsiApi.addListener("videoConferenceLeft", () => {
|
||||
this.closeOrUnload(coWebsite);
|
||||
});
|
||||
|
||||
this.jitsiApi.addListener("readyToClose", () => {
|
||||
this.closeOrUnload(coWebsite);
|
||||
});
|
||||
}
|
||||
|
||||
public stop() {
|
||||
@@ -200,14 +250,16 @@ class JitsiFactory {
|
||||
return;
|
||||
}
|
||||
|
||||
const jitsiCoWebsite = coWebsiteManager.searchJitsi();
|
||||
|
||||
if (jitsiCoWebsite) {
|
||||
coWebsiteManager.closeJitsi().catch((e) => console.error(e));
|
||||
}
|
||||
|
||||
this.jitsiApi.removeListener("audioMuteStatusChanged", this.audioCallback);
|
||||
this.jitsiApi.removeListener("videoMuteStatusChanged", this.videoCallback);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
if (!this.jitsiApi) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.stop();
|
||||
this.jitsiApi?.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStor
|
||||
export type StartScreenSharingCallback = (media: MediaStream) => void;
|
||||
export type StopScreenSharingCallback = (media: MediaStream) => void;
|
||||
|
||||
import { cowebsiteCloseButtonId } from "./CoWebsiteManager";
|
||||
import { gameOverlayVisibilityStore } from "../Stores/GameOverlayStoreVisibility";
|
||||
import { myCameraVisibilityStore } from "../Stores/MyCameraStoreVisibility";
|
||||
import { layoutManagerActionStore } from "../Stores/LayoutManagerStore";
|
||||
import { MediaStreamConstraintsError } from "../Stores/Errors/MediaStreamConstraintsError";
|
||||
import { localUserStore } from "../Connexion/LocalUserStore";
|
||||
@@ -20,8 +19,6 @@ export class MediaManager {
|
||||
startScreenSharingCallBacks: Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
|
||||
stopScreenSharingCallBacks: Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
|
||||
|
||||
private triggerCloseJistiFrame: Map<String, Function> = new Map<String, Function>();
|
||||
|
||||
private userInputManager?: UserInputManager;
|
||||
|
||||
constructor() {
|
||||
@@ -73,36 +70,12 @@ export class MediaManager {
|
||||
});
|
||||
}
|
||||
|
||||
public showGameOverlay(): void {
|
||||
const gameOverlay = HtmlUtils.getElementByIdOrFail("game-overlay");
|
||||
gameOverlay.classList.add("active");
|
||||
|
||||
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
|
||||
const functionTrigger = () => {
|
||||
this.triggerCloseJitsiFrameButton();
|
||||
};
|
||||
buttonCloseFrame.removeEventListener("click", () => {
|
||||
buttonCloseFrame.blur();
|
||||
functionTrigger();
|
||||
});
|
||||
|
||||
gameOverlayVisibilityStore.showGameOverlay();
|
||||
public showMyCamera(): void {
|
||||
myCameraVisibilityStore.set(true);
|
||||
}
|
||||
|
||||
public hideGameOverlay(): void {
|
||||
const gameOverlay = HtmlUtils.getElementByIdOrFail("game-overlay");
|
||||
gameOverlay.classList.remove("active");
|
||||
|
||||
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
|
||||
const functionTrigger = () => {
|
||||
this.triggerCloseJitsiFrameButton();
|
||||
};
|
||||
buttonCloseFrame.addEventListener("click", () => {
|
||||
buttonCloseFrame.blur();
|
||||
functionTrigger();
|
||||
});
|
||||
|
||||
gameOverlayVisibilityStore.hideGameOverlay();
|
||||
public hideMyCamera(): void {
|
||||
myCameraVisibilityStore.set(false);
|
||||
}
|
||||
|
||||
private getScreenSharingId(userId: string): string {
|
||||
@@ -179,20 +152,6 @@ export class MediaManager {
|
||||
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) {
|
||||
this.userInputManager = userInputManager;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import type { RoomConnection } from "../Connexion/RoomConnection";
|
||||
import { MESSAGE_TYPE_CONSTRAINT, PeerStatus } from "./VideoPeer";
|
||||
import type { UserSimplePeerInterface } from "./SimplePeer";
|
||||
import { Readable, readable } from "svelte/store";
|
||||
import { videoFocusStore } from "../Stores/VideoFocusStore";
|
||||
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");
|
||||
|
||||
@@ -43,7 +43,10 @@ export class ScreenSharingPeer extends Peer {
|
||||
|
||||
this.streamStore = readable<MediaStream | null>(null, (set) => {
|
||||
const onStream = (stream: MediaStream | null) => {
|
||||
videoFocusStore.focus(this);
|
||||
highlightedEmbedScreen.highlight({
|
||||
type: "streamable",
|
||||
embed: this,
|
||||
});
|
||||
set(stream);
|
||||
};
|
||||
const onData = (chunk: Buffer) => {
|
||||
@@ -177,7 +180,13 @@ export class ScreenSharingPeer extends Peer {
|
||||
public stopPushingScreenSharingToRemoteUser(stream: MediaStream) {
|
||||
this.removeStream(stream);
|
||||
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"),
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export class SimplePeer {
|
||||
}
|
||||
);
|
||||
|
||||
mediaManager.showGameOverlay();
|
||||
mediaManager.showMyCamera();
|
||||
|
||||
//receive message start
|
||||
this.Connection.webRtcStartMessageStream.subscribe((message: UserSimplePeerInterface) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { localStreamStore, obtainedMediaConstraintStore, ObtainedMediaStreamCons
|
||||
import { playersStore } from "../Stores/PlayersStore";
|
||||
import { chatMessagesStore, newChatMessageSubject } from "../Stores/ChatStore";
|
||||
import { getIceServersConfig } from "../Components/Video/utils";
|
||||
import { isMobile } from "../Enum/EnvironmentVariable";
|
||||
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
|
||||
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
|
||||
|
||||
@@ -202,7 +202,7 @@ export class VideoPeer extends Peer {
|
||||
JSON.stringify({
|
||||
type: MESSAGE_TYPE_CONSTRAINT,
|
||||
...constraints,
|
||||
isMobile: isMobile(),
|
||||
isMobile: isMediaBreakpointUp("md"),
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@ const error: NonNullable<Translation["error"]> = {
|
||||
title: "Verbindungen zurückgewiesen",
|
||||
subTitle: "Du kannst diese Welt nicht betreten. Versuche es später noch einmal {error}.",
|
||||
details:
|
||||
"Für weitere Information kannst du die Administratoren kontaktieren oder melde dich bei uns unter: prty.bstly.de",
|
||||
"Für weitere Information kannst du die Administratoren kontaktieren oder melde dich bei uns unter: prty@bstly.de",
|
||||
},
|
||||
connectionRetry: {
|
||||
unableConnect:
|
||||
|
||||
@@ -4,15 +4,15 @@ import audio from "./audio";
|
||||
import camera from "./camera";
|
||||
import chat from "./chat";
|
||||
import companion from "./companion";
|
||||
import emoji from "./emoji";
|
||||
import error from "./error";
|
||||
import follow from "./follow";
|
||||
import login from "./login";
|
||||
import menu from "./menu";
|
||||
import message from "./message";
|
||||
import menu from "./menu";
|
||||
import report from "./report";
|
||||
import warning from "./warning";
|
||||
import woka from "./woka";
|
||||
import emoji from "./emoji";
|
||||
|
||||
const de_DE: Translation = {
|
||||
...en_US,
|
||||
|
||||
+4
-4
@@ -2,7 +2,7 @@ import "phaser";
|
||||
import GameConfig = Phaser.Types.Core.GameConfig;
|
||||
import "../style/index.scss";
|
||||
|
||||
import { DEBUG_MODE, isMobile } from "./Enum/EnvironmentVariable";
|
||||
import { DEBUG_MODE } from "./Enum/EnvironmentVariable";
|
||||
import { LoginScene } from "./Phaser/Login/LoginScene";
|
||||
import { ReconnectingScene } from "./Phaser/Reconnecting/ReconnectingScene";
|
||||
import { SelectCharacterScene } from "./Phaser/Login/SelectCharacterScene";
|
||||
@@ -24,6 +24,7 @@ import App from "./Components/App.svelte";
|
||||
import { HtmlUtils } from "./WebRtc/HtmlUtils";
|
||||
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
|
||||
import { analyticsClient } from "./Administration/AnalyticsClient";
|
||||
import { isMediaBreakpointUp } from "./Utils/BreakpointsUtils";
|
||||
|
||||
const { width, height } = coWebsiteManager.getGameSize();
|
||||
const valueGameQuality = localUserStore.getGameQualityValue();
|
||||
@@ -90,7 +91,7 @@ const config: GameConfig = {
|
||||
scene: [
|
||||
EntryScene,
|
||||
LoginScene,
|
||||
isMobile() ? SelectCharacterMobileScene : SelectCharacterScene,
|
||||
isMediaBreakpointUp("md") ? SelectCharacterMobileScene : SelectCharacterScene,
|
||||
SelectCompanionScene,
|
||||
EnableCameraScene,
|
||||
ReconnectingScene,
|
||||
@@ -136,7 +137,6 @@ const config: GameConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
//const game = new Phaser.Game(config);
|
||||
const game = new Game(config);
|
||||
|
||||
waScaleManager.setGame(game);
|
||||
@@ -156,7 +156,7 @@ coWebsiteManager.onResize.subscribe(() => {
|
||||
iframeListener.init();
|
||||
|
||||
const app = new App({
|
||||
target: HtmlUtils.getElementByIdOrFail("svelte-overlay"),
|
||||
target: HtmlUtils.getElementByIdOrFail("game-overlay"),
|
||||
props: {
|
||||
game: game,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user