This commit is contained in:
_Bastler 2021-05-11 20:18:12 +02:00
commit e9baef5963
10 changed files with 164 additions and 120 deletions

View File

@ -2,7 +2,7 @@ name: Build, push and deploy Docker image
on:
push:
branch: [master]
branches: [master]
release:
types: [created]
pull_request:
@ -16,7 +16,7 @@ env:
jobs:
build-front:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest
steps:
@ -36,11 +36,11 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-front
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true
build-back:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest
steps:
@ -59,11 +59,11 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-back
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true
build-pusher:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest
steps:
@ -82,11 +82,11 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-pusher
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true
build-uploader:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest
steps:
@ -105,11 +105,11 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-uploader
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true
build-maps:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest
steps:
@ -129,7 +129,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-maps
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }}
tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true
deeploy:
@ -140,7 +140,7 @@ jobs:
- build-maps
- build-uploader
runs-on: ubuntu-latest
if: ${{ github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }}
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
steps:
- name: Checkout
@ -158,13 +158,13 @@ jobs:
JITSI_URL: ${{ secrets.JITSI_URL }}
SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }}
TURN_STATIC_AUTH_SECRET: ${{ secrets.TURN_STATIC_AUTH_SECRET }}
DEPLOY_REF: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || 'master' }}
DEPLOY_REF: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
with:
namespace: workadventure-${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || 'master' }}
namespace: workadventure-${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
- name: Add a comment in PR
uses: unsplash/comment-on-pr@v1.2.0
if: ${{ github.event.pull_request }}
if: ${{ github.event_name == 'pull_request' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:

View File

@ -2,7 +2,7 @@
local env = std.extVar("env"),
local namespace = env.DEPLOY_REF,
local tag = namespace,
local url = if namespace == "master" then "workadventu.re" else namespace+".test.workadventu.re",
local url = namespace+".test.workadventu.re",
// develop branch does not use admin because of issue with SSL certificate of admin as of now.
local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://"+url else null,
"$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json",
@ -25,10 +25,7 @@
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
} + (if adminUrl != null then {
"ADMIN_API_URL": adminUrl,
} else {}) + if namespace != "master" then {
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
} else {})
},
"back2": {
"image": "thecodingmachine/workadventure-back:"+tag,
@ -47,10 +44,7 @@
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
} + (if adminUrl != null then {
"ADMIN_API_URL": adminUrl,
} else {}) + if namespace != "master" then {
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
} else {})
},
"pusher": {
"replicas": 2,
@ -69,10 +63,7 @@
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
} + (if adminUrl != null then {
"ADMIN_API_URL": adminUrl,
} else {}) + if namespace != "master" then {
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
} else {})
},
"front": {
"image": "thecodingmachine/workadventure-front:"+tag,

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -17,7 +17,6 @@ export class SoundMeter {
}
private init(context: AudioContext) {
if (this.context === undefined) {
this.context = context;
this.analyser = this.context.createAnalyser();
@ -25,7 +24,6 @@ export class SoundMeter {
const bufferLength = this.analyser.fftSize;
this.dataArray = new Uint8Array(bufferLength);
}
}
public connectToSource(stream: MediaStream, context: AudioContext): void
{

View File

@ -2,6 +2,7 @@ import {ResizableScene} from "../Login/ResizableScene";
import GameObject = Phaser.GameObjects.GameObject;
import Events = Phaser.Scenes.Events;
import AnimationEvents = Phaser.Animations.Events;
import StructEvents = Phaser.Structs.Events;
/**
* A scene that can track its dirty/pristine state.
@ -23,12 +24,11 @@ export abstract class DirtyScene extends ResizableScene {
}
this.isAlreadyTracking = true;
const trackAnimationFunction = this.trackAnimation.bind(this);
this.events.on(Events.ADDED_TO_SCENE, (gameObject: GameObject) => {
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_ADD, (gameObject: GameObject) => {
this.objectListChanged = true;
gameObject.on(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
});
this.events.on(Events.REMOVED_FROM_SCENE, (gameObject: GameObject) => {
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_REMOVE, (gameObject: GameObject) => {
this.objectListChanged = true;
gameObject.removeListener(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
});

View File

@ -187,6 +187,7 @@ export class GameScene extends DirtyScene implements CenterListener {
private originalMapUrl: string|undefined;
private pinchManager: PinchManager|undefined;
private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time.
private onVisibilityChangeCallback: () => void;
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
super({
@ -202,10 +203,11 @@ export class GameScene extends DirtyScene implements CenterListener {
this.createPromise = new Promise<void>((resolve, reject): void => {
this.createPromiseResolve = resolve;
})
});
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
this.connectionAnswerPromiseResolve = resolve;
});
this.onVisibilityChangeCallback = this.onVisibilityChange.bind(this);
}
//hook preload scene
@ -504,6 +506,8 @@ export class GameScene extends DirtyScene implements CenterListener {
if (!this.room.isDisconnected()) {
this.connect();
}
document.addEventListener('visibilitychange', this.onVisibilityChangeCallback);
}
/**
@ -625,6 +629,7 @@ export class GameScene extends DirtyScene implements CenterListener {
self.chatModeSprite.setVisible(false);
self.openChatIcon.setVisible(false);
audioManager.restoreVolume();
self.onVisibilityChange();
}
}
})
@ -947,6 +952,8 @@ ${escapedMessage}
for(const iframeEvents of this.iframeSubscriptionList){
iframeEvents.unsubscribe();
}
document.removeEventListener('visibilitychange', this.onVisibilityChangeCallback);
}
private removeAllRemotePlayers(): void {
@ -1511,6 +1518,8 @@ ${escapedMessage}
mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => {
this.stopJitsi();
});
this.onVisibilityChange();
}
public stopJitsi(): void {
@ -1546,6 +1555,7 @@ ${escapedMessage}
openJitsiRoomFunction();
}, this.userInputManager);
}
this.onVisibilityChange();
}
//todo: put this into an 'orchestrator' scene (EntryScene?)
@ -1585,4 +1595,20 @@ ${escapedMessage}
waScaleManager.zoomModifier *= zoomFactor;
this.updateCameraOffset();
}
private onVisibilityChange(): void {
// If the overlay is not displayed, we are in Jitsi. We don't need the webcam.
if (!mediaManager.isGameOverlayVisible()) {
mediaManager.blurCamera();
return;
}
if (document.visibilityState === 'visible') {
mediaManager.focusCamera();
} else {
if (this.simplePeer.getNbConnections() === 0) {
mediaManager.blurCamera();
}
}
}
}

View File

@ -252,7 +252,6 @@ export class EnableCameraScene extends ResizableScene {
update(time: number, delta: number): void {
this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
mediaManager.updateScene();
this.centerXDomElement(this.enableCameraSceneElement, 300);
}

View File

@ -10,9 +10,10 @@ interface jitsiConfigInterface {
}
const getDefaultConfig = () : jitsiConfigInterface => {
const constraints = mediaManager.getConstraintRequestedByUser();
return {
startWithAudioMuted: !mediaManager.constraintsMedia.audio,
startWithVideoMuted: mediaManager.constraintsMedia.video === false,
startWithAudioMuted: !constraints.audio,
startWithVideoMuted: constraints.video === false,
prejoinPageEnabled: false
}
}
@ -71,7 +72,7 @@ class JitsiFactory {
private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any
private audioCallback = this.onAudioChange.bind(this);
private videoCallback = this.onVideoChange.bind(this);
private previousConfigMeet? : jitsiConfigInterface;
private previousConfigMeet! : jitsiConfigInterface;
private jitsiScriptLoaded: boolean = false;
/**
@ -136,32 +137,24 @@ class JitsiFactory {
//restore previous config
if(this.previousConfigMeet?.startWithAudioMuted){
mediaManager.disableMicrophone();
await mediaManager.disableMicrophone();
}else{
mediaManager.enableMicrophone();
await mediaManager.enableMicrophone();
}
if(this.previousConfigMeet?.startWithVideoMuted){
mediaManager.disableCamera();
await mediaManager.disableCamera();
}else{
mediaManager.enableCamera();
await mediaManager.enableCamera();
}
}
private onAudioChange({muted}: {muted: boolean}): void {
if (muted && mediaManager.constraintsMedia.audio === true) {
mediaManager.disableMicrophone();
} else if(!muted && mediaManager.constraintsMedia.audio === false) {
mediaManager.enableMicrophone();
}
this.previousConfigMeet.startWithAudioMuted = muted;
}
private onVideoChange({muted}: {muted: boolean}): void {
if (muted && mediaManager.constraintsMedia.video !== false) {
mediaManager.disableCamera();
} else if(!muted && mediaManager.constraintsMedia.video === false) {
mediaManager.enableCamera();
}
this.previousConfigMeet.startWithVideoMuted = muted;
}
private async loadJitsiScript(domain: string): Promise<void> {

View File

@ -20,7 +20,7 @@ const audioConstraint: boolean|MediaTrackConstraints = {
//TODO: make these values configurable in the game settings menu and store them in localstorage
autoGainControl: false,
echoCancellation: true,
noiseSuppression: false
noiseSuppression: true
};
export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
@ -43,7 +43,8 @@ export class MediaManager {
microphoneClose: HTMLImageElement;
microphone: HTMLImageElement;
webrtcInAudio: HTMLAudioElement;
mySoundMeterElement: HTMLDivElement;
//FIX ME SOUNDMETER: check stalability of sound meter calculation
//mySoundMeterElement: HTMLDivElement;
private webrtcOutAudio: HTMLAudioElement;
constraintsMedia : MediaStreamConstraints = {
audio: audioConstraint,
@ -62,18 +63,16 @@ export class MediaManager {
private previousConstraint : MediaStreamConstraints;
private focused : boolean = true;
private lastUpdateScene : Date = new Date();
private setTimeOutlastUpdateScene? : NodeJS.Timeout;
private hasCamera = true;
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
private userInputManager?: UserInputManager;
private mySoundMeter?: SoundMeter|null;
//FIX ME SOUNDMETER: check stalability of sound meter calculation
/*private mySoundMeter?: SoundMeter|null;
private soundMeters: Map<string, SoundMeter> = new Map<string, SoundMeter>();
private soundMeterElements: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>();
private soundMeterElements: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>();*/
constructor() {
@ -132,17 +131,19 @@ export class MediaManager {
this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
this.pingCameraStatus();
this.checkActiveUser(); //todo: desactivated in case of bug
this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter'));
//FIX ME SOUNDMETER: check stalability of sound meter calculation
/*this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter'));
this.mySoundMeterElement.childNodes.forEach((value: ChildNode, index) => {
this.mySoundMeterElement.children.item(index)?.classList.remove('active');
});
});*/
//Check of ask notification navigator permission
this.getNotification();
}
public updateScene(){
this.lastUpdateScene = new Date();
this.updateSoudMeter();
//FIX ME SOUNDMETER: check stalability of sound meter calculation
//this.updateSoudMeter();
}
public blurCamera() {
@ -154,6 +155,13 @@ export class MediaManager {
this.disableCamera();
}
/**
* Returns the constraint that the user wants (independently of the visibility / jitsi state...)
*/
public getConstraintRequestedByUser(): MediaStreamConstraints {
return this.previousConstraint ?? this.constraintsMedia;
}
public focusCamera() {
if(this.focused){
return;
@ -196,7 +204,7 @@ export class MediaManager {
}
}
public showGameOverlay(){
public showGameOverlay(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.add('active');
@ -207,7 +215,7 @@ export class MediaManager {
buttonCloseFrame.removeEventListener('click', functionTrigger);
}
public hideGameOverlay(){
public hideGameOverlay(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.remove('active');
@ -218,6 +226,11 @@ export class MediaManager {
buttonCloseFrame.addEventListener('click', functionTrigger);
}
public isGameOverlayVisible(): boolean {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
return gameOverlay.classList.contains('active');
}
public updateCameraQuality(value: number) {
this.enableCameraStyle();
const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint));
@ -229,29 +242,32 @@ export class MediaManager {
});
}
public enableCamera() {
public async enableCamera() {
this.constraintsMedia.video = videoConstraint;
this.getCamera().then((stream: MediaStream) => {
try {
const stream = await this.getCamera()
//TODO show error message tooltip upper of camera button
//TODO message : please check camera permission of your navigator
if(stream.getVideoTracks().length === 0) {
throw Error('Video track is empty, please check camera permission of your navigator')
throw new Error('Video track is empty, please check camera permission of your navigator')
}
this.enableCameraStyle();
this.triggerUpdatedLocalStreamCallbacks(stream);
}).catch((err) => {
} catch(err) {
console.error(err);
this.disableCameraStyle();
this.stopCamera();
layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => {
this.showHelpCameraSettingsCallBack();
}, this.userInputManager);
});
}
}
public async disableCamera() {
this.disableCameraStyle();
this.stopCamera();
if (this.constraintsMedia.audio !== false) {
const stream = await this.getCamera();
@ -261,10 +277,12 @@ export class MediaManager {
}
}
public enableMicrophone() {
public async enableMicrophone() {
this.constraintsMedia.audio = audioConstraint;
this.getCamera().then((stream) => {
try {
const stream = await this.getCamera();
//TODO show error message tooltip upper of camera button
//TODO message : please check microphone permission of your navigator
if (stream.getAudioTracks().length === 0) {
@ -272,14 +290,14 @@ export class MediaManager {
}
this.enableMicrophoneStyle();
this.triggerUpdatedLocalStreamCallbacks(stream);
}).catch((err) => {
} catch(err) {
console.error(err);
this.disableMicrophoneStyle();
layoutManager.addInformation('warning', 'Microphone access denied. Click here and check navigators permissions.', () => {
this.showHelpCameraSettingsCallBack();
}, this.userInputManager);
});
}
}
public async disableMicrophone() {
@ -324,7 +342,6 @@ export class MediaManager {
this.cinemaBtn.classList.add("disabled");
this.constraintsMedia.video = false;
this.myCamVideo.srcObject = null;
this.stopCamera();
}
private enableMicrophoneStyle(){
@ -435,6 +452,8 @@ export class MediaManager {
return this.getLocalStream().catch((err) => {
console.info('Error get camera, trying with video option at null =>', err);
this.disableCameraStyle();
this.stopCamera();
return this.getLocalStream().then((stream : MediaStream) => {
this.hasCamera = false;
return stream;
@ -457,12 +476,12 @@ export class MediaManager {
this.localStream = stream;
this.myCamVideo.srcObject = this.localStream;
//init sound meter
this.mySoundMeter = null;
//FIX ME SOUNDMETER: check stalability of sound meter calculation
/*this.mySoundMeter = null;
if(this.constraintsMedia.audio){
this.mySoundMeter = new SoundMeter();
this.mySoundMeter.connectToSource(stream, new AudioContext());
}
}*/
return stream;
}).catch((err: Error) => {
throw err;
@ -489,7 +508,7 @@ export class MediaManager {
track.stop();
}
}
this.mySoundMeter?.stop();
//this.mySoundMeter?.stop();
}
setCamera(id: string): Promise<MediaStream> {
@ -632,11 +651,12 @@ export class MediaManager {
}
remoteVideo.srcObject = stream;
//FIX ME SOUNDMETER: check stalability of sound meter calculation
//sound metter
const soundMeter = new SoundMeter();
/*const soundMeter = new SoundMeter();
soundMeter.connectToSource(stream, new AudioContext());
this.soundMeters.set(userId, soundMeter);
this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail<HTMLImageElement>('soundMeter-'+userId));
this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail<HTMLImageElement>('soundMeter-'+userId));*/
}
addStreamRemoteScreenSharing(userId: string, stream : MediaStream){
// In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet
@ -652,9 +672,10 @@ export class MediaManager {
layoutManager.remove(userId);
this.remoteVideo.delete(userId);
this.soundMeters.get(userId)?.stop();
//FIX ME SOUNDMETER: check stalability of sound meter calculation
/*this.soundMeters.get(userId)?.stop();
this.soundMeters.delete(userId);
this.soundMeterElements.delete(userId);
this.soundMeterElements.delete(userId);*/
//permit to remove user in discussion part
this.removeParticipant(userId);
@ -776,22 +797,6 @@ export class MediaManager {
this.userInputManager = userInputManager;
discussionManager.setUserInputManager(userInputManager);
}
//check if user is active
private checkActiveUser(){
if(this.setTimeOutlastUpdateScene){
clearTimeout(this.setTimeOutlastUpdateScene);
}
this.setTimeOutlastUpdateScene = setTimeout(() => {
const now = new Date();
//if last update is more of 10 sec
if( (now.getTime() - this.lastUpdateScene.getTime()) > 10000) {
this.blurCamera();
}else{
this.focusCamera();
}
this.checkActiveUser();
}, this.focused ? 10000 : 1000);
}
public setShowReportModalCallBacks(callback: ShowReportCallBack){
this.showReportModalCallBacks.add(callback);
@ -807,7 +812,8 @@ export class MediaManager {
}
}
updateSoudMeter(){
//FIX ME SOUNDMETER: check stalability of sound meter calculation
/*updateSoudMeter(){
try{
const volume = parseInt(((this.mySoundMeter ? this.mySoundMeter.getVolume() : 0) / 10).toFixed(0));
this.setVolumeSoundMeter(volume, this.mySoundMeterElement);
@ -824,7 +830,7 @@ export class MediaManager {
}catch(err){
//console.error(err);
}
}
}*/
private setVolumeSoundMeter(volume: number, element: HTMLDivElement){
if(volume <= 0 && !element.classList.contains('active')){
@ -847,6 +853,32 @@ export class MediaManager {
elementChildre.classList.add('active');
});
}
public getNotification(){
//Get notification
if (window.Notification && Notification.permission !== "granted") {
Notification.requestPermission().catch((err) => {
console.error(`Notification permission error`, err);
});
}
}
public createNotification(userName: string){
if(this.focused){
return;
}
if (window.Notification && Notification.permission === "granted") {
const title = 'WorkAdventure';
const options = {
body: `Hi! ${userName} wants to discuss with you, don't be afraid!`,
icon: '/resources/logos/logo-WA-min.png',
image: '/resources/logos/logo-WA-min.png',
badge: '/resources/logos/logo-WA-min.png',
};
new Notification(title, options);
//new Notification(`Hi! ${userName} wants to discuss with you, don't be afraid!`);
}
}
}
export const mediaManager = new MediaManager();

View File

@ -158,6 +158,11 @@ export class SimplePeer {
this.sendLocalScreenSharingStreamToUser(user.userId);
}
});
//Create a notification for first user in circle discussion
if(this.PeerConnectionArray.size === 0){
mediaManager.createNotification(user.name??'');
}
this.PeerConnectionArray.set(user.userId, peer);
for (const peerConnectionListener of this.peerConnectionListeners) {