Merge branch 'develop' of github.com:thecodingmachine/workadventure into scripting_api_room_metadata
This commit is contained in:
commit
5c7ea7b258
66 changed files with 1296 additions and 967 deletions
52
front/src/WebRtc/ColorGenerator.ts
Normal file
52
front/src/WebRtc/ColorGenerator.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
export function getRandomColor(): string {
|
||||
const golden_ratio_conjugate = 0.618033988749895;
|
||||
let hue = Math.random();
|
||||
hue += golden_ratio_conjugate;
|
||||
hue %= 1;
|
||||
return hsv_to_rgb(hue, 0.5, 0.95);
|
||||
}
|
||||
|
||||
//todo: test this.
|
||||
function hsv_to_rgb(hue: number, saturation: number, brightness: number): string {
|
||||
const h_i = Math.floor(hue * 6);
|
||||
const f = hue * 6 - h_i;
|
||||
const p = brightness * (1 - saturation);
|
||||
const q = brightness * (1 - f * saturation);
|
||||
const t = brightness * (1 - (1 - f) * saturation);
|
||||
let r: number, g: number, b: number;
|
||||
switch (h_i) {
|
||||
case 0:
|
||||
r = brightness;
|
||||
g = t;
|
||||
b = p;
|
||||
break;
|
||||
case 1:
|
||||
r = q;
|
||||
g = brightness;
|
||||
b = p;
|
||||
break;
|
||||
case 2:
|
||||
r = p;
|
||||
g = brightness;
|
||||
b = t;
|
||||
break;
|
||||
case 3:
|
||||
r = p;
|
||||
g = q;
|
||||
b = brightness;
|
||||
break;
|
||||
case 4:
|
||||
r = t;
|
||||
g = p;
|
||||
b = brightness;
|
||||
break;
|
||||
case 5:
|
||||
r = brightness;
|
||||
g = p;
|
||||
b = q;
|
||||
break;
|
||||
default:
|
||||
throw "h_i cannot be " + h_i;
|
||||
}
|
||||
return "#" + Math.floor(r * 256).toString(16) + Math.floor(g * 256).toString(16) + Math.floor(b * 256).toString(16);
|
||||
}
|
|
@ -1,232 +1,12 @@
|
|||
import { HtmlUtils } from "./HtmlUtils";
|
||||
import type { UserInputManager } from "../Phaser/UserInput/UserInputManager";
|
||||
import { connectionManager } from "../Connexion/ConnectionManager";
|
||||
import { GameConnexionTypes } from "../Url/UrlManager";
|
||||
import { iframeListener } from "../Api/IframeListener";
|
||||
import { showReportScreenStore } from "../Stores/ShowReportScreenStore";
|
||||
|
||||
export type SendMessageCallback = (message: string) => void;
|
||||
import { chatMessagesStore, chatVisibilityStore } from "../Stores/ChatStore";
|
||||
|
||||
export class DiscussionManager {
|
||||
private mainContainer: HTMLDivElement;
|
||||
|
||||
private divDiscuss?: HTMLDivElement;
|
||||
private divParticipants?: HTMLDivElement;
|
||||
private nbpParticipants?: HTMLParagraphElement;
|
||||
private divMessages?: HTMLParagraphElement;
|
||||
|
||||
private participants: Map<number | string, HTMLDivElement> = new Map<number | string, HTMLDivElement>();
|
||||
|
||||
private activeDiscussion: boolean = false;
|
||||
|
||||
private sendMessageCallBack: Map<number | string, SendMessageCallback> = new Map<
|
||||
number | string,
|
||||
SendMessageCallback
|
||||
>();
|
||||
|
||||
private userInputManager?: UserInputManager;
|
||||
|
||||
constructor() {
|
||||
this.mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("main-container");
|
||||
this.createDiscussPart(""); //todo: why do we always use empty string?
|
||||
|
||||
iframeListener.chatStream.subscribe((chatEvent) => {
|
||||
this.addMessage(chatEvent.author, chatEvent.message, false);
|
||||
this.showDiscussion();
|
||||
chatMessagesStore.addExternalMessage(parseInt(chatEvent.author), chatEvent.message);
|
||||
chatVisibilityStore.set(true);
|
||||
});
|
||||
this.onSendMessageCallback("iframe_listener", (message) => {
|
||||
iframeListener.sendUserInputChat(message);
|
||||
});
|
||||
}
|
||||
|
||||
private createDiscussPart(name: string) {
|
||||
this.divDiscuss = document.createElement("div");
|
||||
this.divDiscuss.classList.add("discussion");
|
||||
|
||||
const buttonCloseDiscussion: HTMLButtonElement = document.createElement("button");
|
||||
buttonCloseDiscussion.classList.add("close-btn");
|
||||
buttonCloseDiscussion.innerHTML = `<img src="resources/logos/close.svg"/>`;
|
||||
buttonCloseDiscussion.addEventListener("click", () => {
|
||||
this.hideDiscussion();
|
||||
});
|
||||
this.divDiscuss.appendChild(buttonCloseDiscussion);
|
||||
|
||||
const myName: HTMLParagraphElement = document.createElement("p");
|
||||
myName.innerText = name.toUpperCase();
|
||||
this.nbpParticipants = document.createElement("p");
|
||||
this.nbpParticipants.innerText = "PARTICIPANTS (1)";
|
||||
|
||||
this.divParticipants = document.createElement("div");
|
||||
this.divParticipants.classList.add("participants");
|
||||
|
||||
this.divMessages = document.createElement("div");
|
||||
this.divMessages.classList.add("messages");
|
||||
this.divMessages.innerHTML = "<h2>Local messages</h2>";
|
||||
|
||||
this.divDiscuss.appendChild(myName);
|
||||
this.divDiscuss.appendChild(this.nbpParticipants);
|
||||
this.divDiscuss.appendChild(this.divParticipants);
|
||||
this.divDiscuss.appendChild(this.divMessages);
|
||||
|
||||
const sendDivMessage: HTMLDivElement = document.createElement("div");
|
||||
sendDivMessage.classList.add("send-message");
|
||||
const inputMessage: HTMLInputElement = document.createElement("input");
|
||||
inputMessage.onfocus = () => {
|
||||
if (this.userInputManager) {
|
||||
this.userInputManager.disableControls();
|
||||
}
|
||||
};
|
||||
inputMessage.onblur = () => {
|
||||
if (this.userInputManager) {
|
||||
this.userInputManager.restoreControls();
|
||||
}
|
||||
};
|
||||
inputMessage.type = "text";
|
||||
inputMessage.addEventListener("keyup", (event: KeyboardEvent) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
if (inputMessage.value === null || inputMessage.value === "" || inputMessage.value === undefined) {
|
||||
return;
|
||||
}
|
||||
this.addMessage(name, inputMessage.value, true);
|
||||
for (const callback of this.sendMessageCallBack.values()) {
|
||||
callback(inputMessage.value);
|
||||
}
|
||||
inputMessage.value = "";
|
||||
}
|
||||
});
|
||||
sendDivMessage.appendChild(inputMessage);
|
||||
this.divDiscuss.appendChild(sendDivMessage);
|
||||
|
||||
//append in main container
|
||||
this.mainContainer.appendChild(this.divDiscuss);
|
||||
|
||||
this.addParticipant("me", "Moi", undefined, true);
|
||||
}
|
||||
|
||||
public addParticipant(
|
||||
userId: number | "me",
|
||||
name: string | undefined,
|
||||
img?: string | undefined,
|
||||
isMe: boolean = false
|
||||
) {
|
||||
const divParticipant: HTMLDivElement = document.createElement("div");
|
||||
divParticipant.classList.add("participant");
|
||||
divParticipant.id = `participant-${userId}`;
|
||||
|
||||
const divImgParticipant: HTMLImageElement = document.createElement("img");
|
||||
divImgParticipant.src = "resources/logos/boy.svg";
|
||||
if (img !== undefined) {
|
||||
divImgParticipant.src = img;
|
||||
}
|
||||
const divPParticipant: HTMLParagraphElement = document.createElement("p");
|
||||
if (!name) {
|
||||
name = "Anonymous";
|
||||
}
|
||||
divPParticipant.innerText = name;
|
||||
|
||||
divParticipant.appendChild(divImgParticipant);
|
||||
divParticipant.appendChild(divPParticipant);
|
||||
|
||||
if (
|
||||
!isMe &&
|
||||
connectionManager.getConnexionType &&
|
||||
connectionManager.getConnexionType !== GameConnexionTypes.anonymous &&
|
||||
userId !== "me"
|
||||
) {
|
||||
const reportBanUserAction: HTMLButtonElement = document.createElement("button");
|
||||
reportBanUserAction.classList.add("report-btn");
|
||||
reportBanUserAction.innerText = "Report";
|
||||
reportBanUserAction.addEventListener("click", () => {
|
||||
showReportScreenStore.set({ userId: userId, userName: name ? name : "" });
|
||||
});
|
||||
divParticipant.appendChild(reportBanUserAction);
|
||||
}
|
||||
|
||||
this.divParticipants?.appendChild(divParticipant);
|
||||
|
||||
this.participants.set(userId, divParticipant);
|
||||
|
||||
this.updateParticipant(this.participants.size);
|
||||
}
|
||||
|
||||
public updateParticipant(nb: number) {
|
||||
if (!this.nbpParticipants) {
|
||||
return;
|
||||
}
|
||||
this.nbpParticipants.innerText = `PARTICIPANTS (${nb})`;
|
||||
}
|
||||
|
||||
public addMessage(name: string, message: string, isMe: boolean = false) {
|
||||
const divMessage: HTMLDivElement = document.createElement("div");
|
||||
divMessage.classList.add("message");
|
||||
if (isMe) {
|
||||
divMessage.classList.add("me");
|
||||
}
|
||||
|
||||
const pMessage: HTMLParagraphElement = document.createElement("p");
|
||||
const date = new Date();
|
||||
if (isMe) {
|
||||
name = "Me";
|
||||
} else {
|
||||
name = HtmlUtils.escapeHtml(name);
|
||||
}
|
||||
pMessage.innerHTML = `<span style="font-weight: bold">${name}</span>
|
||||
<span style="color:#bac2cc;display:inline-block;font-size:12px;">
|
||||
${date.getHours()}:${date.getMinutes()}
|
||||
</span>`;
|
||||
divMessage.appendChild(pMessage);
|
||||
|
||||
const userMessage: HTMLParagraphElement = document.createElement("p");
|
||||
userMessage.innerHTML = HtmlUtils.urlify(message);
|
||||
userMessage.classList.add("body");
|
||||
divMessage.appendChild(userMessage);
|
||||
this.divMessages?.appendChild(divMessage);
|
||||
|
||||
//automatic scroll when there are new message
|
||||
setTimeout(() => {
|
||||
this.divMessages?.scroll({
|
||||
top: this.divMessages?.scrollTop + divMessage.getBoundingClientRect().y,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
|
||||
public removeParticipant(userId: number | string) {
|
||||
const element = this.participants.get(userId);
|
||||
if (element) {
|
||||
element.remove();
|
||||
this.participants.delete(userId);
|
||||
}
|
||||
//if all participant leave, hide discussion button
|
||||
|
||||
this.sendMessageCallBack.delete(userId);
|
||||
}
|
||||
|
||||
public onSendMessageCallback(userId: string | number, callback: SendMessageCallback): void {
|
||||
this.sendMessageCallBack.set(userId, callback);
|
||||
}
|
||||
|
||||
get activatedDiscussion() {
|
||||
return this.activeDiscussion;
|
||||
}
|
||||
|
||||
private showDiscussion() {
|
||||
this.activeDiscussion = true;
|
||||
this.divDiscuss?.classList.add("active");
|
||||
}
|
||||
|
||||
private hideDiscussion() {
|
||||
this.activeDiscussion = false;
|
||||
this.divDiscuss?.classList.remove("active");
|
||||
}
|
||||
|
||||
public setUserInputManager(userInputManager: UserInputManager) {
|
||||
this.userInputManager = userInputManager;
|
||||
}
|
||||
|
||||
public showDiscussionPart() {
|
||||
this.showDiscussion();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ export class HtmlUtils {
|
|||
public static getElementByIdOrFail<T extends HTMLElement>(id: string): T {
|
||||
const elem = document.getElementById(id);
|
||||
if (HtmlUtils.isHtmlElement<T>(elem)) {
|
||||
return elem;
|
||||
return elem;
|
||||
}
|
||||
throw new Error("Cannot find HTML element with id '"+id+"'");
|
||||
throw new Error("Cannot find HTML element with id '" + id + "'");
|
||||
}
|
||||
|
||||
public static querySelectorOrFail<T extends HTMLElement>(selector: string): T {
|
||||
|
@ -12,7 +12,7 @@ export class HtmlUtils {
|
|||
if (HtmlUtils.isHtmlElement<T>(elem)) {
|
||||
return elem;
|
||||
}
|
||||
throw new Error("Cannot find HTML element with selector '"+selector+"'");
|
||||
throw new Error("Cannot find HTML element with selector '" + selector + "'");
|
||||
}
|
||||
|
||||
public static removeElementByIdOrFail<T extends HTMLElement>(id: string): T {
|
||||
|
@ -21,12 +21,12 @@ export class HtmlUtils {
|
|||
elem.remove();
|
||||
return elem;
|
||||
}
|
||||
throw new Error("Cannot find HTML element with id '"+id+"'");
|
||||
throw new Error("Cannot find HTML element with id '" + id + "'");
|
||||
}
|
||||
|
||||
public static escapeHtml(html: string): string {
|
||||
const text = document.createTextNode(html);
|
||||
const p = document.createElement('p');
|
||||
const text = document.createTextNode(html.replace(/(\r\n|\r|\n)/g, "<br/>"));
|
||||
const p = document.createElement("p");
|
||||
p.appendChild(text);
|
||||
return p.innerHTML;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ export class HtmlUtils {
|
|||
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
||||
text = HtmlUtils.escapeHtml(text);
|
||||
return text.replace(urlRegex, (url: string) => {
|
||||
const link = document.createElement('a');
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.target = "_blank";
|
||||
const text = document.createTextNode(url);
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
import { DivImportance, layoutManager } from "./LayoutManager";
|
||||
import { layoutManager } from "./LayoutManager";
|
||||
import { HtmlUtils } from "./HtmlUtils";
|
||||
import { discussionManager, SendMessageCallback } from "./DiscussionManager";
|
||||
import type { UserInputManager } from "../Phaser/UserInput/UserInputManager";
|
||||
import { localUserStore } from "../Connexion/LocalUserStore";
|
||||
import type { UserSimplePeerInterface } from "./SimplePeer";
|
||||
import { SoundMeter } from "../Phaser/Components/SoundMeter";
|
||||
import { DISABLE_NOTIFICATIONS } from "../Enum/EnvironmentVariable";
|
||||
import { localStreamStore } from "../Stores/MediaStore";
|
||||
import { screenSharingLocalStreamStore } from "../Stores/ScreenSharingStore";
|
||||
import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore";
|
||||
|
||||
export type UpdatedLocalStreamCallback = (media: MediaStream | null) => void;
|
||||
export type StartScreenSharingCallback = (media: MediaStream) => void;
|
||||
export type StopScreenSharingCallback = (media: MediaStream) => void;
|
||||
|
||||
|
@ -21,16 +15,11 @@ export class MediaManager {
|
|||
startScreenSharingCallBacks: Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
|
||||
stopScreenSharingCallBacks: Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
|
||||
|
||||
private focused: boolean = true;
|
||||
|
||||
private triggerCloseJistiFrame: Map<String, Function> = new Map<String, Function>();
|
||||
|
||||
private userInputManager?: UserInputManager;
|
||||
|
||||
constructor() {
|
||||
//Check of ask notification navigator permission
|
||||
this.getNotification();
|
||||
|
||||
localStreamStore.subscribe((result) => {
|
||||
if (result.type === "error") {
|
||||
console.error(result.error);
|
||||
|
@ -182,67 +171,35 @@ export class MediaManager {
|
|||
}
|
||||
}
|
||||
|
||||
public addNewMessage(name: string, message: string, isMe: boolean = false) {
|
||||
discussionManager.addMessage(name, message, isMe);
|
||||
|
||||
//when there are new message, show discussion
|
||||
if (!discussionManager.activatedDiscussion) {
|
||||
discussionManager.showDiscussionPart();
|
||||
}
|
||||
}
|
||||
|
||||
public addSendMessageCallback(userId: string | number, callback: SendMessageCallback) {
|
||||
discussionManager.onSendMessageCallback(userId, callback);
|
||||
}
|
||||
|
||||
public setUserInputManager(userInputManager: UserInputManager) {
|
||||
this.userInputManager = userInputManager;
|
||||
discussionManager.setUserInputManager(userInputManager);
|
||||
}
|
||||
|
||||
public getNotification() {
|
||||
//Get notification
|
||||
if (!DISABLE_NOTIFICATIONS && window.Notification && Notification.permission !== "granted") {
|
||||
if (this.checkNotificationPromise()) {
|
||||
Notification.requestPermission().catch((err) => {
|
||||
console.error(`Notification permission error`, err);
|
||||
});
|
||||
} else {
|
||||
Notification.requestPermission();
|
||||
}
|
||||
}
|
||||
public hasNotification(): boolean {
|
||||
return Notification.permission === "granted";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the browser supports the modern version of the Notification API (which is Promise based) or false
|
||||
* if we are on Safari...
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API
|
||||
*/
|
||||
private checkNotificationPromise(): boolean {
|
||||
try {
|
||||
Notification.requestPermission().then();
|
||||
} catch (e) {
|
||||
return false;
|
||||
public requestNotification() {
|
||||
if (window.Notification && Notification.permission !== "granted") {
|
||||
return Notification.requestPermission();
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public createNotification(userName: string) {
|
||||
if (this.focused) {
|
||||
if (document.hasFocus()) {
|
||||
return;
|
||||
}
|
||||
if (window.Notification && Notification.permission === "granted") {
|
||||
const title = "WorkAdventure";
|
||||
|
||||
if (this.hasNotification()) {
|
||||
const title = `${userName} wants to discuss with you`;
|
||||
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!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import type * as SimplePeerNamespace from "simple-peer";
|
||||
import { mediaManager } from "./MediaManager";
|
||||
import { STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER } from "../Enum/EnvironmentVariable";
|
||||
import type { RoomConnection } from "../Connexion/RoomConnection";
|
||||
import { MESSAGE_TYPE_CONSTRAINT, PeerStatus } from "./VideoPeer";
|
||||
import type { UserSimplePeerInterface } from "./SimplePeer";
|
||||
import { Readable, readable, writable, Writable } from "svelte/store";
|
||||
import { Readable, readable } from "svelte/store";
|
||||
import { videoFocusStore } from "../Stores/VideoFocusStore";
|
||||
import { getIceServersConfig } from "../Components/Video/utils";
|
||||
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
|
||||
|
||||
|
@ -32,21 +31,9 @@ export class ScreenSharingPeer extends Peer {
|
|||
stream: MediaStream | null
|
||||
) {
|
||||
super({
|
||||
initiator: initiator ? initiator : false,
|
||||
//reconnectTimer: 10000,
|
||||
initiator,
|
||||
config: {
|
||||
iceServers: [
|
||||
{
|
||||
urls: STUN_SERVER.split(","),
|
||||
},
|
||||
TURN_SERVER !== ""
|
||||
? {
|
||||
urls: TURN_SERVER.split(","),
|
||||
username: user.webRtcUser || TURN_USER,
|
||||
credential: user.webRtcPassword || TURN_PASSWORD,
|
||||
}
|
||||
: undefined,
|
||||
].filter((value) => value !== undefined),
|
||||
iceServers: getIceServersConfig(user),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import { localStreamStore, LocalStreamStoreValue, obtainedMediaConstraintStore }
|
|||
import { screenSharingLocalStreamStore } from "../Stores/ScreenSharingStore";
|
||||
import { discussionManager } from "./DiscussionManager";
|
||||
import { playersStore } from "../Stores/PlayersStore";
|
||||
import { newChatMessageStore } from "../Stores/ChatStore";
|
||||
|
||||
export interface UserSimplePeerInterface {
|
||||
userId: number;
|
||||
|
@ -155,27 +156,11 @@ export class SimplePeer {
|
|||
|
||||
const name = this.getName(user.userId);
|
||||
|
||||
discussionManager.removeParticipant(user.userId);
|
||||
|
||||
this.lastWebrtcUserName = user.webRtcUser;
|
||||
this.lastWebrtcPassword = user.webRtcPassword;
|
||||
|
||||
const peer = new VideoPeer(user, user.initiator ? user.initiator : false, name, this.Connection, localStream);
|
||||
|
||||
//permit to send message
|
||||
mediaManager.addSendMessageCallback(user.userId, (message: string) => {
|
||||
peer.write(
|
||||
new Buffer(
|
||||
JSON.stringify({
|
||||
type: MESSAGE_TYPE_MESSAGE,
|
||||
name: this.myName.toUpperCase(),
|
||||
userId: this.userId,
|
||||
message: message,
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
peer.toClose = false;
|
||||
// When a connection is established to a video stream, and if a screen sharing is taking place,
|
||||
// the user sharing screen should also initiate a connection to the remote user!
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import type * as SimplePeerNamespace from "simple-peer";
|
||||
import { mediaManager } from "./MediaManager";
|
||||
import { STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER } from "../Enum/EnvironmentVariable";
|
||||
import type { RoomConnection } from "../Connexion/RoomConnection";
|
||||
import { blackListManager } from "./BlackListManager";
|
||||
import type { Subscription } from "rxjs";
|
||||
import type { UserSimplePeerInterface } from "./SimplePeer";
|
||||
import { get, readable, Readable } from "svelte/store";
|
||||
import { get, readable, Readable, Unsubscriber } from "svelte/store";
|
||||
import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
|
||||
import { discussionManager } from "./DiscussionManager";
|
||||
import { playersStore } from "../Stores/PlayersStore";
|
||||
import { chatMessagesStore, chatVisibilityStore, newChatMessageStore } from "../Stores/ChatStore";
|
||||
import { getIceServersConfig } from "../Components/Video/utils";
|
||||
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
|
||||
|
||||
|
@ -34,6 +34,8 @@ export class VideoPeer extends Peer {
|
|||
public readonly streamStore: Readable<MediaStream | null>;
|
||||
public readonly statusStore: Readable<PeerStatus>;
|
||||
public readonly constraintsStore: Readable<MediaStreamConstraints | null>;
|
||||
private newMessageunsubscriber: Unsubscriber | null = null;
|
||||
private closing: Boolean = false; //this is used to prevent destroy() from being called twice
|
||||
|
||||
constructor(
|
||||
public user: UserSimplePeerInterface,
|
||||
|
@ -43,21 +45,9 @@ export class VideoPeer extends Peer {
|
|||
localStream: MediaStream | null
|
||||
) {
|
||||
super({
|
||||
initiator: initiator ? initiator : false,
|
||||
//reconnectTimer: 10000,
|
||||
initiator,
|
||||
config: {
|
||||
iceServers: [
|
||||
{
|
||||
urls: STUN_SERVER.split(","),
|
||||
},
|
||||
TURN_SERVER !== ""
|
||||
? {
|
||||
urls: TURN_SERVER.split(","),
|
||||
username: user.webRtcUser || TURN_USER,
|
||||
credential: user.webRtcPassword || TURN_PASSWORD,
|
||||
}
|
||||
: undefined,
|
||||
].filter((value) => value !== undefined),
|
||||
iceServers: getIceServersConfig(user),
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -147,6 +137,20 @@ export class VideoPeer extends Peer {
|
|||
|
||||
this.on("connect", () => {
|
||||
this._connected = true;
|
||||
chatMessagesStore.addIncomingUser(this.userId);
|
||||
|
||||
this.newMessageunsubscriber = newChatMessageStore.subscribe((newMessage) => {
|
||||
if (!newMessage) return;
|
||||
this.write(
|
||||
new Buffer(
|
||||
JSON.stringify({
|
||||
type: MESSAGE_TYPE_MESSAGE,
|
||||
message: newMessage,
|
||||
})
|
||||
)
|
||||
); //send more data
|
||||
newChatMessageStore.set(null); //This is to prevent a newly created SimplePeer to send an old message a 2nd time. Is there a better way?
|
||||
});
|
||||
});
|
||||
|
||||
this.on("data", (chunk: Buffer) => {
|
||||
|
@ -164,8 +168,9 @@ export class VideoPeer extends Peer {
|
|||
mediaManager.disabledVideoByUserId(this.userId);
|
||||
}
|
||||
} else if (message.type === MESSAGE_TYPE_MESSAGE) {
|
||||
if (!blackListManager.isBlackListed(message.userId)) {
|
||||
mediaManager.addNewMessage(message.name, message.message);
|
||||
if (!blackListManager.isBlackListed(this.userUuid)) {
|
||||
chatMessagesStore.addExternalMessage(this.userId, message.message);
|
||||
chatVisibilityStore.set(true);
|
||||
}
|
||||
} else if (message.type === MESSAGE_TYPE_BLOCKED) {
|
||||
//FIXME when A blacklists B, the output stream from A is muted in B's js client. This is insecure since B can manipulate the code to unmute A stream.
|
||||
|
@ -245,18 +250,18 @@ export class VideoPeer extends Peer {
|
|||
/**
|
||||
* This is triggered twice. Once by the server, and once by a remote client disconnecting
|
||||
*/
|
||||
public destroy(error?: Error): void {
|
||||
public destroy(): void {
|
||||
try {
|
||||
this._connected = false;
|
||||
if (!this.toClose) {
|
||||
if (!this.toClose || this.closing) {
|
||||
return;
|
||||
}
|
||||
this.closing = true;
|
||||
this.onBlockSubscribe.unsubscribe();
|
||||
this.onUnBlockSubscribe.unsubscribe();
|
||||
discussionManager.removeParticipant(this.userId);
|
||||
// FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray"
|
||||
// I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel.
|
||||
super.destroy(error);
|
||||
if (this.newMessageunsubscriber) this.newMessageunsubscriber();
|
||||
chatMessagesStore.addOutcomingUser(this.userId);
|
||||
super.destroy();
|
||||
} catch (err) {
|
||||
console.error("VideoPeer::destroy", err);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue