diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts
index 9c46a41b..3e2dd3e0 100644
--- a/back/src/Services/AdminApi.ts
+++ b/back/src/Services/AdminApi.ts
@@ -100,11 +100,12 @@ class AdminApi {
return res.data;
}
- reportPlayer(reportedUserUuid: string, reportedUserComment: string, reporterUserUuid: string) {
+ reportPlayer(reportedUserUuid: string, reportedUserComment: string, reporterUserUuid: string, reportWorldSlug: string) {
return Axios.post(`${ADMIN_API_URL}/api/report`, {
reportedUserUuid,
reportedUserComment,
reporterUserUuid,
+ reportWorldSlug,
},
{
headers: {"Authorization": `${ADMIN_API_TOKEN}`}
diff --git a/front/dist/resources/html/gameReport.html b/front/dist/resources/html/gameReport.html
new file mode 100644
index 00000000..9a761c32
--- /dev/null
+++ b/front/dist/resources/html/gameReport.html
@@ -0,0 +1,104 @@
+
+
+
diff --git a/front/dist/resources/logos/report.back.svg b/front/dist/resources/logos/report.back.svg
new file mode 100644
index 00000000..1cb3b068
--- /dev/null
+++ b/front/dist/resources/logos/report.back.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/front/dist/resources/logos/report.svg b/front/dist/resources/logos/report.svg
index 1cb3b068..14753256 100644
--- a/front/dist/resources/logos/report.svg
+++ b/front/dist/resources/logos/report.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css
index 33332270..e540092b 100644
--- a/front/dist/resources/style/style.css
+++ b/front/dist/resources/style/style.css
@@ -39,6 +39,7 @@ body .message-info.warning{
position: relative;
transition: all 0.2s ease;
background-color: #00000099;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
.video-container i{
position: absolute;
@@ -53,25 +54,62 @@ body .message-info.warning{
font-size: 28px;
color: white;
}
-.video-container img.active{
- display: block;
-}
+
.video-container img{
position: absolute;
display: none;
- width: 15px;
- height: 15px;
- background: #d93025;
- border-radius: 48px;
+ width: 25px;
+ height: 25px;
left: 5px;
bottom: 5px;
padding: 10px;
z-index: 2;
}
-.video-container img.report{
+.video-container button.report{
+ display: block;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
+ background: none;
+ background-color: rgba(0, 0, 0, 0);
+ border: none;
+ background-color: black;
+ border-radius: 15px;
+ position: absolute;
+ width: 0px;
+ height: 35px;
right: 5px;
- left: auto;
+ bottom: 5px;
+ padding: 0px;
+ overflow: hidden;
+ z-index: 2;
+ transition: all .5s ease;
+}
+
+.video-container:hover button.report{
+ width: 35px;
+ padding: 10px;
+}
+
+.video-container button.report:hover {
+ width: 94px;
+}
+
+.video-container button.report img{
+ position: absolute;
+ display: block;
+ bottom: 5px;
+ left: 5px;
+ margin: 0;
+ padding: 0;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
+}
+.video-container button.report span{
+ position: absolute;
+ bottom: 8px;
+ left: 36px;
+ color: white;
+ font-size: 16px;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
.video-container video{
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 4f959ee6..b341d8a0 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -154,7 +154,7 @@ export class GameScene extends ResizableScene implements CenterListener {
private actionableItems: Map = new Map();
// The item that can be selected by pressing the space key.
private outlinedItem: ActionableItem|null = null;
- private userInputManager!: UserInputManager;
+ public userInputManager!: UserInputManager;
private isReconnecting: boolean = false;
private startLayerName!: string | null;
private openChatIcon!: OpenChatIcon;
diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts
index 8bd64cd1..6f035446 100644
--- a/front/src/Phaser/Menu/MenuScene.ts
+++ b/front/src/Phaser/Menu/MenuScene.ts
@@ -2,7 +2,7 @@ import {LoginScene, LoginSceneName} from "../Login/LoginScene";
import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene";
import {gameManager} from "../Game/GameManager";
import {localUserStore} from "../../Connexion/LocalUserStore";
-import {mediaManager} from "../../WebRtc/MediaManager";
+import {mediaManager, ReportCallback, ShowReportCallBack} from "../../WebRtc/MediaManager";
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
export const MenuSceneName = 'MenuScene';
@@ -10,6 +10,7 @@ const gameMenuKey = 'gameMenu';
const gameMenuIconKey = 'gameMenuIcon';
const gameSettingsMenuKey = 'gameSettingsMenu';
const gameShare = 'gameShare';
+const gameReport = 'gameReport';
const closedSideMenuX = -200;
const openedSideMenuX = 0;
@@ -21,9 +22,11 @@ export class MenuScene extends Phaser.Scene {
private menuElement!: Phaser.GameObjects.DOMElement;
private gameQualityMenuElement!: Phaser.GameObjects.DOMElement;
private gameShareElement!: Phaser.GameObjects.DOMElement;
+ private gameReportElement!: Phaser.GameObjects.DOMElement;
private sideMenuOpened = false;
private settingsMenuOpened = false;
private gameShareOpened = false;
+ private gameReportOpened = false;
private gameQualityValue: number;
private videoQualityValue: number;
private menuButton!: Phaser.GameObjects.DOMElement;
@@ -40,6 +43,7 @@ export class MenuScene extends Phaser.Scene {
this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html');
this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html');
this.load.html(gameShare, 'resources/html/gameShare.html');
+ this.load.html(gameReport, 'resources/html/gameReport.html');
}
create() {
@@ -64,6 +68,19 @@ export class MenuScene extends Phaser.Scene {
}
});
+ this.gameReportElement = this.add.dom(middleX, -400).createFromCache(gameReport);
+ this.revealMenusAfterInit(this.gameReportElement, gameReport);
+ this.gameReportElement.addListener('click');
+ this.gameReportElement.on('click', (event:MouseEvent) => {
+ event.preventDefault();
+ if((event?.target as HTMLInputElement).id === 'gameReportFormSubmit') {
+ this.submitReport();
+ }else if((event?.target as HTMLInputElement).id === 'gameReportFormCancel') {
+ this.closeGameReport();
+ }
+ });
+ mediaManager.setShowReportModalCallBacks(this.openGameReport.bind(this));
+
this.input.keyboard.on('keyup-TAB', () => {
this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu();
});
@@ -221,6 +238,71 @@ export class MenuScene extends Phaser.Scene {
});
}
+ private openGameReport(userId: string, userName: string|undefined){
+ if (this.gameReportOpened) {
+ this.closeGameReport();
+ return;
+ }
+
+ //close all
+ this.closeAll();
+
+ const gameTitleReport = this.gameReportElement.getChildByID('nameReported') as HTMLElement;
+ gameTitleReport.innerText = userName ? `Report user: ${userName}` : 'Report user';
+ const gameIdUserReported = this.gameReportElement.getChildByID('idUserReported') as HTMLInputElement;
+ gameIdUserReported.value = userId;
+
+ this.gameReportOpened = true;
+ let middleY = (window.innerHeight / 3) - (257);
+ if(middleY < 0){
+ middleY = 0;
+ }
+ let middleX = (window.innerWidth / 3) - 298;
+ if(middleX < 0){
+ middleX = 0;
+ }
+
+ gameManager.getCurrentGameScene(this).userInputManager.clearAllInputKeyboard();
+
+ this.tweens.add({
+ targets: this.gameReportElement,
+ y: middleY,
+ x: middleX,
+ duration: 1000,
+ ease: 'Power3'
+ });
+ return;
+ }
+
+ private closeGameReport(): void{
+ this.gameReportOpened = false;
+ gameManager.getCurrentGameScene(this).userInputManager.initKeyBoardEvent();
+ this.tweens.add({
+ targets: this.gameReportElement,
+ y: -400,
+ duration: 1000,
+ ease: 'Power3'
+ });
+ }
+
+ private submitReport(): void{
+ const gamePError = this.gameReportElement.getChildByID('gameReportErr') as HTMLParagraphElement;
+ gamePError.innerText = '';
+ gamePError.style.display = 'none';
+ const gameTextArea = this.gameReportElement.getChildByID('gameReportInput') as HTMLInputElement;
+ const gameIdUserReported = this.gameReportElement.getChildByID('idUserReported') as HTMLInputElement;
+ if(!gameTextArea || !gameTextArea.value || !gameIdUserReported || !gameIdUserReported.value){
+ gamePError.innerText = 'Report message cannot to be empty.';
+ gamePError.style.display = 'block';
+ return;
+ }
+ gameManager.getCurrentGameScene(this).connection.emitReportPlayerMessage(
+ parseInt(gameIdUserReported.value),
+ gameTextArea.value
+ );
+ this.closeGameReport();
+ }
+
private onMenuClick(event:MouseEvent) {
event.preventDefault();
@@ -280,5 +362,6 @@ export class MenuScene extends Phaser.Scene {
private closeAll(){
this.closeGameQualityMenu();
this.closeGameShare();
+ this.closeGameReport();
}
}
diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts
index 7653bc7a..1bd488e9 100644
--- a/front/src/WebRtc/DiscussionManager.ts
+++ b/front/src/WebRtc/DiscussionManager.ts
@@ -1,5 +1,5 @@
import {HtmlUtils} from "./HtmlUtils";
-import {mediaManager, ReportCallback} from "./MediaManager";
+import {mediaManager, ReportCallback, ShowReportCallBack} from "./MediaManager";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {connectionManager} from "../Connexion/ConnectionManager";
import {GameConnexionTypes} from "../Url/UrlManager";
@@ -99,7 +99,7 @@ export class DiscussionManager {
name: string|undefined,
img?: string|undefined,
isMe: boolean = false,
- reportCallback?: ReportCallback
+ showReportCallBack?: ShowReportCallBack
) {
const divParticipant: HTMLDivElement = document.createElement('div');
divParticipant.classList.add('participant');
@@ -128,8 +128,8 @@ export class DiscussionManager {
reportBanUserAction.classList.add('report-btn')
reportBanUserAction.innerText = 'Report';
reportBanUserAction.addEventListener('click', () => {
- if(reportCallback) {
- mediaManager.showReportModal(`${userId}`, name ?? '', reportCallback);
+ if(showReportCallBack) {
+ showReportCallBack(`${userId}`, name);
}else{
console.info('report feature is not activated!');
}
diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts
index 1a84d4a9..78749491 100644
--- a/front/src/WebRtc/MediaManager.ts
+++ b/front/src/WebRtc/MediaManager.ts
@@ -3,6 +3,8 @@ import {HtmlUtils} from "./HtmlUtils";
import {discussionManager, SendMessageCallback} from "./DiscussionManager";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {VIDEO_QUALITY_SELECT} from "../Administration/ConsoleGlobalMessageManager";
+import {connectionManager} from "../Connexion/ConnectionManager";
+import {GameConnexionTypes} from "../Url/UrlManager";
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
const localValueVideo = localStorage.getItem(VIDEO_QUALITY_SELECT);
@@ -23,6 +25,7 @@ export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
export type StartScreenSharingCallback = (media: MediaStream) => void;
export type StopScreenSharingCallback = (media: MediaStream) => void;
export type ReportCallback = (message: string) => void;
+export type ShowReportCallBack = (userId: string, userName: string|undefined) => void;
// TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only)
// TODO: verify that microphone event listeners are not triggered plenty of time NOW (since MediaManager is created many times!!!!)
@@ -46,6 +49,7 @@ export class MediaManager {
updatedLocalStreamCallBacks : Set = new Set();
startScreenSharingCallBacks : Set = new Set();
stopScreenSharingCallBacks : Set = new Set();
+ showReportModalCallBacks : Set = new Set();
private microphoneBtn: HTMLDivElement;
private cinemaBtn: HTMLDivElement;
private monitorBtn: HTMLDivElement;
@@ -469,7 +473,7 @@ export class MediaManager {
return this.getCamera();
}
- addActiveVideo(userId: string, reportCallBack: ReportCallback|undefined, userName: string = ""){
+ addActiveVideo(userId: string, userName: string = "", anonymous: boolean = true){
this.webrtcInAudio.play();
userName = userName.toUpperCase();
@@ -482,7 +486,13 @@ export class MediaManager {
${userName}
` +
- ((reportCallBack!==undefined)?``:'')
+ ((anonymous === false)?`
+
+ `:''
+ )
+
`
@@ -490,18 +500,23 @@ export class MediaManager {
layoutManager.add(DivImportance.Normal, userId, html);
- if (reportCallBack) {
- const reportBtn = HtmlUtils.getElementByIdOrFail(`report-${userId}`);
- reportBtn.addEventListener('click', (e: MouseEvent) => {
- e.preventDefault();
- this.showReportModal(userId, userName, reportCallBack);
- });
- }
-
this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail(userId));
//permit to create participant in discussion part
- this.addNewParticipant(userId, userName, undefined, reportCallBack);
+ const showReportUser = () => {
+ for(const callBack of this.showReportModalCallBacks){
+ callBack(userId, userName);
+ }
+ };
+ this.addNewParticipant(userId, userName, undefined, showReportUser);
+
+ if(!anonymous){
+ const reportBanUserAction: HTMLImageElement = HtmlUtils.getElementByIdOrFail(`report-${userId}`);
+ reportBanUserAction.addEventListener('click', (e) => {
+ e.preventDefault();
+ showReportUser();
+ });
+ }
}
addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){
@@ -645,65 +660,8 @@ export class MediaManager {
return color;
}
- public showReportModal(userId: string, userName: string, reportCallBack: ReportCallback){
- //create report text area
- const mainContainer = HtmlUtils.getElementByIdOrFail('main-container');
-
- const divReport = document.createElement('div');
- divReport.classList.add('modal-report-user');
-
- const inputHidden = document.createElement('input');
- inputHidden.id = 'input-report-user';
- inputHidden.type = 'hidden';
- inputHidden.value = userId;
- divReport.appendChild(inputHidden);
-
- const titleMessage = document.createElement('p');
- titleMessage.id = 'title-report-user';
- titleMessage.innerText = 'Open a report';
- divReport.appendChild(titleMessage);
-
- const bodyMessage = document.createElement('p');
- bodyMessage.id = 'body-report-user';
- bodyMessage.innerText = `You are about to open a report regarding an offensive conduct from user ${userName.toUpperCase()}. Please explain to us how you think ${userName.toUpperCase()} breached the code of conduct.`;
- divReport.appendChild(bodyMessage);
-
- const imgReportUser = document.createElement('img');
- imgReportUser.id = 'img-report-user';
- imgReportUser.src = 'resources/logos/report.svg';
- divReport.appendChild(imgReportUser);
-
- const textareaUser = document.createElement('textarea');
- textareaUser.id = 'textarea-report-user';
- textareaUser.placeholder = 'Write ...';
- divReport.appendChild(textareaUser);
-
- const buttonReport = document.createElement('button');
- buttonReport.id = 'button-save-report-user';
- buttonReport.innerText = 'Report';
- buttonReport.addEventListener('click', () => {
- if(!textareaUser.value){
- textareaUser.style.border = '1px solid red'
- return;
- }
- reportCallBack(textareaUser.value);
- divReport.remove();
- });
- divReport.appendChild(buttonReport);
-
- const buttonCancel = document.createElement('img');
- buttonCancel.id = 'cancel-report-user';
- buttonCancel.src = 'resources/logos/close.svg';
- buttonCancel.addEventListener('click', () => {
- divReport.remove();
- });
- divReport.appendChild(buttonCancel);
-
- mainContainer.appendChild(divReport);
- }
-
- public addNewParticipant(userId: number|string, name: string|undefined, img?: string, reportCallBack?: ReportCallback){
- discussionManager.addParticipant(userId, name, img, false, reportCallBack);
+ public addNewParticipant(userId: number|string, name: string|undefined, img?: string, showReportUserCallBack?: ShowReportCallBack){
+ discussionManager.addParticipant(userId, name, img, false, showReportUserCallBack);
}
public removeParticipant(userId: number|string){
@@ -769,6 +727,10 @@ export class MediaManager {
this.checkActiveUser();
}, this.focused ? 10000 : 1000);
}
+
+ public setShowReportModalCallBacks(callback: ShowReportCallBack){
+ this.showReportModalCallBacks.add(callback);
+ }
}
export const mediaManager = new MediaManager();
diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts
index 90d260ee..bc2590d7 100644
--- a/front/src/WebRtc/SimplePeer.ts
+++ b/front/src/WebRtc/SimplePeer.ts
@@ -11,6 +11,8 @@ import {
import {ScreenSharingPeer} from "./ScreenSharingPeer";
import {MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer";
import {RoomConnection} from "../Connexion/RoomConnection";
+import {connectionManager} from "../Connexion/ConnectionManager";
+import {GameConnexionTypes} from "../Url/UrlManager";
export interface UserSimplePeerInterface{
userId: number;
@@ -134,11 +136,7 @@ export class SimplePeer {
mediaManager.removeActiveVideo("" + user.userId);
- const reportCallback = this.enableReporting ? (comment: string) => {
- this.reportUser(user.userId, comment);
- } : undefined;
-
- mediaManager.addActiveVideo("" + user.userId, reportCallback, name);
+ mediaManager.addActiveVideo("" + user.userId, name, connectionManager.getConnexionType === GameConnexionTypes.anonymous);
const peer = new VideoPeer(user.userId, user.initiator ? user.initiator : false, this.Connection);
@@ -391,13 +389,6 @@ export class SimplePeer {
}
}
- /**
- * Triggered locally when clicking on the report button
- */
- public reportUser(userId: number, message: string) {
- this.Connection.emitReportPlayerMessage(userId, message)
- }
-
private sendLocalScreenSharingStreamToUser(userId: number): void {
// If a connection already exists with user (because it is already sharing a screen with us... let's use this connection)
if (this.PeerScreenSharingConnectionArray.has(userId)) {
diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts
index 48e8a1a4..e9bccef8 100644
--- a/pusher/src/Services/AdminApi.ts
+++ b/pusher/src/Services/AdminApi.ts
@@ -105,11 +105,12 @@ class AdminApi {
return res.data;
}
- reportPlayer(reportedUserUuid: string, reportedUserComment: string, reporterUserUuid: string) {
+ reportPlayer(reportedUserUuid: string, reportedUserComment: string, reporterUserUuid: string, reportWorldSlug: string) {
return Axios.post(`${ADMIN_API_URL}/api/report`, {
reportedUserUuid,
reportedUserComment,
reporterUserUuid,
+ reportWorldSlug
},
{
headers: {"Authorization": `${ADMIN_API_TOKEN}`}
diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts
index 2f86ae19..7bd50b32 100644
--- a/pusher/src/Services/SocketManager.ts
+++ b/pusher/src/Services/SocketManager.ts
@@ -304,7 +304,7 @@ export class SocketManager implements ZoneEventListener {
throw 'reported socket user not found';
}
//TODO report user on admin application
- await adminApi.reportPlayer(reportedSocket.userUuid, reportPlayerMessage.getReportcomment(), client.userUuid)
+ await adminApi.reportPlayer(reportedSocket.userUuid, reportPlayerMessage.getReportcomment(), client.userUuid, client.roomId.split('/')[2])
} catch (e) {
console.error('An error occurred on "handleReportMessage"');
console.error(e);