diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html index eeb8bb5b..55adcebc 100644 --- a/front/dist/index.tmpl.html +++ b/front/dist/index.tmpl.html @@ -92,12 +92,12 @@
- +
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 05ff5a03..fda0c9dd 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -363,10 +363,14 @@ body { justify-content: center; } +.audioplayer > div { + padding-right: 1.2rem; +} + #audioplayerctrl { position: fixed; top: 0; - right: 50%; + right: calc(50% - 120px); padding: 0.3rem 0.5rem; color: white; transition: transform 0.5s; diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 8ac8c7b2..1e4267de 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -1,12 +1,15 @@ import {LocalUser} from "./LocalUser"; -const characterLayersKey = 'characterLayers'; -const gameQualityKey = 'gameQuality'; -const videoQualityKey = 'videoQuality'; +const playerNameKey = 'playerName'; +const selectedPlayerKey = 'selectedPlayer'; +const customCursorPositionKey = 'customCursorPosition'; +const characterLayersKey = 'characterLayers'; +const gameQualityKey = 'gameQuality'; +const videoQualityKey = 'videoQuality'; +const audioPlayerVolumeKey = 'audioVolume'; +const audioPlayerMuteKey = 'audioMute'; -//todo: add localstorage fallback class LocalUserStore { - saveUser(localUser: LocalUser) { localStorage.setItem('localUser', JSON.stringify(localUser)); } @@ -14,48 +17,62 @@ class LocalUserStore { const data = localStorage.getItem('localUser'); return data ? JSON.parse(data) : null; } - + setName(name:string): void { - window.localStorage.setItem('playerName', name); + localStorage.setItem(playerNameKey, name); } getName(): string { - return window.localStorage.getItem('playerName') ?? ''; + return localStorage.getItem(playerNameKey) || ''; } setPlayerCharacterIndex(playerCharacterIndex: number): void { - window.localStorage.setItem('selectedPlayer', ''+playerCharacterIndex); + localStorage.setItem(selectedPlayerKey, ''+playerCharacterIndex); } getPlayerCharacterIndex(): number { - return parseInt(window.localStorage.getItem('selectedPlayer') || ''); + return parseInt(localStorage.getItem(selectedPlayerKey) || ''); } setCustomCursorPosition(activeRow:number, selectedLayers: number[]): void { - window.localStorage.setItem('customCursorPosition', JSON.stringify({activeRow, selectedLayers})); + localStorage.setItem(customCursorPositionKey, JSON.stringify({activeRow, selectedLayers})); } getCustomCursorPosition(): {activeRow:number, selectedLayers:number[]}|null { - return JSON.parse(window.localStorage.getItem('customCursorPosition') || "null"); + return JSON.parse(localStorage.getItem(customCursorPositionKey) || "null"); } setCharacterLayers(layers: string[]): void { - window.localStorage.setItem(characterLayersKey, JSON.stringify(layers)); + localStorage.setItem(characterLayersKey, JSON.stringify(layers)); } getCharacterLayers(): string[]|null { - return JSON.parse(window.localStorage.getItem(characterLayersKey) || "null"); - } - - getGameQualityValue(): number { - return parseInt(window.localStorage.getItem(gameQualityKey) || '') || 60; + return JSON.parse(localStorage.getItem(characterLayersKey) || "null"); } + setGameQualityValue(value: number): void { localStorage.setItem(gameQualityKey, '' + value); } - - getVideoQualityValue(): number { - return parseInt(window.localStorage.getItem(videoQualityKey) || '') || 20; + getGameQualityValue(): number { + return parseInt(localStorage.getItem(gameQualityKey) || '60'); } + setVideoQualityValue(value: number): void { localStorage.setItem(videoQualityKey, '' + value); } + getVideoQualityValue(): number { + return parseInt(localStorage.getItem(videoQualityKey) || '20'); + } + + setAudioPlayerVolume(value: number): void { + localStorage.setItem(audioPlayerVolumeKey, '' + value); + } + getAudioPlayerVolume(): number { + return parseFloat(localStorage.getItem(audioPlayerVolumeKey) || '1'); + } + + setAudioPlayerMuted(value: boolean): void { + localStorage.setItem(audioPlayerMuteKey, value.toString()); + } + getAudioPlayerMuted(): boolean { + return localStorage.getItem(audioPlayerMuteKey) === 'true'; + } } -export const localUserStore = new LocalUserStore(); \ No newline at end of file +export const localUserStore = new LocalUserStore(); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 826bf5ae..9321dbce 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -29,7 +29,9 @@ import { ON_ACTION_TRIGGER_BUTTON, TRIGGER_JITSI_PROPERTIES, TRIGGER_WEBSITE_PROPERTIES, - WEBSITE_MESSAGE_PROPERTIES + WEBSITE_MESSAGE_PROPERTIES, + AUDIO_VOLUME_PROPERTY, + AUDIO_LOOP_PROPERTY } from "../../WebRtc/LayoutManager"; import {GameMap} from "./GameMap"; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; @@ -455,16 +457,12 @@ export class GameScene extends ResizableScene implements CenterListener { }); this.connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { - audioManager.decreaseVolume(); this.shareGroupPosition(groupPositionMessage); - this.openChatIcon.setVisible(true); }) this.connection.onGroupDeleted((groupId: number) => { - audioManager.restoreVolume(); try { this.deleteGroup(groupId); - this.openChatIcon.setVisible(false); } catch (e) { console.error(e); } @@ -517,11 +515,15 @@ export class GameScene extends ResizableScene implements CenterListener { onConnect(user: UserSimplePeerInterface) { self.presentationModeSprite.setVisible(true); self.chatModeSprite.setVisible(true); + self.openChatIcon.setVisible(true); + audioManager.decreaseVolume(); }, onDisconnect(userId: number) { if (self.simplePeer.getNbConnections() === 0) { self.presentationModeSprite.setVisible(false); self.chatModeSprite.setVisible(false); + self.openChatIcon.setVisible(false); + audioManager.restoreVolume(); } } }) @@ -662,14 +664,15 @@ export class GameScene extends ResizableScene implements CenterListener { this.connection.setSilent(true); } }); - this.gameMap.onPropertyChange('playAudio', (newValue, oldValue) => { - newValue === undefined ? audioManager.unloadAudio() : audioManager.playAudio(newValue, this.getMapDirUrl()); + this.gameMap.onPropertyChange('playAudio', (newValue, oldValue, allProps) => { + const volume = allProps.get(AUDIO_VOLUME_PROPERTY) as number|undefined; + const loop = allProps.get(AUDIO_LOOP_PROPERTY) as boolean|undefined; + newValue === undefined ? audioManager.unloadAudio() : audioManager.playAudio(newValue, this.getMapDirUrl(), volume, loop); }); - + // TODO: This legacy property should be removed at some point this.gameMap.onPropertyChange('playAudioLoop', (newValue, oldValue) => { - newValue === undefined ? audioManager.unloadAudio() : audioManager.playAudio(newValue, this.getMapDirUrl()); + newValue === undefined ? audioManager.unloadAudio() : audioManager.playAudio(newValue, this.getMapDirUrl(), undefined, true); }); - } private getMapDirUrl(): string { diff --git a/front/src/WebRtc/AudioManager.ts b/front/src/WebRtc/AudioManager.ts index 24fb74a1..60255a77 100644 --- a/front/src/WebRtc/AudioManager.ts +++ b/front/src/WebRtc/AudioManager.ts @@ -1,5 +1,6 @@ import {HtmlUtils} from "./HtmlUtils"; import {isUndefined} from "generic-type-guard"; +import {localUserStore} from "../Connexion/LocalUserStore"; enum audioStates { closed = 0, @@ -9,6 +10,8 @@ enum audioStates { const audioPlayerDivId = "audioplayer"; const audioPlayerCtrlId = "audioplayerctrl"; +const audioPlayerVolId = "audioplayer_volume"; +const audioPlayerMuteId = "audioplayer_volume_icon_playing"; const animationTime = 500; class AudioManager { @@ -17,6 +20,8 @@ class AudioManager { private audioPlayerDiv: HTMLDivElement; private audioPlayerCtrl: HTMLDivElement; private audioPlayerElem: HTMLAudioElement | undefined; + private audioPlayerVol: HTMLInputElement; + private audioPlayerMute: HTMLInputElement; private volume = 1; private muted = false; @@ -26,19 +31,19 @@ class AudioManager { constructor() { this.audioPlayerDiv = HtmlUtils.getElementByIdOrFail(audioPlayerDivId); this.audioPlayerCtrl = HtmlUtils.getElementByIdOrFail(audioPlayerCtrlId); + this.audioPlayerVol = HtmlUtils.getElementByIdOrFail(audioPlayerVolId); + this.audioPlayerMute = HtmlUtils.getElementByIdOrFail(audioPlayerMuteId); - const storedVolume = localStorage.getItem('volume') - if (storedVolume === null) { - this.setVolume(1); - } else { - this.volume = parseFloat(storedVolume); - HtmlUtils.getElementByIdOrFail('audioplayer_volume').value = storedVolume; + this.volume = localUserStore.getAudioPlayerVolume(); + this.audioPlayerVol.value = '' + this.volume; + + this.muted = localUserStore.getAudioPlayerMuted(); + if (this.muted) { + this.audioPlayerMute.classList.add('muted'); } - - HtmlUtils.getElementByIdOrFail('audioplayer_volume').value = '' + this.volume; } - public playAudio(url: string|number|boolean, mapDirUrl: string, loop=false): void { + public playAudio(url: string|number|boolean, mapDirUrl: string, volume: number|undefined, loop=false): void { const audioPath = url as string; let realAudioPath = ''; @@ -50,7 +55,7 @@ class AudioManager { realAudioPath = mapDirUrl + '/' + url; } - this.loadAudio(realAudioPath); + this.loadAudio(realAudioPath, volume); if (loop) { this.loop(); @@ -75,26 +80,29 @@ class AudioManager { } private changeVolume(talking = false): void { - if (!isUndefined(this.audioPlayerElem)) { - this.audioPlayerElem.volume = this.naturalVolume(talking && this.decreaseWhileTalking); - this.audioPlayerElem.muted = this.muted; + if (isUndefined(this.audioPlayerElem)) { + return; } - } - private naturalVolume(makeSofter: boolean = false): number { - const volume = this.volume - const retVol = makeSofter && !this.volumeReduced ? Math.pow(volume * 0.5, 3) : volume - this.volumeReduced = makeSofter - return retVol; + const reduceVolume = talking && this.decreaseWhileTalking; + if (reduceVolume && !this.volumeReduced) { + this.volume *= 0.5; + } else if (!reduceVolume && this.volumeReduced) { + this.volume *= 2.0; + } + this.volumeReduced = reduceVolume; + + this.audioPlayerElem.volume = this.volume; + this.audioPlayerVol.value = '' + this.volume; + this.audioPlayerElem.muted = this.muted; } private setVolume(volume: number): void { this.volume = volume; - localStorage.setItem('volume', '' + volume); + localUserStore.setAudioPlayerVolume(volume); } - - private loadAudio(url: string): void { + private loadAudio(url: string, volume: number|undefined): void { this.load(); /* Solution 1, remove whole audio player */ @@ -112,23 +120,24 @@ class AudioManager { this.audioPlayerElem.append(srcElem); this.audioPlayerDiv.append(this.audioPlayerElem); + this.volume = volume ? Math.min(volume, this.volume) : this.volume; this.changeVolume(); this.audioPlayerElem.play(); const muteElem = HtmlUtils.getElementByIdOrFail('audioplayer_mute'); - muteElem.onclick = (ev: Event)=> { + muteElem.onclick = (ev: Event) => { this.muted = !this.muted; this.changeVolume(); + localUserStore.setAudioPlayerMuted(this.muted); if (this.muted) { - HtmlUtils.getElementByIdOrFail('audioplayer_volume_icon_playing').classList.add('muted'); + this.audioPlayerMute.classList.add('muted'); } else { - HtmlUtils.getElementByIdOrFail('audioplayer_volume_icon_playing').classList.remove('muted'); + this.audioPlayerMute.classList.remove('muted'); } } - const volumeElem = HtmlUtils.getElementByIdOrFail('audioplayer_volume'); - volumeElem.oninput = (ev: Event)=> { + this.audioPlayerVol.oninput = (ev: Event)=> { this.setVolume(parseFloat((ev.currentTarget).value)); this.changeVolume(); diff --git a/front/src/WebRtc/LayoutManager.ts b/front/src/WebRtc/LayoutManager.ts index 91d78798..233b5327 100644 --- a/front/src/WebRtc/LayoutManager.ts +++ b/front/src/WebRtc/LayoutManager.ts @@ -31,6 +31,9 @@ export const TRIGGER_JITSI_PROPERTIES = 'jitsiTrigger'; export const WEBSITE_MESSAGE_PROPERTIES = 'openWebsiteTriggerMessage'; export const JITSI_MESSAGE_PROPERTIES = 'jitsiTriggerMessage'; +export const AUDIO_VOLUME_PROPERTY = 'audioVolume'; +export const AUDIO_LOOP_PROPERTY = 'audioLoop'; + /** * This class is in charge of the video-conference layout. * It receives positioning requests for videos and does its best to place them on the screen depending on the active layout mode. diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 2baeef11..73a87d14 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -63,7 +63,7 @@ export class SimplePeer { } public getNbConnections(): number { - return this.PeerConnectionArray.size; + return this.Users.length; } /** @@ -230,9 +230,6 @@ export class SimplePeer { this.closeScreenSharingConnection(userId); - for (const peerConnectionListener of this.peerConnectionListeners) { - peerConnectionListener.onDisconnect(userId); - } const userIndex = this.Users.findIndex(user => user.userId === userId); if(userIndex < 0){ throw 'Couln\'t delete user'; @@ -250,6 +247,10 @@ export class SimplePeer { this.PeerScreenSharingConnectionArray.delete(userId); } } + + for (const peerConnectionListener of this.peerConnectionListeners) { + peerConnectionListener.onDisconnect(userId); + } } /**