Merge branch 'develop' of github.com:thecodingmachine/workadventure into oidcProfile
This commit is contained in:
@@ -289,68 +289,42 @@ class IframeListener {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
console.info("Loading map related script at ", scriptUrl);
|
||||
|
||||
if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
|
||||
// Using external iframe mode (
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.id = IframeListener.getIFrameId(scriptUrl);
|
||||
iframe.style.display = "none";
|
||||
iframe.src =
|
||||
"/iframe.html?script=" +
|
||||
encodeURIComponent(scriptUrl) +
|
||||
"&moduleMode=" +
|
||||
(enableModuleMode ? "true" : "false");
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.id = IframeListener.getIFrameId(scriptUrl);
|
||||
iframe.style.display = "none";
|
||||
|
||||
// We are putting a sandbox on this script because it will run in the same domain as the main website.
|
||||
iframe.sandbox.add("allow-scripts");
|
||||
iframe.sandbox.add("allow-top-navigation-by-user-activation");
|
||||
// We are putting a sandbox on this script because it will run in the same domain as the main website.
|
||||
iframe.sandbox.add("allow-scripts");
|
||||
iframe.sandbox.add("allow-top-navigation-by-user-activation");
|
||||
|
||||
iframe.addEventListener("load", () => {
|
||||
resolve();
|
||||
});
|
||||
//iframe.src = "data:text/html;charset=utf-8," + escape(html);
|
||||
iframe.srcdoc =
|
||||
"<!doctype html>\n" +
|
||||
"\n" +
|
||||
'<html lang="en">\n' +
|
||||
"<head>\n" +
|
||||
'<script src="' +
|
||||
window.location.protocol +
|
||||
"//" +
|
||||
window.location.host +
|
||||
'/iframe_api.js" ></script>\n' +
|
||||
"<script " +
|
||||
(enableModuleMode ? 'type="module" ' : "") +
|
||||
'src="' +
|
||||
scriptUrl +
|
||||
'" ></script>\n' +
|
||||
"<title></title>\n" +
|
||||
"</head>\n" +
|
||||
"</html>\n";
|
||||
|
||||
document.body.prepend(iframe);
|
||||
iframe.addEventListener("load", () => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
this.scripts.set(scriptUrl, iframe);
|
||||
this.registerIframe(iframe);
|
||||
} else {
|
||||
// production code
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.id = IframeListener.getIFrameId(scriptUrl);
|
||||
iframe.style.display = "none";
|
||||
document.body.prepend(iframe);
|
||||
|
||||
// We are putting a sandbox on this script because it will run in the same domain as the main website.
|
||||
iframe.sandbox.add("allow-scripts");
|
||||
iframe.sandbox.add("allow-top-navigation-by-user-activation");
|
||||
|
||||
//iframe.src = "data:text/html;charset=utf-8," + escape(html);
|
||||
iframe.srcdoc =
|
||||
"<!doctype html>\n" +
|
||||
"\n" +
|
||||
'<html lang="en">\n' +
|
||||
"<head>\n" +
|
||||
'<script src="' +
|
||||
window.location.protocol +
|
||||
"//" +
|
||||
window.location.host +
|
||||
'/iframe_api.js" ></script>\n' +
|
||||
"<script " +
|
||||
(enableModuleMode ? 'type="module" ' : "") +
|
||||
'src="' +
|
||||
scriptUrl +
|
||||
'" ></script>\n' +
|
||||
"<title></title>\n" +
|
||||
"</head>\n" +
|
||||
"</html>\n";
|
||||
|
||||
iframe.addEventListener("load", () => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
document.body.prepend(iframe);
|
||||
|
||||
this.scripts.set(scriptUrl, iframe);
|
||||
this.registerIframe(iframe);
|
||||
}
|
||||
this.scripts.set(scriptUrl, iframe);
|
||||
this.registerIframe(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { actionsMenuStore } from "../../Stores/ActionsMenuStore";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import type { Game } from "../Phaser/Game/Game";
|
||||
import { chatVisibilityStore } from "../Stores/ChatStore";
|
||||
import { customCharacterSceneVisibleStore } from "../Stores/CustomCharacterStore";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { requestedScreenSharingState, screenSharingAvailableStore } from "../Stores/ScreenSharingStore";
|
||||
import { isSilentStore, requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore";
|
||||
import monitorImg from "./images/monitor.svg";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
import type { PictureStore } from "../../Stores/PictureStore";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import { CustomizeScene, CustomizeSceneName } from "../../Phaser/Login/CustomizeScene";
|
||||
import { activeRowStore } from "../../Stores/CustomCharacterStore";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import { streamableCollectionStore } from "../../Stores/StreamableCollectionStore";
|
||||
import MediaBox from "../Video/MediaBox.svelte";
|
||||
|
||||
export let highlightedEmbedScreen: EmbedScreen | null;
|
||||
export let highlightedEmbedScreen: EmbedScreen | undefined;
|
||||
export let full = false;
|
||||
$: clickable = !full;
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { ICON_URL } from "../../Enum/EnvironmentVariable";
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
onMount(() => {
|
||||
icon.src = isJitsi
|
||||
? "/resources/logos/meet.svg"
|
||||
? "/resources/logos/jitsi.png"
|
||||
: `${ICON_URL}/icon?url=${coWebsite.getUrl().hostname}&size=64..96..256&fallback_icon_color=14304c`;
|
||||
icon.alt = coWebsite.getUrl().hostname;
|
||||
icon.onload = () => {
|
||||
@@ -73,9 +73,9 @@
|
||||
$mainCoWebsite !== undefined &&
|
||||
$mainCoWebsite.getId() === coWebsite.getId();
|
||||
isHighlight =
|
||||
$highlightedEmbedScreen !== null &&
|
||||
$highlightedEmbedScreen.type === "cowebsite" &&
|
||||
$highlightedEmbedScreen.embed.getId() === coWebsite.getId();
|
||||
$highlightedEmbedScreen !== undefined &&
|
||||
$highlightedEmbedScreen?.type === "cowebsite" &&
|
||||
$highlightedEmbedScreen?.embed.getId() === coWebsite.getId();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -188,10 +188,16 @@
|
||||
/>
|
||||
</rect>
|
||||
</svg>
|
||||
|
||||
<!-- TODO use trigger message property -->
|
||||
<div class="cowebsite-hover" class:hide={!isJitsi} style="width: max-content;">
|
||||
<p>Open / Close Jitsi meeting!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.cowebsite-thumbnail {
|
||||
cursor: url("../../../style/images/cursor_pointer.png"), pointer;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
background-color: rgba(#000000, 0.6);
|
||||
@@ -236,6 +242,11 @@
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.cowebsite-hover {
|
||||
top: -4px;
|
||||
left: 55px;
|
||||
}
|
||||
|
||||
animation: shake 0.35s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -315,10 +326,33 @@
|
||||
}
|
||||
|
||||
&.jitsi {
|
||||
filter: invert(100%);
|
||||
-webkit-filter: invert(100%);
|
||||
padding: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.cowebsite-hover {
|
||||
display: block;
|
||||
width: max-content !important;
|
||||
}
|
||||
}
|
||||
|
||||
.cowebsite-hover {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
top: -40px;
|
||||
left: -4px;
|
||||
width: 0 !important;
|
||||
min-height: 20px;
|
||||
transition: all 0.2s ease;
|
||||
overflow: hidden;
|
||||
color: white;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { coWebsites } from "../../Stores/CoWebsiteStore";
|
||||
import CoWebsiteThumbnail from "./CoWebsiteThumbnailSlot.svelte";
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
left: 2%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
overflow: visible;
|
||||
|
||||
&.vertical {
|
||||
height: auto !important;
|
||||
@@ -31,8 +32,6 @@
|
||||
bottom: auto !important;
|
||||
left: auto !important;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: 4px;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import PresentationLayout from "./Layouts/PresentationLayout.svelte";
|
||||
import MozaicLayout from "./Layouts/MozaicLayout.svelte";
|
||||
import { LayoutMode } from "../../WebRtc/LayoutManager";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import type { Unsubscriber } from "svelte/store";
|
||||
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import { EnableCameraScene, EnableCameraSceneName } from "../../Phaser/Login/EnableCameraScene";
|
||||
import {
|
||||
audioConstraintStore,
|
||||
cameraListStore,
|
||||
localStreamStore,
|
||||
localVolumeStore,
|
||||
microphoneListStore,
|
||||
videoConstraintStore,
|
||||
} from "../../Stores/MediaStore";
|
||||
@@ -38,7 +39,7 @@
|
||||
|
||||
let stream: MediaStream | null;
|
||||
|
||||
const unsubscribe = localStreamStore.subscribe((value) => {
|
||||
const unsubscribeLocalStreamStore = localStreamStore.subscribe((value) => {
|
||||
if (value.type === "success") {
|
||||
stream = value.stream;
|
||||
|
||||
@@ -59,7 +60,9 @@
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(unsubscribe);
|
||||
onDestroy(() => {
|
||||
unsubscribeLocalStreamStore();
|
||||
});
|
||||
|
||||
function normalizeDeviceName(label: string): string {
|
||||
// remove IDs (that can appear in Chrome, like: "HD Pro Webcam (4df7:4eda)"
|
||||
@@ -86,7 +89,7 @@
|
||||
<img class="background-img" src={cinemaCloseImg} alt="" />
|
||||
</div>
|
||||
{/if}
|
||||
<HorizontalSoundMeterWidget {stream} />
|
||||
<HorizontalSoundMeterWidget volume={$localVolumeStore} />
|
||||
|
||||
<section class="selectWebcamForm">
|
||||
{#if $cameraListStore.length > 1}
|
||||
|
||||
@@ -1,50 +1,8 @@
|
||||
<script lang="typescript">
|
||||
import { AudioContext } from "standardized-audio-context";
|
||||
import { SoundMeter } from "../../Phaser/Components/SoundMeter";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
export let stream: MediaStream | null;
|
||||
let volume = 0;
|
||||
<script lang="ts">
|
||||
export let volume = 0;
|
||||
|
||||
const NB_BARS = 20;
|
||||
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
const soundMeter = new SoundMeter();
|
||||
let display = false;
|
||||
let error = false;
|
||||
|
||||
$: {
|
||||
if (stream && stream.getAudioTracks().length > 0) {
|
||||
display = true;
|
||||
soundMeter.connectToSource(stream, new AudioContext());
|
||||
|
||||
if (timeout) {
|
||||
clearInterval(timeout);
|
||||
error = false;
|
||||
}
|
||||
|
||||
timeout = setInterval(() => {
|
||||
try {
|
||||
volume = parseInt(((soundMeter.getVolume() / 100) * NB_BARS).toFixed(0));
|
||||
} catch (err) {
|
||||
if (!error) {
|
||||
console.error(err);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
display = false;
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
soundMeter.stop();
|
||||
if (timeout) {
|
||||
clearInterval(timeout);
|
||||
}
|
||||
});
|
||||
|
||||
function color(i: number, volume: number) {
|
||||
const red = (255 * i) / NB_BARS;
|
||||
const green = 255 * (1 - i / NB_BARS);
|
||||
@@ -58,7 +16,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="horizontal-sound-meter" class:active={display}>
|
||||
<div class="horizontal-sound-meter" class:active={volume !== undefined}>
|
||||
{#each [...Array(NB_BARS).keys()] as i (i)}
|
||||
<div style={color(i, volume)} />
|
||||
{/each}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import followImg from "../images/follow.svg";
|
||||
|
||||
export let hidden: Boolean;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import { helpCameraSettingsVisibleStore } from "../../Stores/HelpCameraSettingsStore";
|
||||
import firefoxImg from "./images/help-setting-camera-permission-firefox.png";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import { LoginScene, LoginSceneName } from "../../Phaser/Login/LoginScene";
|
||||
import { DISPLAY_TERMS_OF_USE, MAX_USERNAME_LENGTH } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { audioManagerVisibilityStore } from "../Stores/AudioManagerStore";
|
||||
import { embedScreenLayout, hasEmbedScreen } from "../Stores/EmbedScreensStore";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import SettingsSubMenu from "./SettingsSubMenu.svelte";
|
||||
import ProfileSubMenu from "./ProfileSubMenu.svelte";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import logoTalk from "../images/logo-message-pixel.png";
|
||||
import logoWA from "../images/logo-WA-pixel.png";
|
||||
import logoInvite from "../images/logo-invite-pixel.png";
|
||||
@@ -61,15 +61,15 @@
|
||||
on:dragstart|preventDefault={noDrag}
|
||||
on:click|preventDefault={showMenu}
|
||||
/>
|
||||
<img
|
||||
src={logoTalk}
|
||||
alt={$LL.menu.icon.open.chat()}
|
||||
class="nes-pointer"
|
||||
draggable="false"
|
||||
on:dragstart|preventDefault={noDrag}
|
||||
on:click|preventDefault={showChat}
|
||||
/>
|
||||
{/if}
|
||||
<img
|
||||
src={logoTalk}
|
||||
alt={$LL.menu.icon.open.chat()}
|
||||
class="nes-pointer"
|
||||
draggable="false"
|
||||
on:dragstart|preventDefault={noDrag}
|
||||
on:click|preventDefault={showChat}
|
||||
/>
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
import { SelectCompanionScene, SelectCompanionSceneName } from "../../Phaser/Login/SelectCompanionScene";
|
||||
import { menuIconVisiblilityStore, menuVisiblilityStore, userIsConnected } from "../../Stores/MenuStore";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { videoConstraintStore } from "../../Stores/MediaStore";
|
||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import { showShareLinkMapModalStore } from "../../Stores/ModalStore";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="typescript">
|
||||
import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
|
||||
<script lang="ts">
|
||||
import { localVolumeStore, obtainedMediaConstraintStore } from "../Stores/MediaStore";
|
||||
import { localStreamStore, isSilentStore } from "../Stores/MediaStore";
|
||||
import SoundMeterWidget from "./SoundMeterWidget.svelte";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
let stream: MediaStream | null;
|
||||
|
||||
const unsubscribe = localStreamStore.subscribe((value) => {
|
||||
const unsubscribeLocalStreamStore = localStreamStore.subscribe((value) => {
|
||||
if (value.type === "success") {
|
||||
stream = value.stream;
|
||||
} else {
|
||||
@@ -16,7 +16,9 @@
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(unsubscribe);
|
||||
onDestroy(() => {
|
||||
unsubscribeLocalStreamStore();
|
||||
});
|
||||
|
||||
let isSilent: boolean;
|
||||
const unsubscribeIsSilent = isSilentStore.subscribe((value) => {
|
||||
@@ -51,7 +53,7 @@
|
||||
<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} />
|
||||
<SoundMeterWidget volume={$localVolumeStore} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import LL from "../../i18n/i18n-svelte";
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import { SelectCompanionScene, SelectCompanionSceneName } from "../../Phaser/Login/SelectCompanionScene";
|
||||
|
||||
@@ -1,47 +1,6 @@
|
||||
<script lang="typescript">
|
||||
import { AudioContext } from "standardized-audio-context";
|
||||
import { SoundMeter } from "../Phaser/Components/SoundMeter";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
export let stream: MediaStream | null;
|
||||
let volume = 0;
|
||||
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
const soundMeter = new SoundMeter();
|
||||
let display = false;
|
||||
let error = false;
|
||||
|
||||
$: {
|
||||
if (stream && stream.getAudioTracks().length > 0) {
|
||||
display = true;
|
||||
soundMeter.connectToSource(stream, new AudioContext());
|
||||
|
||||
if (timeout) {
|
||||
clearInterval(timeout);
|
||||
error = false;
|
||||
}
|
||||
|
||||
timeout = setInterval(() => {
|
||||
try {
|
||||
volume = soundMeter.getVolume();
|
||||
} catch (err) {
|
||||
if (!error) {
|
||||
console.error(err);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
display = false;
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
soundMeter.stop();
|
||||
if (timeout) {
|
||||
clearInterval(timeout);
|
||||
}
|
||||
});
|
||||
<script lang="ts">
|
||||
export let volume = 0;
|
||||
let display = true;
|
||||
</script>
|
||||
|
||||
<div class="sound-progress" class:active={display}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import type { ScreenSharingLocalMedia } from "../../Stores/ScreenSharingStore";
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
import type { Streamable } from "../../Stores/StreamableCollectionStore";
|
||||
|
||||
import type { ScreenSharingPeer } from "../../WebRtc/ScreenSharingPeer";
|
||||
import { getColorByString, srcObject } from "./utils";
|
||||
import { getColorByString, srcObject, getTextColorByBackgroundColor } from "./utils";
|
||||
|
||||
export let clickable = false;
|
||||
|
||||
export let peer: ScreenSharingPeer;
|
||||
let streamStore = peer.streamStore;
|
||||
let name = peer.userName;
|
||||
let backGroundColor = getColorByString(peer.userName);
|
||||
let textColor = getTextColorByBackgroundColor(backGroundColor);
|
||||
let statusStore = peer.statusStore;
|
||||
|
||||
let embedScreen: EmbedScreen;
|
||||
@@ -32,7 +34,7 @@
|
||||
{/if}
|
||||
{#if $streamStore !== null}
|
||||
<i class="container">
|
||||
<span style="background-color: {getColorByString(name)};">{name}</span>
|
||||
<span style="background-color: {backGroundColor}; color: {textColor};">{name}</span>
|
||||
</i>
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import reportImg from "./images/report.svg";
|
||||
import blockSignImg from "./images/blockSign.svg";
|
||||
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
|
||||
import { getColorByString, srcObject } from "./utils";
|
||||
import { getColorByString, getTextColorByBackgroundColor, srcObject } from "./utils";
|
||||
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import type { Streamable } from "../../Stores/StreamableCollectionStore";
|
||||
@@ -18,7 +18,10 @@
|
||||
|
||||
export let peer: VideoPeer;
|
||||
let streamStore = peer.streamStore;
|
||||
let volumeStore = peer.volumeStore;
|
||||
let name = peer.userName;
|
||||
let backGroundColor = getColorByString(peer.userName);
|
||||
let textColor = getTextColorByBackgroundColor(backGroundColor);
|
||||
let statusStore = peer.statusStore;
|
||||
let constraintStore = peer.constraintsStore;
|
||||
|
||||
@@ -64,7 +67,7 @@
|
||||
{/if}
|
||||
<!-- {#if !$constraintStore || $constraintStore.video === false} -->
|
||||
<i class="container">
|
||||
<span style="background-color: {getColorByString(name)};">{name}</span>
|
||||
<span style="background-color: {backGroundColor}; color: {textColor};">{name}</span>
|
||||
</i>
|
||||
<div class="woka-icon {($constraintStore && $constraintStore.video !== false) || minimized ? '' : 'no-video'}">
|
||||
<Woka userId={peer.userId} placeholderSrc={""} />
|
||||
@@ -93,7 +96,7 @@
|
||||
/>
|
||||
<img src={blockSignImg} draggable="false" on:dragstart|preventDefault={noDrag} class="block-logo" alt="Block" />
|
||||
{#if $constraintStore && $constraintStore.audio !== false}
|
||||
<SoundMeterWidget stream={$streamStore} />
|
||||
<SoundMeterWidget volume={$volumeStore} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,6 +18,24 @@ export function getColorByString(str: string): string | null {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param color: string
|
||||
* @return string
|
||||
*/
|
||||
export function getTextColorByBackgroundColor(color: string | null): string {
|
||||
if (!color) {
|
||||
return "white";
|
||||
}
|
||||
const rgb = color.slice(1);
|
||||
const brightness = Math.round(
|
||||
(parseInt(rgb[0] + rgb[1], 16) * 299 +
|
||||
parseInt(rgb[2] + rgb[3], 16) * 587 +
|
||||
parseInt(rgb[4] + rgb[5], 16) * 114) /
|
||||
1000
|
||||
);
|
||||
return brightness > 125 ? "black" : "white";
|
||||
}
|
||||
|
||||
export function srcObject(node: HTMLVideoElement, stream: MediaStream | null) {
|
||||
node.srcObject = stream;
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import { requestVisitCardsStore } from "../../Stores/GameStore";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
@@ -1,39 +1,36 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import { userIsAdminStore, limitMapStore } from "../../Stores/GameStore";
|
||||
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||
import LL from "../../i18n/i18n-svelte";
|
||||
|
||||
const upgradeLink = ADMIN_URL + "/pricing";
|
||||
const registerLink = ADMIN_URL + "/second-step-register";
|
||||
</script>
|
||||
|
||||
<main class="warningMain" transition:fly={{ y: -200, duration: 500 }}>
|
||||
{#if $userIsAdminStore}
|
||||
<h2>{$LL.warning.title()}</h2>
|
||||
<p>
|
||||
{$LL.warning.content({ upgradeLink })}
|
||||
</p>
|
||||
<p>{@html $LL.warning.content()}</p>
|
||||
{:else if $limitMapStore}
|
||||
<p>
|
||||
This map is available for 2 days. You can register your domain <a href={registerLink}>here</a>!
|
||||
</p>
|
||||
{:else}
|
||||
<h2>{$LL.warning.title()}</h2>
|
||||
<p>{$LL.warning.limit()}</p>
|
||||
<p>{@html $LL.warning.content()}</p>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
main.warningMain {
|
||||
pointer-events: auto;
|
||||
width: 80%;
|
||||
width: 100%;
|
||||
background-color: #f9e81e;
|
||||
color: #14304c;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
|
||||
top: 4%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
|
||||
import LL from "../../i18n/i18n-svelte";
|
||||
|
||||
@@ -1,46 +1,33 @@
|
||||
declare global {
|
||||
interface Window {
|
||||
env?: Record<string, string>;
|
||||
}
|
||||
}
|
||||
import { getEnvConfig } from "@geprog/vite-plugin-env-config/getEnvConfig";
|
||||
|
||||
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 DEBUG_MODE: boolean = getEnvConfig("DEBUG_MODE") == "true";
|
||||
const START_ROOM_URL: string =
|
||||
getEnvConfig("START_ROOM_URL") || "/_/global/maps.workadventure.localhost/Floor1/floor1.json";
|
||||
const PUSHER_URL = getEnvConfig("PUSHER_URL") || "//pusher.workadventure.localhost";
|
||||
export const ADMIN_URL = getEnvConfig("ADMIN_URL") || "//workadventu.re";
|
||||
const UPLOADER_URL = getEnvConfig("UPLOADER_URL") || "//uploader.workadventure.localhost";
|
||||
const ICON_URL = getEnvConfig("ICON_URL") || "//icon.workadventure.localhost";
|
||||
const STUN_SERVER: string = getEnvConfig("STUN_SERVER") || "stun:stun.l.google.com:19302";
|
||||
const TURN_SERVER: string = getEnvConfig("TURN_SERVER") || "";
|
||||
const SKIP_RENDER_OPTIMIZATIONS: boolean = getEnvConfig("SKIP_RENDER_OPTIMIZATIONS") == "true";
|
||||
const DISABLE_NOTIFICATIONS: boolean = getEnvConfig("DISABLE_NOTIFICATIONS") == "true";
|
||||
const TURN_USER: string = getEnvConfig("TURN_USER") || "";
|
||||
const TURN_PASSWORD: string = getEnvConfig("TURN_PASSWORD") || "";
|
||||
const JITSI_URL: string | undefined = getEnvConfig("JITSI_URL") === "" ? undefined : getEnvConfig("JITSI_URL");
|
||||
const JITSI_PRIVATE_MODE: boolean = getEnvConfig("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(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 const MAX_USERNAME_LENGTH = parseInt(getEnvConfig("MAX_USERNAME_LENGTH") || "") || 8;
|
||||
export const MAX_PER_GROUP = parseInt(getEnvConfig("MAX_PER_GROUP") || "4");
|
||||
export const DISPLAY_TERMS_OF_USE = getEnvConfig("DISPLAY_TERMS_OF_USE") == "true";
|
||||
export const NODE_ENV = getEnvConfig("NODE_ENV") || "development";
|
||||
export const CONTACT_URL = getEnvConfig("CONTACT_URL") || undefined;
|
||||
export const PROFILE_URL = getEnvConfig("PROFILE_URL") || undefined;
|
||||
export const POSTHOG_API_KEY: string = (getEnvConfig("POSTHOG_API_KEY") as string) || "";
|
||||
export const POSTHOG_URL = getEnvConfig("POSTHOG_URL") || undefined;
|
||||
export const DISABLE_ANONYMOUS: boolean = getEnvConfig("DISABLE_ANONYMOUS") === "true";
|
||||
export const OPID_LOGIN_SCREEN_PROVIDER = getEnvConfig("OPID_LOGIN_SCREEN_PROVIDER");
|
||||
const FALLBACK_LOCALE = getEnvConfig("FALLBACK_LOCALE") || undefined;
|
||||
|
||||
export {
|
||||
DEBUG_MODE,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from "standardized-audio-context";
|
||||
import { AudioContext, IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from "standardized-audio-context";
|
||||
|
||||
/**
|
||||
* Class to measure the sound volume of a media stream
|
||||
@@ -6,41 +6,15 @@ import type { IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from "
|
||||
export class SoundMeter {
|
||||
private instant: number;
|
||||
private clip: number;
|
||||
//private script: ScriptProcessorNode;
|
||||
private analyser: IAnalyserNode<IAudioContext> | undefined;
|
||||
private dataArray: Uint8Array | undefined;
|
||||
private context: IAudioContext | undefined;
|
||||
private source: IMediaStreamAudioSourceNode<IAudioContext> | undefined;
|
||||
|
||||
constructor() {
|
||||
constructor(mediaStream: MediaStream) {
|
||||
this.instant = 0.0;
|
||||
this.clip = 0.0;
|
||||
//this.script = context.createScriptProcessor(2048, 1, 1);
|
||||
}
|
||||
|
||||
private init(context: IAudioContext) {
|
||||
this.context = context;
|
||||
this.analyser = this.context.createAnalyser();
|
||||
|
||||
this.analyser.fftSize = 2048;
|
||||
const bufferLength = this.analyser.fftSize;
|
||||
this.dataArray = new Uint8Array(bufferLength);
|
||||
}
|
||||
|
||||
public connectToSource(stream: MediaStream, context: IAudioContext): void {
|
||||
if (this.source !== undefined) {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
this.init(context);
|
||||
|
||||
this.source = this.context?.createMediaStreamSource(stream);
|
||||
if (this.analyser !== undefined) {
|
||||
this.source?.connect(this.analyser);
|
||||
}
|
||||
//analyser.connect(distortion);
|
||||
//distortion.connect(this.context.destination);
|
||||
//this.analyser.connect(this.context.destination);
|
||||
this.connectToSource(mediaStream, new AudioContext());
|
||||
}
|
||||
|
||||
public getVolume(): number {
|
||||
@@ -78,4 +52,29 @@ export class SoundMeter {
|
||||
this.dataArray = undefined;
|
||||
this.source = undefined;
|
||||
}
|
||||
|
||||
private init(context: IAudioContext) {
|
||||
this.context = context;
|
||||
this.analyser = this.context.createAnalyser();
|
||||
|
||||
this.analyser.fftSize = 2048;
|
||||
const bufferLength = this.analyser.fftSize;
|
||||
this.dataArray = new Uint8Array(bufferLength);
|
||||
}
|
||||
|
||||
private connectToSource(stream: MediaStream, context: IAudioContext): void {
|
||||
if (this.source !== undefined) {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
this.init(context);
|
||||
|
||||
this.source = this.context?.createMediaStreamSource(stream);
|
||||
if (this.analyser !== undefined) {
|
||||
this.source?.connect(this.analyser);
|
||||
}
|
||||
//analyser.connect(distortion);
|
||||
//distortion.connect(this.context.destination);
|
||||
//this.analyser.connect(this.context.destination);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Easing } from "../../types";
|
||||
|
||||
export class TalkIcon extends Phaser.GameObjects.Image {
|
||||
private shown: boolean;
|
||||
private showAnimationTween?: Phaser.Tweens.Tween;
|
||||
|
||||
private defaultPosition: { x: number; y: number };
|
||||
private defaultScale: number;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||
super(scene, x, y, "iconTalk");
|
||||
|
||||
this.defaultPosition = { x, y };
|
||||
this.defaultScale = 0.3;
|
||||
|
||||
this.shown = false;
|
||||
this.setAlpha(0);
|
||||
this.setScale(this.defaultScale);
|
||||
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
public show(show: boolean = true, forceClose: boolean = false): void {
|
||||
if (this.shown === show && !forceClose) {
|
||||
return;
|
||||
}
|
||||
this.showAnimation(show, forceClose);
|
||||
}
|
||||
|
||||
private showAnimation(show: boolean = true, forceClose: boolean = false) {
|
||||
if (forceClose && !show) {
|
||||
this.showAnimationTween?.stop();
|
||||
} else if (this.showAnimationTween?.isPlaying()) {
|
||||
return;
|
||||
}
|
||||
this.shown = show;
|
||||
if (show) {
|
||||
this.y += 50;
|
||||
this.scale = 0.05;
|
||||
this.alpha = 0;
|
||||
}
|
||||
this.showAnimationTween = this.scene.tweens.add({
|
||||
targets: [this],
|
||||
duration: 350,
|
||||
alpha: show ? 1 : 0,
|
||||
y: this.defaultPosition.y,
|
||||
scale: this.defaultScale,
|
||||
ease: Easing.BackEaseOut,
|
||||
onComplete: () => {
|
||||
this.showAnimationTween = undefined;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public isShown(): boolean {
|
||||
return this.shown;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { Unsubscriber, Writable, writable } from "svelte/store";
|
||||
import { createColorStore } from "../../Stores/OutlineColorStore";
|
||||
import type { OutlineableInterface } from "../Game/OutlineableInterface";
|
||||
import type CancelablePromise from "cancelable-promise";
|
||||
import { TalkIcon } from "../Components/TalkIcon";
|
||||
|
||||
const playerNameY = -25;
|
||||
|
||||
@@ -33,6 +34,7 @@ const interactiveRadius = 35;
|
||||
export abstract class Character extends Container implements OutlineableInterface {
|
||||
private bubble: SpeechBubble | null = null;
|
||||
private readonly playerNameText: Text;
|
||||
private readonly talkIcon: TalkIcon;
|
||||
public playerName: string;
|
||||
public sprites: Map<string, Sprite>;
|
||||
protected lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down;
|
||||
@@ -95,13 +97,24 @@ export abstract class Character extends Container implements OutlineableInterfac
|
||||
fontFamily: '"Press Start 2P"',
|
||||
fontSize: "8px",
|
||||
strokeThickness: 2,
|
||||
stroke: "gray",
|
||||
stroke: "#14304C",
|
||||
metrics: {
|
||||
ascent: 20,
|
||||
descent: 10,
|
||||
fontSize: 35,
|
||||
},
|
||||
});
|
||||
|
||||
this.talkIcon = new TalkIcon(scene, 0, -45);
|
||||
this.add(this.talkIcon);
|
||||
|
||||
if (isClickable) {
|
||||
this.setInteractive({
|
||||
hitArea: new Phaser.Geom.Circle(0, 0, interactiveRadius),
|
||||
hitAreaCallback: Phaser.Geom.Circle.Contains, //eslint-disable-line @typescript-eslint/unbound-method
|
||||
useHandCursor: true,
|
||||
});
|
||||
}
|
||||
this.playerNameText.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX);
|
||||
this.add(this.playerNameText);
|
||||
|
||||
@@ -200,6 +213,10 @@ export abstract class Character extends Container implements OutlineableInterfac
|
||||
});
|
||||
}
|
||||
|
||||
public showTalkIcon(show: boolean = true, forceClose: boolean = false): void {
|
||||
this.talkIcon.show(show, forceClose);
|
||||
}
|
||||
|
||||
public addCompanion(name: string, texturePromise?: CancelablePromise<string>): void {
|
||||
if (typeof texturePromise !== "undefined") {
|
||||
this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise);
|
||||
|
||||
@@ -13,7 +13,7 @@ export class ActivatablesManager {
|
||||
|
||||
private canSelectByDistance: boolean = true;
|
||||
|
||||
private readonly outlineColor = 0xffff00;
|
||||
private readonly outlineColor = 0xf9e81e;
|
||||
private readonly directionalActivationPositionShift = 50;
|
||||
|
||||
constructor(currentPlayer: Player) {
|
||||
|
||||
@@ -133,6 +133,18 @@ export class GameMap {
|
||||
return grid;
|
||||
}
|
||||
|
||||
public getWalkingCostGrid(): number[][] {
|
||||
const grid: number[][] = [];
|
||||
for (let y = 0; y < this.map.height; y += 1) {
|
||||
const row: number[] = [];
|
||||
for (let x = 0; x < this.map.width; x += 1) {
|
||||
row.push(this.getWalkingCostAt(x, y));
|
||||
}
|
||||
grid.push(row);
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
public getTileDimensions(): { width: number; height: number } {
|
||||
return { width: this.map.tilewidth, height: this.map.tileheight };
|
||||
}
|
||||
@@ -336,13 +348,39 @@ export class GameMap {
|
||||
if (!layer.visible) {
|
||||
continue;
|
||||
}
|
||||
if (layer.getTileAt(x, y)?.properties[GameMapProperties.COLLIDES]) {
|
||||
if (layer.getTileAt(x, y)?.properties?.[GameMapProperties.COLLIDES]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private getWalkingCostAt(x: number, y: number): number {
|
||||
const bigCost = 100;
|
||||
for (const layer of this.phaserLayers) {
|
||||
if (!layer.visible) {
|
||||
continue;
|
||||
}
|
||||
const tile = layer.getTileAt(x, y);
|
||||
if (!tile) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
tile &&
|
||||
(tile.properties[GameMapProperties.EXIT_URL] || tile.properties[GameMapProperties.EXIT_SCENE_URL])
|
||||
) {
|
||||
return bigCost;
|
||||
}
|
||||
for (const property of layer.layer.properties) {
|
||||
//@ts-ignore
|
||||
if (property.name && property.name === "exitUrl") {
|
||||
return bigCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private triggerAllProperties(): void {
|
||||
const newProps = this.getProperties(this.key ?? 0);
|
||||
const oldProps = this.lastProperties;
|
||||
|
||||
@@ -91,6 +91,7 @@ import { MapStore } from "../../Stores/Utils/MapStore";
|
||||
import { followUsersColorStore } from "../../Stores/FollowStore";
|
||||
import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler";
|
||||
import { locale } from "../../i18n/i18n-svelte";
|
||||
import { localVolumeStore } from "../../Stores/MediaStore";
|
||||
import { StringUtils } from "../../Utils/StringUtils";
|
||||
import { startLayerNamesStore } from "../../Stores/StartLayerNamesStore";
|
||||
import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite";
|
||||
@@ -172,6 +173,9 @@ export class GameScene extends DirtyScene {
|
||||
private peerStoreUnsubscribe!: Unsubscriber;
|
||||
private emoteUnsubscribe!: Unsubscriber;
|
||||
private emoteMenuUnsubscribe!: Unsubscriber;
|
||||
|
||||
private volumeStoreUnsubscribers: Map<number, Unsubscriber> = new Map<number, Unsubscriber>();
|
||||
private localVolumeStoreUnsubscriber: Unsubscriber | undefined;
|
||||
private followUsersColorStoreUnsubscribe!: Unsubscriber;
|
||||
|
||||
private biggestAvailableAreaStoreUnsubscribe!: () => void;
|
||||
@@ -247,6 +251,7 @@ export class GameScene extends DirtyScene {
|
||||
loadCustomTexture(this.load, texture).catch((e) => console.error(e));
|
||||
}
|
||||
}
|
||||
this.load.image("iconTalk", "/resources/icons/icon_talking.png");
|
||||
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
this.load.image(joystickBaseKey, joystickBaseImg);
|
||||
@@ -338,7 +343,6 @@ export class GameScene extends DirtyScene {
|
||||
(this.load as any).rexWebFont({
|
||||
custom: {
|
||||
families: ["Press Start 2P"],
|
||||
urls: ["/resources/fonts/fonts.css"],
|
||||
testString: "abcdefg",
|
||||
},
|
||||
});
|
||||
@@ -567,6 +571,7 @@ export class GameScene extends DirtyScene {
|
||||
this.pathfindingManager = new PathfindingManager(
|
||||
this,
|
||||
this.gameMap.getCollisionGrid(),
|
||||
this.gameMap.getWalkingCostGrid(),
|
||||
this.gameMap.getTileDimensions()
|
||||
);
|
||||
|
||||
@@ -582,12 +587,6 @@ export class GameScene extends DirtyScene {
|
||||
waScaleManager
|
||||
);
|
||||
|
||||
this.pathfindingManager = new PathfindingManager(
|
||||
this,
|
||||
this.gameMap.getCollisionGrid(),
|
||||
this.gameMap.getTileDimensions()
|
||||
);
|
||||
|
||||
this.activatablesManager = new ActivatablesManager(this.CurrentPlayer);
|
||||
|
||||
biggestAvailableAreaStore.recompute();
|
||||
@@ -641,14 +640,45 @@ export class GameScene extends DirtyScene {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
const talkIconVolumeTreshold = 10;
|
||||
let oldPeerNumber = 0;
|
||||
this.peerStoreUnsubscribe = peerStore.subscribe((peers) => {
|
||||
this.volumeStoreUnsubscribers.forEach((unsubscribe) => unsubscribe());
|
||||
this.volumeStoreUnsubscribers.clear();
|
||||
|
||||
for (const [key, videoStream] of peers) {
|
||||
this.volumeStoreUnsubscribers.set(
|
||||
key,
|
||||
videoStream.volumeStore.subscribe((volume) => {
|
||||
if (volume) {
|
||||
this.MapPlayersByKey.get(key)?.showTalkIcon(volume > talkIconVolumeTreshold);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const newPeerNumber = peers.size;
|
||||
if (newPeerNumber > oldPeerNumber) {
|
||||
this.playSound("audio-webrtc-in");
|
||||
} else if (newPeerNumber < oldPeerNumber) {
|
||||
this.playSound("audio-webrtc-out");
|
||||
}
|
||||
if (newPeerNumber > 0) {
|
||||
if (!this.localVolumeStoreUnsubscriber) {
|
||||
this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => {
|
||||
if (volume) {
|
||||
this.CurrentPlayer.showTalkIcon(volume > talkIconVolumeTreshold);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.CurrentPlayer.showTalkIcon(false, true);
|
||||
this.MapPlayersByKey.forEach((remotePlayer) => remotePlayer.showTalkIcon(false, true));
|
||||
if (this.localVolumeStoreUnsubscriber) {
|
||||
this.localVolumeStoreUnsubscriber();
|
||||
this.localVolumeStoreUnsubscriber = undefined;
|
||||
}
|
||||
}
|
||||
oldPeerNumber = newPeerNumber;
|
||||
});
|
||||
|
||||
@@ -1190,6 +1220,8 @@ ${escapedMessage}
|
||||
openCoWebsite.closable ?? true
|
||||
);
|
||||
|
||||
coWebsiteManager.addCoWebsiteToStore(coWebsite, openCoWebsite.position);
|
||||
|
||||
if (openCoWebsite.lazy === undefined || !openCoWebsite.lazy) {
|
||||
await coWebsiteManager.loadCoWebsite(coWebsite);
|
||||
}
|
||||
@@ -1418,7 +1450,7 @@ ${escapedMessage}
|
||||
phaserLayers[i].setCollisionByProperty({ collides: true }, visible);
|
||||
}
|
||||
}
|
||||
this.pathfindingManager.setCollisionGrid(this.gameMap.getCollisionGrid());
|
||||
this.pathfindingManager.setCollisionGrid(this.gameMap.getCollisionGrid(), this.gameMap.getWalkingCostGrid());
|
||||
this.markDirty();
|
||||
}
|
||||
|
||||
@@ -1566,6 +1598,8 @@ ${escapedMessage}
|
||||
}
|
||||
})
|
||||
.catch((reason) => console.warn(reason));
|
||||
|
||||
urlManager.clearHashParameter();
|
||||
} catch (err) {
|
||||
console.warn(`Cannot proceed with moveTo command:\n\t-> ${err}`);
|
||||
}
|
||||
@@ -1685,7 +1719,7 @@ ${escapedMessage}
|
||||
}
|
||||
});
|
||||
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OVER, (pointer: Phaser.Input.Pointer) => {
|
||||
this.CurrentPlayer.pointerOverOutline(0x00ffff);
|
||||
this.CurrentPlayer.pointerOverOutline(0x365dff);
|
||||
});
|
||||
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OUT, (pointer: Phaser.Input.Pointer) => {
|
||||
this.CurrentPlayer.pointerOutOutline();
|
||||
|
||||
@@ -56,7 +56,7 @@ export class ActionableItem implements ActivatableInterface {
|
||||
|
||||
this.getOutlinePlugin()?.add(this.sprite, {
|
||||
thickness: 2,
|
||||
outlineColor: 0xffff00,
|
||||
outlineColor: 0xf9e81e,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ export default {
|
||||
state = initState;
|
||||
}
|
||||
|
||||
// Idée: ESSAYER WebPack? https://paultavares.wordpress.com/2018/07/02/webpack-how-to-generate-an-es-module-bundle/
|
||||
const computer = new Sprite(scene, object.x, object.y, "computer");
|
||||
scene.add.existing(computer);
|
||||
if (state.status === "on") {
|
||||
|
||||
@@ -15,7 +15,7 @@ export type EmbedScreen =
|
||||
};
|
||||
|
||||
function createHighlightedEmbedScreenStore() {
|
||||
const { subscribe, set, update } = writable<EmbedScreen | null>(null);
|
||||
const { subscribe, set, update } = writable<EmbedScreen | undefined>(undefined);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
@@ -23,7 +23,7 @@ function createHighlightedEmbedScreenStore() {
|
||||
set(embedScreen);
|
||||
},
|
||||
removeHighlight: () => {
|
||||
set(null);
|
||||
set(undefined);
|
||||
},
|
||||
toggleHighlight: (embedScreen: EmbedScreen) => {
|
||||
update((currentEmbedScreen) =>
|
||||
@@ -36,7 +36,7 @@ function createHighlightedEmbedScreenStore() {
|
||||
currentEmbedScreen.type === "streamable" &&
|
||||
embedScreen.embed.uniqueId !== currentEmbedScreen.embed.uniqueId)
|
||||
? embedScreen
|
||||
: null
|
||||
: undefined
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -10,6 +10,8 @@ import { myCameraVisibilityStore } from "./MyCameraStoreVisibility";
|
||||
import { peerStore } from "./PeerStore";
|
||||
import { privacyShutdownStore } from "./PrivacyShutdownStore";
|
||||
import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError";
|
||||
import { SoundMeter } from "../Phaser/Components/SoundMeter";
|
||||
import { AudioContext } from "standardized-audio-context";
|
||||
|
||||
/**
|
||||
* A store that contains the camera state requested by the user (on or off).
|
||||
@@ -541,6 +543,41 @@ export const obtainedMediaConstraintStore = derived<Readable<MediaStreamConstrai
|
||||
}
|
||||
);
|
||||
|
||||
export const localVolumeStore = readable<number | undefined>(undefined, (set) => {
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
const unsubscribe = localStreamStore.subscribe((localStreamStoreValue) => {
|
||||
clearInterval(timeout);
|
||||
if (localStreamStoreValue.type === "error") {
|
||||
set(undefined);
|
||||
return;
|
||||
}
|
||||
const mediaStream = localStreamStoreValue.stream;
|
||||
|
||||
if (mediaStream === null || mediaStream.getAudioTracks().length <= 0) {
|
||||
set(undefined);
|
||||
return;
|
||||
}
|
||||
const soundMeter = new SoundMeter(mediaStream);
|
||||
let error = false;
|
||||
|
||||
timeout = setInterval(() => {
|
||||
try {
|
||||
set(soundMeter.getVolume());
|
||||
} catch (err) {
|
||||
if (!error) {
|
||||
console.error(err);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
clearInterval(timeout);
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Device list
|
||||
*/
|
||||
|
||||
@@ -89,7 +89,7 @@ export class MapStore<K, V> extends Map<K, V> implements Readable<Map<K, V>> {
|
||||
}
|
||||
}
|
||||
|
||||
return readable(initStoreValue, (set) => {
|
||||
return readable<T | undefined>(initStoreValue, (set) => {
|
||||
const storeByKey = this.getStore(key);
|
||||
|
||||
let unsubscribeDeepStore: Unsubscriber | undefined;
|
||||
|
||||
@@ -58,6 +58,10 @@ class UrlManager {
|
||||
return this.getHashParameters()[name];
|
||||
}
|
||||
|
||||
public clearHashParameter(): void {
|
||||
window.location.hash = "";
|
||||
}
|
||||
|
||||
private getHashParameters(): Record<string, string> {
|
||||
return window.location.hash
|
||||
.substring(1)
|
||||
|
||||
@@ -8,19 +8,27 @@ export class PathfindingManager {
|
||||
private grid: number[][];
|
||||
private tileDimensions: { width: number; height: number };
|
||||
|
||||
constructor(scene: Phaser.Scene, collisionsGrid: number[][], tileDimensions: { width: number; height: number }) {
|
||||
constructor(
|
||||
scene: Phaser.Scene,
|
||||
collisionsGrid: number[][],
|
||||
walkingCostGrid: number[][],
|
||||
tileDimensions: { width: number; height: number }
|
||||
) {
|
||||
this.scene = scene;
|
||||
|
||||
this.easyStar = new EasyStar.js();
|
||||
this.easyStar.enableDiagonals();
|
||||
this.easyStar.disableCornerCutting();
|
||||
|
||||
this.grid = collisionsGrid;
|
||||
this.tileDimensions = tileDimensions;
|
||||
this.setEasyStarGrid(collisionsGrid);
|
||||
this.setWalkingCostGrid(walkingCostGrid);
|
||||
}
|
||||
|
||||
public setCollisionGrid(collisionGrid: number[][]): void {
|
||||
public setCollisionGrid(collisionGrid: number[][], walkingCostGrid: number[][]): void {
|
||||
this.setEasyStarGrid(collisionGrid);
|
||||
this.setWalkingCostGrid(walkingCostGrid);
|
||||
}
|
||||
|
||||
public async findPath(
|
||||
@@ -115,6 +123,14 @@ export class PathfindingManager {
|
||||
this.easyStar.setAcceptableTiles([0]); // zeroes are walkable
|
||||
}
|
||||
|
||||
private setWalkingCostGrid(grid: number[][]): void {
|
||||
for (let y = 0; y < grid.length; y += 1) {
|
||||
for (let x = 0; x < grid[y].length; x += 1) {
|
||||
this.easyStar.setAdditionalPointCost(x, y, grid[y][x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private logGridToTheConsole(grid: number[][]): void {
|
||||
let rowNumber = 0;
|
||||
for (const row of grid) {
|
||||
|
||||
@@ -159,9 +159,17 @@ class CoWebsiteManager {
|
||||
});
|
||||
|
||||
buttonSwipe.addEventListener("click", () => {
|
||||
const mainCoWebsite = this.getMainCoWebsite();
|
||||
const highlightedEmbed = get(highlightedEmbedScreen);
|
||||
if (highlightedEmbed?.type === "cowebsite") {
|
||||
this.goToMain(highlightedEmbed.embed);
|
||||
|
||||
if (mainCoWebsite) {
|
||||
highlightedEmbedScreen.toggleHighlight({
|
||||
type: "cowebsite",
|
||||
embed: mainCoWebsite,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -553,6 +561,13 @@ class CoWebsiteManager {
|
||||
coWebsites.remove(coWebsite);
|
||||
coWebsites.add(coWebsite, 0);
|
||||
|
||||
if (mainCoWebsite) {
|
||||
const iframe = mainCoWebsite.getIframe();
|
||||
if (iframe) {
|
||||
iframe.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
isMediaBreakpointDown("lg") &&
|
||||
get(embedScreenLayout) === LayoutMode.Presentation &&
|
||||
@@ -596,12 +611,20 @@ class CoWebsiteManager {
|
||||
.load()
|
||||
.then(() => {
|
||||
const mainCoWebsite = this.getMainCoWebsite();
|
||||
if (mainCoWebsite && mainCoWebsite.getId() === coWebsite.getId()) {
|
||||
this.openMain();
|
||||
const highlightedEmbed = get(highlightedEmbedScreen);
|
||||
if (mainCoWebsite) {
|
||||
if (mainCoWebsite.getId() === coWebsite.getId()) {
|
||||
this.openMain();
|
||||
|
||||
setTimeout(() => {
|
||||
this.fire();
|
||||
}, animationTime);
|
||||
setTimeout(() => {
|
||||
this.fire();
|
||||
}, animationTime);
|
||||
} else if (!highlightedEmbed) {
|
||||
highlightedEmbedScreen.toggleHighlight({
|
||||
type: "cowebsite",
|
||||
embed: coWebsite,
|
||||
});
|
||||
}
|
||||
}
|
||||
this.resizeAllIframes();
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type * as SimplePeerNamespace from "simple-peer";
|
||||
import type { RoomConnection } from "../Connexion/RoomConnection";
|
||||
import { MESSAGE_TYPE_CONSTRAINT, PeerStatus } from "./VideoPeer";
|
||||
import type { UserSimplePeerInterface } from "./SimplePeer";
|
||||
@@ -6,8 +5,8 @@ import { Readable, readable, writable, Writable } from "svelte/store";
|
||||
import { getIceServersConfig } from "../Components/Video/utils";
|
||||
import { highlightedEmbedScreen } from "../Stores/EmbedScreensStore";
|
||||
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
|
||||
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
|
||||
import Peer from "simple-peer/simplepeer.min.js";
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
/**
|
||||
* A peer connection used to transmit video / audio signals between 2 peers.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type * as SimplePeerNamespace from "simple-peer";
|
||||
import { mediaManager } from "./MediaManager";
|
||||
import type { RoomConnection } from "../Connexion/RoomConnection";
|
||||
import { blackListManager } from "./BlackListManager";
|
||||
@@ -10,8 +9,11 @@ import { playersStore } from "../Stores/PlayersStore";
|
||||
import { chatMessagesStore, newChatMessageSubject } from "../Stores/ChatStore";
|
||||
import { getIceServersConfig } from "../Components/Video/utils";
|
||||
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
|
||||
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
|
||||
import { SoundMeter } from "../Phaser/Components/SoundMeter";
|
||||
import { AudioContext } from "standardized-audio-context";
|
||||
import { Console } from "console";
|
||||
import Peer from "simple-peer/simplepeer.min.js";
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
export type PeerStatus = "connecting" | "connected" | "error" | "closed";
|
||||
|
||||
@@ -33,6 +35,7 @@ export class VideoPeer extends Peer {
|
||||
private onBlockSubscribe: Subscription;
|
||||
private onUnBlockSubscribe: Subscription;
|
||||
public readonly streamStore: Readable<MediaStream | null>;
|
||||
public readonly volumeStore: Readable<number | undefined>;
|
||||
public readonly statusStore: Readable<PeerStatus>;
|
||||
public readonly constraintsStore: Readable<ObtainedMediaStreamConstraints | null>;
|
||||
private newMessageSubscribtion: Subscription | undefined;
|
||||
@@ -69,6 +72,34 @@ export class VideoPeer extends Peer {
|
||||
};
|
||||
});
|
||||
|
||||
this.volumeStore = readable<number | undefined>(undefined, (set) => {
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
const unsubscribe = this.streamStore.subscribe((mediaStream) => {
|
||||
if (mediaStream === null || mediaStream.getAudioTracks().length <= 0) {
|
||||
set(undefined);
|
||||
return;
|
||||
}
|
||||
const soundMeter = new SoundMeter(mediaStream);
|
||||
let error = false;
|
||||
|
||||
timeout = setInterval(() => {
|
||||
try {
|
||||
set(soundMeter.getVolume());
|
||||
} catch (err) {
|
||||
if (!error) {
|
||||
console.error(err);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
clearInterval(timeout);
|
||||
};
|
||||
});
|
||||
|
||||
this.constraintsStore = readable<ObtainedMediaStreamConstraints | null>(null, (set) => {
|
||||
const onData = (chunk: Buffer) => {
|
||||
const message = JSON.parse(chunk.toString("utf8"));
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { Translation } from "../i18n-types";
|
||||
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
const upgradeLink = ADMIN_URL + "/pricing";
|
||||
|
||||
const warning: NonNullable<Translation["warning"]> = {
|
||||
title: "Warnung!",
|
||||
content:
|
||||
'Diese Welt erreicht bald die maximale Kapazität. Du kannst die Kapazität <a href={upgradeLink} target="_blank">hier</a> erhöhen',
|
||||
content: `Diese Welt erreicht bald die maximale Kapazität. Du kannst die Kapazität <a href="${upgradeLink}" target="_blank">hier</a> erhöhen`,
|
||||
limit: "Diese Welt erreicht bald die maximale Kapazität!",
|
||||
accessDenied: {
|
||||
camera: "Zugriff auf die Kamera verweigert. Hier klicken um deine Browser Berechtigungen zu prüfen.",
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { BaseTranslation } from "../i18n-types";
|
||||
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
const upgradeLink = ADMIN_URL + "/pricing";
|
||||
|
||||
const warning: BaseTranslation = {
|
||||
title: "Warning!",
|
||||
content:
|
||||
'This world is close to its limit!. You can upgrade its capacity <a href={upgradeLink} target="_blank">here</a>',
|
||||
content: `This world is close to its limit!. You can upgrade its capacity <a href="${upgradeLink}" target="_blank">here</a>`,
|
||||
limit: "This world is close to its limit!",
|
||||
accessDenied: {
|
||||
camera: "Camera access denied. Click here and check your browser permissions.",
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { Translation } from "../i18n-types";
|
||||
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
const upgradeLink = ADMIN_URL + "/pricing";
|
||||
|
||||
const warning: NonNullable<Translation["warning"]> = {
|
||||
title: "Attention!",
|
||||
content:
|
||||
'Ce monde est proche de sa limite ! Vous pouvez améliorer sa capacité <a href={upgradeLink} target="_blank">içi</a>',
|
||||
content: `Ce monde est proche de sa limite ! Vous pouvez améliorer sa capacité <a href="${upgradeLink}" target="_blank">içi</a>`,
|
||||
limit: "Ce monde est proche de ses limites!",
|
||||
accessDenied: {
|
||||
camera: "Accès à la caméra refusé. Cliquez ici et vérifiez les autorisations de votre navigateur.",
|
||||
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
declare module "simple-peer/simplepeer.min.js" {
|
||||
import SimplePeer from "simple-peer";
|
||||
export default SimplePeer;
|
||||
}
|
||||
Reference in New Issue
Block a user