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);
+ }
}
/**