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

This commit is contained in:
Lurkars 2021-09-16 18:12:51 +02:00
commit 7922de10ff
32 changed files with 575 additions and 81 deletions

View file

@ -0,0 +1,74 @@
import { POSTHOG_API_KEY, POSTHOG_URL } from "../Enum/EnvironmentVariable";
class AnalyticsClient {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private posthogPromise: Promise<any>;
constructor() {
if (POSTHOG_API_KEY && POSTHOG_URL) {
this.posthogPromise = import("posthog-js").then(({ default: posthog }) => {
posthog.init(POSTHOG_API_KEY, { api_host: POSTHOG_URL, disable_cookie: true });
return posthog;
});
} else {
this.posthogPromise = Promise.reject();
}
}
identifyUser(uuid: string) {
this.posthogPromise
.then((posthog) => {
posthog.identify(uuid, { uuid, wa: true });
})
.catch();
}
loggedWithSso() {
this.posthogPromise
.then((posthog) => {
posthog.capture("wa-logged-sso");
})
.catch();
}
loggedWithToken() {
this.posthogPromise
.then((posthog) => {
posthog.capture("wa-logged-token");
})
.catch();
}
enteredRoom(roomId: string) {
this.posthogPromise
.then((posthog) => {
posthog.capture("$pageView", { roomId });
})
.catch();
}
openedMenu() {
this.posthogPromise
.then((posthog) => {
posthog.capture("wa-opened-menu");
})
.catch();
}
launchEmote(emote: string) {
this.posthogPromise
.then((posthog) => {
posthog.capture("wa-emote-launch", { emote });
})
.catch();
}
enteredJitsi(roomName: string, roomId: string) {
this.posthogPromise
.then((posthog) => {
posthog.capture("wa-entered-jitsi", { roomName, roomId });
})
.catch();
}
}
export const analyticsClient = new AnalyticsClient();

View file

@ -46,30 +46,18 @@ type AnswererCallback<T extends keyof IframeQueryMap> = (
* Also allows to send messages to those iframes.
*/
class IframeListener {
private readonly _readyStream: Subject<HTMLIFrameElement> = new Subject();
public readonly readyStream = this._readyStream.asObservable();
private readonly _chatStream: Subject<ChatEvent> = new Subject();
public readonly chatStream = this._chatStream.asObservable();
private readonly _openPopupStream: Subject<OpenPopupEvent> = new Subject();
public readonly openPopupStream = this._openPopupStream.asObservable();
private readonly _openTabStream: Subject<OpenTabEvent> = new Subject();
public readonly openTabStream = this._openTabStream.asObservable();
private readonly _goToPageStream: Subject<GoToPageEvent> = new Subject();
public readonly goToPageStream = this._goToPageStream.asObservable();
private readonly _loadPageStream: Subject<string> = new Subject();
public readonly loadPageStream = this._loadPageStream.asObservable();
private readonly _openCoWebSiteStream: Subject<OpenCoWebSiteEvent> = new Subject();
public readonly openCoWebSiteStream = this._openCoWebSiteStream.asObservable();
private readonly _closeCoWebSiteStream: Subject<void> = new Subject();
public readonly closeCoWebSiteStream = this._closeCoWebSiteStream.asObservable();
private readonly _disablePlayerControlStream: Subject<void> = new Subject();
public readonly disablePlayerControlStream = this._disablePlayerControlStream.asObservable();
@ -219,7 +207,7 @@ class IframeListener {
} else if (payload.type === "setProperty" && isSetPropertyEvent(payload.data)) {
this._setPropertyStream.next(payload.data);
} else if (payload.type === "chat" && isChatEvent(payload.data)) {
this._chatStream.next(payload.data);
scriptUtils.sendAnonymousChat(payload.data);
} else if (payload.type === "openPopup" && isOpenPopupEvent(payload.data)) {
this._openPopupStream.next(payload.data);
} else if (payload.type === "closePopup" && isClosePopupEvent(payload.data)) {

View file

@ -1,4 +1,7 @@
import { coWebsiteManager } from "../WebRtc/CoWebsiteManager";
import { playersStore } from "../Stores/PlayersStore";
import { chatMessagesStore } from "../Stores/ChatStore";
import type { ChatEvent } from "./Events/ChatEvent";
class ScriptUtils {
public openTab(url: string) {
@ -16,6 +19,11 @@ class ScriptUtils {
public closeCoWebSite() {
coWebsiteManager.closeCoWebsite();
}
public sendAnonymousChat(chatEvent: ChatEvent) {
const userId = playersStore.addFacticePlayer(chatEvent.author);
chatMessagesStore.addExternalMessage(userId, chatEvent.message);
}
}
export const scriptUtils = new ScriptUtils();

View file

@ -5,9 +5,9 @@
audioManagerFileStore,
audioManagerVolumeStore,
} from "../../Stores/AudioManagerStore";
import {get} from "svelte/store";
import { get } from "svelte/store";
import type { Unsubscriber } from "svelte/store";
import {onDestroy, onMount} from "svelte";
import { onDestroy, onMount } from "svelte";
let HTMLAudioPlayer: HTMLAudioElement;
let audioPlayerVolumeIcon: HTMLElement;
@ -21,9 +21,9 @@
onMount(() => {
volume = localUserStore.getAudioPlayerVolume();
audioManagerVolumeStore.setMuted(localUserStore.getAudioPlayerMuted());
setVolume();
changeVolume();
unsubscriberFileStore = audioManagerFileStore.subscribe(() =>{
unsubscriberFileStore = audioManagerFileStore.subscribe(() => {
HTMLAudioPlayer.pause();
HTMLAudioPlayer.loop = get(audioManagerVolumeStore).loop;
HTMLAudioPlayer.volume = get(audioManagerVolumeStore).volume;
@ -53,13 +53,7 @@
}
})
function onMute() {
audioManagerVolumeStore.setMuted(!get(audioManagerVolumeStore).muted);
localUserStore.setAudioPlayerMuted(get(audioManagerVolumeStore).muted);
setVolume();
}
function setVolume() {
function changeVolume() {
if (get(audioManagerVolumeStore).muted) {
audioPlayerVolumeIcon.classList.add('muted');
audioPlayerVol.value = "0";
@ -76,8 +70,22 @@
audioPlayerVolumeIcon.classList.remove('mid');
}
}
audioManagerVolumeStore.setVolume(volume)
localUserStore.setAudioPlayerVolume(get(audioManagerVolumeStore).volume);
}
function onMute() {
const muted = !get(audioManagerVolumeStore).muted;
audioManagerVolumeStore.setMuted(muted);
localUserStore.setAudioPlayerMuted(muted);
changeVolume();
}
function setVolume() {
volume = parseFloat(audioPlayerVol.value);
audioManagerVolumeStore.setVolume(volume);
localUserStore.setAudioPlayerVolume(volume);
audioManagerVolumeStore.setMuted(false);
localUserStore.setAudioPlayerMuted(false);
changeVolume();
}
function setDecrease() {
@ -108,8 +116,7 @@
</g>
</svg>
</span>
<input type="range" min="0" max="1" step="0.025" bind:value={volume} bind:this={audioPlayerVol}
on:change={setVolume}>
<input type="range" min="0" max="1" step="0.025" bind:this={audioPlayerVol} on:change={setVolume}>
</div>
<div class="audio-manager-reduce-conversation">
<label>
@ -135,7 +142,7 @@
margin-left: auto;
margin-right: auto;
background-color: rgb(0,0,0,0.5);
background-color: rgb(0, 0, 0, 0.5);
display: grid;
grid-template-rows: 50% 50%;
color: whitesmoke;

View file

@ -3,7 +3,7 @@
import { chatMessagesStore, chatVisibilityStore } from "../../Stores/ChatStore";
import ChatMessageForm from './ChatMessageForm.svelte';
import ChatElement from './ChatElement.svelte';
import {afterUpdate, beforeUpdate} from "svelte";
import {afterUpdate, beforeUpdate, onMount} from "svelte";
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
let listDom: HTMLElement;
@ -15,6 +15,10 @@
autoscroll = listDom && (listDom.offsetHeight + listDom.scrollTop) > (listDom.scrollHeight - 20);
});
onMount(() => {
listDom.scrollTo(0, listDom.scrollHeight);
})
afterUpdate(() => {
if (autoscroll) listDom.scrollTo(0, listDom.scrollHeight);
});

View file

@ -22,9 +22,9 @@
<style lang="scss">
.menuIcon {
pointer-events: auto;
margin: 25px;
img {
pointer-events: auto;
width: 60px;
padding-top: 0;
}

View file

@ -3,6 +3,7 @@ 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";
let fullscreen : boolean = localUserStore.getFullscreen();
let notification : boolean = localUserStore.getNotification() === 'granted';
@ -22,6 +23,8 @@ function saveSetting(){
previewValueVideo = valueVideo;
videoConstraintStore.setFrameRate(valueVideo);
}
closeMenu();
}
function changeFullscreen() {
@ -50,6 +53,10 @@ function changeNotification() {
})
}
}
function closeMenu() {
menuVisiblilityStore.set(false);
}
</script>
<div class="settings-main" on:submit|preventDefault={saveSetting}>

View file

@ -9,6 +9,7 @@ import { Room } from "./Room";
import { _ServiceWorker } from "../Network/ServiceWorker";
import { loginSceneVisibleIframeStore } from "../Stores/LoginSceneStore";
import { userIsConnected } from "../Stores/MenuStore";
import { analyticsClient } from "../Administration/AnalyticsClient";
class ConnectionManager {
private localUser!: LocalUser;
@ -93,6 +94,7 @@ class ConnectionManager {
this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
try {
await this.checkAuthUserConnexion();
analyticsClient.loggedWithSso();
} catch (err) {
console.error(err);
this.loadOpenIDScreen();
@ -109,6 +111,7 @@ class ConnectionManager {
this.authToken = data.authToken;
localUserStore.saveUser(this.localUser);
localUserStore.setAuthToken(this.authToken);
analyticsClient.loggedWithToken();
const roomUrl = data.roomUrl;
@ -184,6 +187,9 @@ class ConnectionManager {
if (this._currentRoom == undefined) {
return Promise.reject(new Error("Invalid URL"));
}
if (this.localUser) {
analyticsClient.identifyUser(this.localUser.uuid);
}
this.serviceWorker = new _ServiceWorker();
return Promise.resolve(this._currentRoom);

View file

@ -20,6 +20,8 @@ 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 isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;

View file

@ -286,4 +286,15 @@ export class GameMap {
this.triggerAll();
}
/**
* Trigger all the callbacks (used when exiting a map)
*/
public triggerExitCallbacks(): void {
const emptyProps = new Map<string, string | boolean | number>();
for (const [oldPropName, oldPropValue] of this.lastProperties.entries()) {
// We found a property that disappeared
this.trigger(oldPropName, oldPropValue, undefined, emptyProps);
}
}
}

View file

@ -13,9 +13,27 @@ export class GameMapPropertiesListener {
constructor(private scene: GameScene, private gameMap: GameMap) {}
register() {
this.gameMap.onPropertyChange("openTab", (newValue) => {
this.gameMap.onPropertyChange("openTab", (newValue, oldvalue, allProps) => {
if (newValue === undefined) {
layoutManagerActionStore.removeAction("openTab");
}
if (typeof newValue == "string" && newValue.length) {
scriptUtils.openTab(newValue);
const openWebsiteTriggerValue = allProps.get(TRIGGER_WEBSITE_PROPERTIES);
if (openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES);
if (message === undefined) {
message = "Press SPACE or touch here to open web site in new tab";
}
layoutManagerActionStore.addAction({
uuid: "openTab",
type: "message",
message: message,
callback: () => scriptUtils.openTab(newValue),
userInputManager: this.scene.userInputManager,
});
} else {
scriptUtils.openTab(newValue);
}
}
});
this.gameMap.onPropertyChange("openWebsite", (newValue, oldValue, allProps) => {

View file

@ -94,6 +94,7 @@ import { userIsAdminStore } from "../../Stores/GameStore";
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager";
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
import { analyticsClient } from "../../Administration/AnalyticsClient";
import { get } from "svelte/store";
export interface GameSceneInitInterface {
@ -429,6 +430,7 @@ export class GameScene extends DirtyScene {
gameManager.gameSceneIsCreated(this);
urlManager.pushRoomIdToUrl(this.room);
analyticsClient.enteredRoom(this.room.id);
if (touchScreenManager.supportTouchScreen) {
this.pinchManager = new PinchManager(this);
@ -1255,6 +1257,8 @@ ${escapedMessage}
if (this.mapTransitioning) return;
this.mapTransitioning = true;
this.gameMap.triggerExitCallbacks();
let targetRoom: Room;
try {
targetRoom = await Room.createRoom(roomUrl);
@ -1463,6 +1467,7 @@ ${escapedMessage}
});
this.CurrentPlayer.on(requestEmoteEventName, (emoteKey: string) => {
this.connection?.emitEmoteEvent(emoteKey);
analyticsClient.launchEmote(emoteKey);
});
} catch (err) {
if (err instanceof TextureError) {
@ -1830,19 +1835,21 @@ ${escapedMessage}
jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl, jitsiWidth);
this.connection?.setSilent(true);
mediaManager.hideGameOverlay();
analyticsClient.enteredJitsi(roomName, this.room.id);
//permit to stop jitsi when user close iframe
mediaManager.addTriggerCloseJitsiFrameButton("close-jisi", () => {
mediaManager.addTriggerCloseJitsiFrameButton("close-jitsi", () => {
this.stopJitsi();
});
}
public stopJitsi(): void {
this.connection?.setSilent(false);
const silent = this.gameMap.getCurrentProperties().get("silent");
this.connection?.setSilent(!!silent);
jitsiFactory.stop();
mediaManager.showGameOverlay();
mediaManager.removeTriggerCloseJitsiFrameButton("close-jisi");
mediaManager.removeTriggerCloseJitsiFrameButton("close-jitsi");
}
//todo: put this into an 'orchestrator' scene (EntryScene?)

View file

@ -2,11 +2,14 @@ import { get, writable } from "svelte/store";
import Timeout = NodeJS.Timeout;
import { userIsAdminStore } from "./GameStore";
import { CONTACT_URL } from "../Enum/EnvironmentVariable";
import { analyticsClient } from "../Administration/AnalyticsClient";
export const menuIconVisiblilityStore = writable(false);
export const menuVisiblilityStore = writable(false);
menuVisiblilityStore.subscribe((value) => {
if (value) analyticsClient.openedMenu();
});
export const menuInputFocusStore = writable(false);
export const loginUrlStore = writable(false);
export const userIsConnected = writable(false);
let warningContainerTimeout: Timeout | null = null;

View file

@ -1,14 +0,0 @@
import { iframeListener } from "../Api/IframeListener";
import { chatMessagesStore } from "../Stores/ChatStore";
import { playersStore } from "../Stores/PlayersStore";
export class DiscussionManager {
constructor() {
iframeListener.chatStream.subscribe((chatEvent) => {
const userId = playersStore.addFacticePlayer(chatEvent.author);
chatMessagesStore.addExternalMessage(userId, chatEvent.message);
});
}
}
export const discussionManager = new DiscussionManager();

View file

@ -5,11 +5,7 @@ import { blackListManager } from "./BlackListManager";
import type { Subscription } from "rxjs";
import type { UserSimplePeerInterface } from "./SimplePeer";
import { readable, Readable, Unsubscriber } from "svelte/store";
import {
localStreamStore,
obtainedMediaConstraintStore,
ObtainedMediaStreamConstraints,
} from "../Stores/MediaStore";
import { localStreamStore, obtainedMediaConstraintStore, ObtainedMediaStreamConstraints } from "../Stores/MediaStore";
import { playersStore } from "../Stores/PlayersStore";
import { chatMessagesStore, newChatMessageStore } from "../Stores/ChatStore";
import { getIceServersConfig } from "../Components/Video/utils";

View file

@ -23,9 +23,9 @@ import { Game } from "./Phaser/Game/Game";
import App from "./Components/App.svelte";
import { HtmlUtils } from "./WebRtc/HtmlUtils";
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
import { analyticsClient } from "./Administration/AnalyticsClient";
const { width, height } = coWebsiteManager.getGameSize();
const valueGameQuality = localUserStore.getGameQualityValue();
const fps: Phaser.Types.Core.FPSConfig = {
/**