diff --git a/CHANGELOG.md b/CHANGELOG.md
index ff7496ba..08c0f7db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,11 @@
- Use `WA.ui.registerMenuCommand(): void` to add a custom menu
- Use `WA.room.setTiles(): void` to add, delete or change an array of tiles
- Users blocking now relies on UUID rather than ID. A blocked user that leaves a room and comes back will stay blocked.
+- The text chat was redesigned to be prettier and to use more features :
+ - The chat is now persistent bewteen discussions and always accesible
+ - The chat now tracks incoming and outcoming users in your conversation
+ - The chat allows your to see the visit card of users
+ - You can close the chat window with the escape key
## Version 1.4.3 - 1.4.4 - 1.4.5
diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts
index 020f4c29..71d2124e 100644
--- a/back/src/Model/GameRoom.ts
+++ b/back/src/Model/GameRoom.ts
@@ -15,12 +15,6 @@ import { Admin } from "../Model/Admin";
export type ConnectCallback = (user: User, group: Group) => void;
export type DisconnectCallback = (user: User, group: Group) => void;
-export enum GameRoomPolicyTypes {
- ANONYMOUS_POLICY = 1,
- MEMBERS_ONLY_POLICY,
- USE_TAGS_POLICY,
-}
-
export class GameRoom {
private readonly minDistance: number;
private readonly groupRadius: number;
diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts
index 8d04e713..8d1659df 100644
--- a/back/src/Services/SocketManager.ts
+++ b/back/src/Services/SocketManager.ts
@@ -436,10 +436,7 @@ export class SocketManager {
const serverToClientMessage1 = new ServerToClientMessage();
serverToClientMessage1.setWebrtcstartmessage(webrtcStartMessage1);
- //if (!user.socket.disconnecting) {
user.socket.write(serverToClientMessage1);
- //console.log('Sending webrtcstart initiator to '+user.socket.userId)
- //}
const webrtcStartMessage2 = new WebRtcStartMessage();
webrtcStartMessage2.setUserid(user.id);
@@ -453,10 +450,7 @@ export class SocketManager {
const serverToClientMessage2 = new ServerToClientMessage();
serverToClientMessage2.setWebrtcstartmessage(webrtcStartMessage2);
- //if (!otherUser.socket.disconnecting) {
otherUser.socket.write(serverToClientMessage2);
- //console.log('Sending webrtcstart to '+otherUser.socket.userId)
- //}
}
}
diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html
index aa63229f..30ea8353 100644
--- a/front/dist/index.tmpl.html
+++ b/front/dist/index.tmpl.html
@@ -37,8 +37,7 @@
-
-
+
diff --git a/front/dist/static/images/send.png b/front/dist/static/images/send.png
new file mode 100644
index 00000000..1f75634a
Binary files /dev/null and b/front/dist/static/images/send.png differ
diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte
index 8ade9398..0f808074 100644
--- a/front/src/Components/App.svelte
+++ b/front/src/Components/App.svelte
@@ -10,12 +10,14 @@
import {errorStore} from "../Stores/ErrorStore";
import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte";
import LoginScene from "./Login/LoginScene.svelte";
+ import Chat from "./Chat/Chat.svelte";
import {loginSceneVisibleStore} from "../Stores/LoginSceneStore";
import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte";
import VisitCard from "./VisitCard/VisitCard.svelte";
import {requestVisitCardsStore} from "../Stores/GameStore";
import type {Game} from "../Phaser/Game/Game";
+ import {chatVisibilityStore} from "../Stores/ChatStore";
import {helpCameraSettingsVisibleStore} from "../Stores/HelpCameraSettingsStore";
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte";
import AudioPlaying from "./UI/AudioPlaying.svelte";
@@ -61,14 +63,6 @@
{/if}
-
-
{#if $gameOverlayVisibilityStore}
@@ -94,4 +88,7 @@
{/if}
+ {#if $chatVisibilityStore}
+
+ {/if}
diff --git a/front/src/Components/Chat/Chat.svelte b/front/src/Components/Chat/Chat.svelte
new file mode 100644
index 00000000..8432df3b
--- /dev/null
+++ b/front/src/Components/Chat/Chat.svelte
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+ Your chat history ×
+
+
+
+
+ {#each $chatMessagesStore as message, i}
+
+ {/each}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/front/src/Components/Chat/ChatElement.svelte b/front/src/Components/Chat/ChatElement.svelte
new file mode 100644
index 00000000..d656aca9
--- /dev/null
+++ b/front/src/Components/Chat/ChatElement.svelte
@@ -0,0 +1,83 @@
+
+
+
+
+ {#if message.type === ChatMessageTypes.userIncoming}
+ >> {#each targets as target, index}
{#if !isLastIteration(index)}, {/if}{/each} enter
({renderDate(message.date)})
+ {:else if message.type === ChatMessageTypes.userOutcoming}
+ << {#each targets as target, index}
{#if !isLastIteration(index)}, {/if}{/each} left
({renderDate(message.date)})
+ {:else if message.type === ChatMessageTypes.me}
+
Me: ({renderDate(message.date)})
+ {#each texts as text}
+
+ {/each}
+ {:else}
+
: ({renderDate(message.date)})
+ {#each texts as text}
+
+ {/each}
+ {/if}
+
+
+
+
\ No newline at end of file
diff --git a/front/src/Components/Chat/ChatMessageForm.svelte b/front/src/Components/Chat/ChatMessageForm.svelte
new file mode 100644
index 00000000..5445b8b8
--- /dev/null
+++ b/front/src/Components/Chat/ChatMessageForm.svelte
@@ -0,0 +1,57 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/front/src/Components/Chat/ChatPlayerName.svelte b/front/src/Components/Chat/ChatPlayerName.svelte
new file mode 100644
index 00000000..9b0630c0
--- /dev/null
+++ b/front/src/Components/Chat/ChatPlayerName.svelte
@@ -0,0 +1,51 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/front/src/Components/Chat/ChatSubMenu.svelte b/front/src/Components/Chat/ChatSubMenu.svelte
new file mode 100644
index 00000000..6690699e
--- /dev/null
+++ b/front/src/Components/Chat/ChatSubMenu.svelte
@@ -0,0 +1,33 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/front/src/Phaser/Components/OpenChatIcon.ts b/front/src/Phaser/Components/OpenChatIcon.ts
index ab07a80c..8c648bc1 100644
--- a/front/src/Phaser/Components/OpenChatIcon.ts
+++ b/front/src/Phaser/Components/OpenChatIcon.ts
@@ -1,7 +1,7 @@
-import {discussionManager} from "../../WebRtc/DiscussionManager";
-import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes";
+import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes";
+import { chatVisibilityStore } from "../../Stores/ChatStore";
-export const openChatIconName = 'openChatIcon';
+export const openChatIconName = "openChatIcon";
export class OpenChatIcon extends Phaser.GameObjects.Image {
constructor(scene: Phaser.Scene, x: number, y: number) {
super(scene, x, y, openChatIconName, 3);
@@ -9,9 +9,9 @@ export class OpenChatIcon extends Phaser.GameObjects.Image {
this.setScrollFactor(0, 0);
this.setOrigin(0, 1);
this.setInteractive();
- this.setVisible(false);
+ //this.setVisible(false);
this.setDepth(DEPTH_INGAME_TEXT_INDEX);
- this.on("pointerup", () => discussionManager.showDiscussionPart());
+ this.on("pointerup", () => chatVisibilityStore.set(true));
}
-}
\ No newline at end of file
+}
diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts
index d2a659ec..3c47c9d9 100644
--- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts
+++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts
@@ -101,7 +101,6 @@ export const createLoadingPromise = (
frameConfig: FrameConfig
) => {
return new Promise((res, rej) => {
- console.log("count", loadPlugin.listenerCount("loaderror"));
if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
return res(playerResourceDescriptor);
}
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 4c6598d1..3bf69846 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -92,6 +92,7 @@ import { peerStore, screenSharingPeerStore } from "../../Stores/PeerStore";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
import { playersStore } from "../../Stores/PlayersStore";
+import { chatVisibilityStore } from "../../Stores/ChatStore";
export interface GameSceneInitInterface {
initPosition: PointInterface | null;
@@ -169,6 +170,7 @@ export class GameScene extends DirtyScene {
private createPromiseResolve!: (value?: void | PromiseLike) => void;
private iframeSubscriptionList!: Array;
private peerStoreUnsubscribe!: () => void;
+ private chatVisibilityUnsubscribe!: () => void;
private biggestAvailableAreaStoreUnsubscribe!: () => void;
MapUrlFile: string;
RoomId: string;
@@ -571,6 +573,10 @@ export class GameScene extends DirtyScene {
}
oldPeerNumber = newPeerNumber;
});
+
+ this.chatVisibilityUnsubscribe = chatVisibilityStore.subscribe((v) => {
+ this.openChatIcon.setVisible(!v);
+ });
}
/**
@@ -692,12 +698,12 @@ export class GameScene extends DirtyScene {
const self = this;
this.simplePeer.registerPeerConnectionListener({
onConnect(peer) {
- self.openChatIcon.setVisible(true);
+ //self.openChatIcon.setVisible(true);
audioManager.decreaseVolume();
},
onDisconnect(userId: number) {
if (self.simplePeer.getNbConnections() === 0) {
- self.openChatIcon.setVisible(false);
+ //self.openChatIcon.setVisible(false);
audioManager.restoreVolume();
}
},
@@ -1173,6 +1179,7 @@ ${escapedMessage}
this.pinchManager?.destroy();
this.emoteManager.destroy();
this.peerStoreUnsubscribe();
+ this.chatVisibilityUnsubscribe();
this.biggestAvailableAreaStoreUnsubscribe();
iframeListener.unregisterAnswerer("getState");
diff --git a/front/src/Phaser/Game/PlayerInterface.ts b/front/src/Phaser/Game/PlayerInterface.ts
index 5a81c89a..6ab439df 100644
--- a/front/src/Phaser/Game/PlayerInterface.ts
+++ b/front/src/Phaser/Game/PlayerInterface.ts
@@ -7,4 +7,5 @@ export interface PlayerInterface {
visitCardUrl: string | null;
companion: string | null;
userUuid: string;
+ color?: string;
}
diff --git a/front/src/Stores/ChatStore.ts b/front/src/Stores/ChatStore.ts
new file mode 100644
index 00000000..df488115
--- /dev/null
+++ b/front/src/Stores/ChatStore.ts
@@ -0,0 +1,118 @@
+import { writable } from "svelte/store";
+import { playersStore } from "./PlayersStore";
+import type { PlayerInterface } from "../Phaser/Game/PlayerInterface";
+
+export const chatVisibilityStore = writable(false);
+export const chatInputFocusStore = writable(false);
+
+export const newChatMessageStore = writable(null);
+
+export enum ChatMessageTypes {
+ text = 1,
+ me,
+ userIncoming,
+ userOutcoming,
+}
+
+export interface ChatMessage {
+ type: ChatMessageTypes;
+ date: Date;
+ author?: PlayerInterface;
+ targets?: PlayerInterface[];
+ text?: string[];
+}
+
+function getAuthor(authorId: number): PlayerInterface {
+ const author = playersStore.getPlayerById(authorId);
+ if (!author) {
+ throw "Could not find data for author " + authorId;
+ }
+ return author;
+}
+
+function createChatMessagesStore() {
+ const { subscribe, update } = writable([]);
+
+ return {
+ subscribe,
+ addIncomingUser(authorId: number) {
+ update((list) => {
+ const lastMessage = list[list.length - 1];
+ if (lastMessage && lastMessage.type === ChatMessageTypes.userIncoming && lastMessage.targets) {
+ lastMessage.targets.push(getAuthor(authorId));
+ } else {
+ list.push({
+ type: ChatMessageTypes.userIncoming,
+ targets: [getAuthor(authorId)],
+ date: new Date(),
+ });
+ }
+ return list;
+ });
+ },
+ addOutcomingUser(authorId: number) {
+ update((list) => {
+ const lastMessage = list[list.length - 1];
+ if (lastMessage && lastMessage.type === ChatMessageTypes.userOutcoming && lastMessage.targets) {
+ lastMessage.targets.push(getAuthor(authorId));
+ } else {
+ list.push({
+ type: ChatMessageTypes.userOutcoming,
+ targets: [getAuthor(authorId)],
+ date: new Date(),
+ });
+ }
+ return list;
+ });
+ },
+ addPersonnalMessage(text: string) {
+ newChatMessageStore.set(text);
+ update((list) => {
+ const lastMessage = list[list.length - 1];
+ if (lastMessage && lastMessage.type === ChatMessageTypes.me && lastMessage.text) {
+ lastMessage.text.push(text);
+ } else {
+ list.push({
+ type: ChatMessageTypes.me,
+ text: [text],
+ date: new Date(),
+ });
+ }
+ return list;
+ });
+ },
+ addExternalMessage(authorId: number, text: string) {
+ update((list) => {
+ const lastMessage = list[list.length - 1];
+ if (lastMessage && lastMessage.type === ChatMessageTypes.text && lastMessage.text) {
+ lastMessage.text.push(text);
+ } else {
+ list.push({
+ type: ChatMessageTypes.text,
+ text: [text],
+ author: getAuthor(authorId),
+ date: new Date(),
+ });
+ }
+ return list;
+ });
+ },
+ };
+}
+export const chatMessagesStore = createChatMessagesStore();
+
+function createChatSubMenuVisibilityStore() {
+ const { subscribe, update } = writable("");
+
+ return {
+ subscribe,
+ openSubMenu(playerName: string, index: number) {
+ const id = playerName + index;
+ update((oldValue) => {
+ return oldValue === id ? "" : id;
+ });
+ },
+ };
+}
+
+export const chatSubMenuVisbilityStore = createChatSubMenuVisibilityStore();
diff --git a/front/src/Stores/PlayersStore.ts b/front/src/Stores/PlayersStore.ts
index 6c21de7a..2ea988bb 100644
--- a/front/src/Stores/PlayersStore.ts
+++ b/front/src/Stores/PlayersStore.ts
@@ -1,6 +1,7 @@
import { writable } from "svelte/store";
import type { PlayerInterface } from "../Phaser/Game/PlayerInterface";
import type { RoomConnection } from "../Connexion/RoomConnection";
+import { getRandomColor } from "../WebRtc/ColorGenerator";
/**
* A store that contains the list of players currently known.
@@ -24,6 +25,7 @@ function createPlayersStore() {
visitCardUrl: message.visitCardUrl,
companion: message.companion,
userUuid: message.userUuid,
+ color: getRandomColor(),
});
return users;
});
diff --git a/front/src/Stores/UserInputStore.ts b/front/src/Stores/UserInputStore.ts
index cbb7f0c3..993d8795 100644
--- a/front/src/Stores/UserInputStore.ts
+++ b/front/src/Stores/UserInputStore.ts
@@ -1,10 +1,11 @@
-import {derived} from "svelte/store";
-import {consoleGlobalMessageManagerFocusStore} from "./ConsoleGlobalMessageManagerStore";
+import { derived } from "svelte/store";
+import { consoleGlobalMessageManagerFocusStore } from "./ConsoleGlobalMessageManagerStore";
+import { chatInputFocusStore } from "./ChatStore";
//derived from the focus on Menu, ConsoleGlobal, Chat and ...
export const enableUserInputsStore = derived(
- consoleGlobalMessageManagerFocusStore,
- ($consoleGlobalMessageManagerFocusStore) => {
- return !$consoleGlobalMessageManagerFocusStore;
+ [consoleGlobalMessageManagerFocusStore, chatInputFocusStore],
+ ([$consoleGlobalMessageManagerFocusStore, $chatInputFocusStore]) => {
+ return !$consoleGlobalMessageManagerFocusStore && !$chatInputFocusStore;
}
-);
\ No newline at end of file
+);
diff --git a/front/src/WebRtc/ColorGenerator.ts b/front/src/WebRtc/ColorGenerator.ts
new file mode 100644
index 00000000..be192f9f
--- /dev/null
+++ b/front/src/WebRtc/ColorGenerator.ts
@@ -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);
+}
diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts
index ae351f76..a3c928f4 100644
--- a/front/src/WebRtc/DiscussionManager.ts
+++ b/front/src/WebRtc/DiscussionManager.ts
@@ -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 = new Map();
-
- private activeDiscussion: boolean = false;
-
- private sendMessageCallBack: Map = new Map<
- number | string,
- SendMessageCallback
- >();
-
- private userInputManager?: UserInputManager;
-
constructor() {
- this.mainContainer = HtmlUtils.getElementByIdOrFail("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 = ` `;
- 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 = "Local messages ";
-
- 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 = `${name}
-
- ${date.getHours()}:${date.getMinutes()}
- `;
- 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();
}
}
diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts
index d9847f44..126bf1a8 100644
--- a/front/src/WebRtc/MediaManager.ts
+++ b/front/src/WebRtc/MediaManager.ts
@@ -1,16 +1,11 @@
-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;
@@ -182,22 +177,8 @@ 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() {
diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts
index 5045a5a3..e30f1b1f 100644
--- a/front/src/WebRtc/SimplePeer.ts
+++ b/front/src/WebRtc/SimplePeer.ts
@@ -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!
diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts
index bde0bcde..45118b5f 100644
--- a/front/src/WebRtc/VideoPeer.ts
+++ b/front/src/WebRtc/VideoPeer.ts
@@ -5,10 +5,11 @@ 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";
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
@@ -34,6 +35,7 @@ export class VideoPeer extends Peer {
public readonly streamStore: Readable;
public readonly statusStore: Readable;
public readonly constraintsStore: Readable;
+ private newMessageunsubscriber: Unsubscriber | null = null;
constructor(
public user: UserSimplePeerInterface,
@@ -147,6 +149,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 +180,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.
@@ -253,7 +270,9 @@ export class VideoPeer extends Peer {
}
this.onBlockSubscribe.unsubscribe();
this.onUnBlockSubscribe.unsubscribe();
- discussionManager.removeParticipant(this.userId);
+ if (this.newMessageunsubscriber) this.newMessageunsubscriber();
+ chatMessagesStore.addOutcomingUser(this.userId);
+ //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);
diff --git a/front/style/fonts.scss b/front/style/fonts.scss
index a49d3967..526f6615 100644
--- a/front/style/fonts.scss
+++ b/front/style/fonts.scss
@@ -1,9 +1,5 @@
@import "~@fontsource/press-start-2p/index.css";
-*{
- font-family: PixelFont-7,monospace;
-}
-
.nes-btn {
font-family: "Press Start 2P";
}
diff --git a/front/webpack.config.ts b/front/webpack.config.ts
index b6efb389..37362baf 100644
--- a/front/webpack.config.ts
+++ b/front/webpack.config.ts
@@ -7,7 +7,6 @@ import MiniCssExtractPlugin from "mini-css-extract-plugin";
import sveltePreprocess from "svelte-preprocess";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import NodePolyfillPlugin from "node-polyfill-webpack-plugin";
-import { DISPLAY_TERMS_OF_USE } from "./src/Enum/EnvironmentVariable";
const mode = process.env.NODE_ENV ?? "development";
const buildNpmTypingsForApi = !!process.env.BUILD_TYPINGS;