Merge pull request #704 from thecodingmachine/develop

Deploy 2021-02-04
This commit is contained in:
David Négrier 2021-02-04 16:16:27 +01:00 committed by GitHub
commit 10e4f515b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 365 additions and 115 deletions

View File

@ -100,11 +100,12 @@ class AdminApi {
return res.data; 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`, { return Axios.post(`${ADMIN_API_URL}/api/report`, {
reportedUserUuid, reportedUserUuid,
reportedUserComment, reportedUserComment,
reporterUserUuid, reporterUserUuid,
reportWorldSlug,
}, },
{ {
headers: {"Authorization": `${ADMIN_API_TOKEN}`} headers: {"Authorization": `${ADMIN_API_TOKEN}`}

View File

@ -15,6 +15,14 @@
#gameMenu section { #gameMenu section {
margin: 10px; margin: 10px;
} }
section#socialLinks{
position: absolute;
margin-bottom: 0;
}
section#socialLinks img{
width: 32px;
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
</style> </style>
<div id="gameMenu" hidden> <div id="gameMenu" hidden>
@ -38,5 +46,9 @@
<section id="adminConsoleSection" hidden> <section id="adminConsoleSection" hidden>
<button id="adminConsoleButton">Admin console</button> <button id="adminConsoleButton">Admin console</button>
</section> </section>
<section id="socialLinks" hidden>
<a class="not-button" href="https://www.facebook.com/workadventurebytcm" target="_blank"><img class="not-button" src="/resources/objects/facebook-icon.png"/></a>
<a class="not-button" href="https://twitter.com/Workadventure_" target="_blank"><img class="not-button" src="/resources/objects/twitter-icon.png"/></a>
</section>
</main> </main>
</div> </div>

View File

@ -0,0 +1,104 @@
<style>
*{
font-family: 'Open Sans', sans-serif;
cursor: url('/resources/logos/cursor_normal.png'), auto;
}
* a, button, input{
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
#gameReport {
background: #eceeee;
border: 1px solid #42464b;
border-radius: 6px;
margin: 2px auto 0;
width: 298px;
height: 220px;
}
#gameReport .cautiousText {
font-size: 50%;
}
#gameReport h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#gameReport textarea {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 100px;
transition: box-shadow 0.3s;
width: 100%;
}
#gameReport section {
margin: 10px;
}
#gameReport section.action{
text-align: center;
margin: 0;
}
#gameReport button {
margin-top: 10px;
background-color: black;
color: white;
border-radius: 7px;
padding-bottom: 4px;
width: 60px;
}
#gameReport button#gameReportFormCancel {
background-color: #c7c7c700;
color: #292929;
}
#gameReport section a{
text-align: center;
font-size: 12px;
margin: 0 6px;
color: black;
}
#gameReport section h6,
#gameReport section h5{
margin: 1px;
}
#gameReport section.text-center{
text-align: center;
}
#gameReport section p{
font-size: 8px;
margin: 0px 70px;
}
#gameReport section p.err{
color: red;
display: none;
}
#gameReport section p.info{
display: none;
}
</style>
<form id="gameReport" hidden>
<section class="text-center">
<h5 id="nameReported"></h5>
<input type="hidden" id="idUserReported"/>
</section>
<section>
<h6>Message</h6>
<textarea type="text" name="report" id="gameReportInput"></textarea>
<p class="err" id="gameReportErr"></p>
</section>
<section class="action">
<button type="submit" id="gameReportFormSubmit">Submit</button>
<button type="submit" id="gameReportFormCancel">Close</button>
</section>
</form>

BIN
front/dist/resources/logos/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1 @@
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56.48 56.48"><defs><style>.cls-1{fill:#e76e54;}.cls-2{fill:#fff;}</style></defs><path class="cls-1" d="M39.94,512H16.54L0,495.46v-23.4l16.54-16.54h23.4l16.54,16.54v23.4Z" transform="translate(0 -455.52)"/><path class="cls-2" d="M33.54,485.52H23l-1.77-21.18H35.3Z" transform="translate(0 -455.52)"/><path class="cls-2" d="M23,492.58H33.54v10.59H23Z" transform="translate(0 -455.52)"/></svg>

After

Width:  |  Height:  |  Size: 477 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -39,6 +39,7 @@ body .message-info.warning{
position: relative; position: relative;
transition: all 0.2s ease; transition: all 0.2s ease;
background-color: #00000099; background-color: #00000099;
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
} }
.video-container i{ .video-container i{
position: absolute; position: absolute;
@ -53,25 +54,62 @@ body .message-info.warning{
font-size: 28px; font-size: 28px;
color: white; color: white;
} }
.video-container img.active{
display: block;
}
.video-container img{ .video-container img{
position: absolute; position: absolute;
display: none; display: none;
width: 15px; width: 25px;
height: 15px; height: 25px;
background: #d93025;
border-radius: 48px;
left: 5px; left: 5px;
bottom: 5px; bottom: 5px;
padding: 10px; padding: 10px;
z-index: 2; 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; 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{ .video-container video{
@ -352,6 +390,7 @@ body {
#cowebsite { #cowebsite {
position: fixed; position: fixed;
transition: transform 0.5s; transition: transform 0.5s;
background-color: white;
} }
#cowebsite.loading { #cowebsite.loading {
background-color: gray; background-color: gray;
@ -1078,7 +1117,7 @@ div.modal-report-user{
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
} }
.discussion .messages .message p.a{ .discussion .messages .message p a{
color: white; color: white;
} }

View File

@ -1,14 +1,50 @@
import ImageFrameConfig = Phaser.Types.Loader.FileTypes.ImageFrameConfig;
export const addLoader = (scene:Phaser.Scene): void => { const LogoNameIndex: string = 'logoLoading';
const loadingText = scene.add.text(scene.game.renderer.width / 2, 200, 'Loading'); const TextName: string = 'Loading...';
const LogoResource: string = 'resources/logos/logo.png';
const LogoFrame: ImageFrameConfig = {frameWidth: 307, frameHeight: 59};
export const addLoader = (scene: Phaser.Scene): void => {
let loadingText: Phaser.GameObjects.Text|null = null;
const loadingBarWidth: number = Math.floor(scene.game.renderer.width / 3);
const loadingBarHeight: number = 16;
const padding: number = 5;
const promiseLoadLogoTexture = new Promise<Phaser.GameObjects.Image>((res) => {
if(scene.load.textureManager.exists(LogoNameIndex)){
return res(scene.add.image(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 150, LogoNameIndex));
}else{
//add loading if logo image is not ready
loadingText = scene.add.text(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 50, TextName);
}
scene.load.spritesheet(LogoNameIndex, LogoResource, LogoFrame);
scene.load.once(`filecomplete-spritesheet-${LogoNameIndex}`, () => {
if(loadingText){
loadingText.destroy();
}
return res(scene.add.image(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 150, LogoNameIndex));
});
});
const progressContainer = scene.add.graphics();
const progress = scene.add.graphics(); const progress = scene.add.graphics();
progressContainer.fillStyle(0x444444, 0.8);
progressContainer.fillRect((scene.game.renderer.width - loadingBarWidth) / 2 - padding, scene.game.renderer.height / 2 + 50 - padding, loadingBarWidth + padding * 2, loadingBarHeight + padding * 2);
scene.load.on('progress', (value: number) => { scene.load.on('progress', (value: number) => {
progress.clear(); progress.clear();
progress.fillStyle(0xffffff, 1); progress.fillStyle(0xBBBBBB, 1);
progress.fillRect(0, 270, 800 * value, 60); progress.fillRect((scene.game.renderer.width - loadingBarWidth) / 2, scene.game.renderer.height / 2 + 50, loadingBarWidth * value, loadingBarHeight);
}); });
scene.load.on('complete', () => { scene.load.on('complete', () => {
if(loadingText){
loadingText.destroy(); loadingText.destroy();
}
promiseLoadLogoTexture.then((resLoadingImage: Phaser.GameObjects.Image) => {
resLoadingImage.destroy();
});
progress.destroy(); progress.destroy();
progressContainer.destroy();
}); });
} }

View File

@ -3,14 +3,12 @@ import {discussionManager} from "../../WebRtc/DiscussionManager";
export const openChatIconName = 'openChatIcon'; export const openChatIconName = 'openChatIcon';
export class OpenChatIcon extends Phaser.GameObjects.Image { export class OpenChatIcon extends Phaser.GameObjects.Image {
constructor(scene: Phaser.Scene, x: number, y: number) { constructor(scene: Phaser.Scene, x: number, y: number) {
super(scene, x, y, openChatIconName); super(scene, x, y, openChatIconName, 3);
scene.add.existing(this); scene.add.existing(this);
this.setScrollFactor(0, 0); this.setScrollFactor(0, 0);
this.setOrigin(0, 1); this.setOrigin(0, 1);
this.displayWidth = 30;
this.displayHeight = 30;
this.setInteractive(); this.setInteractive();
this.setVisible(false) this.setVisible(false);
this.setDepth(99999); this.setDepth(99999);
this.on("pointerup", () => discussionManager.showDiscussionPart()); this.on("pointerup", () => discussionManager.showDiscussionPart());

View File

@ -154,7 +154,7 @@ export class GameScene extends ResizableScene implements CenterListener {
private actionableItems: Map<number, ActionableItem> = new Map<number, ActionableItem>(); private actionableItems: Map<number, ActionableItem> = new Map<number, ActionableItem>();
// The item that can be selected by pressing the space key. // The item that can be selected by pressing the space key.
private outlinedItem: ActionableItem|null = null; private outlinedItem: ActionableItem|null = null;
private userInputManager!: UserInputManager; public userInputManager!: UserInputManager;
private isReconnecting: boolean = false; private isReconnecting: boolean = false;
private startLayerName!: string | null; private startLayerName!: string | null;
private openChatIcon!: OpenChatIcon; private openChatIcon!: OpenChatIcon;
@ -644,7 +644,7 @@ export class GameScene extends ResizableScene implements CenterListener {
coWebsiteManager.closeCoWebsite(); coWebsiteManager.closeCoWebsite();
}else{ }else{
const openWebsiteFunction = () => { const openWebsiteFunction = () => {
coWebsiteManager.loadCoWebsite(newValue as string); coWebsiteManager.loadCoWebsite(newValue as string, allProps.get('openWebsitePolicy') as string | undefined);
layoutManager.removeActionButton('openWebsite', this.userInputManager); layoutManager.removeActionButton('openWebsite', this.userInputManager);
}; };
@ -1202,6 +1202,7 @@ export class GameScene extends ResizableScene implements CenterListener {
private reposition(): void { private reposition(): void {
this.presentationModeSprite.setY(this.game.renderer.height - 2); this.presentationModeSprite.setY(this.game.renderer.height - 2);
this.chatModeSprite.setY(this.game.renderer.height - 2); this.chatModeSprite.setY(this.game.renderer.height - 2);
this.openChatIcon.setY(this.game.renderer.height - 2);
// Recompute camera offset if needed // Recompute camera offset if needed
this.updateCameraOffset(); this.updateCameraOffset();

View File

@ -2,14 +2,17 @@ import {LoginScene, LoginSceneName} from "../Login/LoginScene";
import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene"; import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene";
import {gameManager} from "../Game/GameManager"; import {gameManager} from "../Game/GameManager";
import {localUserStore} from "../../Connexion/LocalUserStore"; import {localUserStore} from "../../Connexion/LocalUserStore";
import {mediaManager} from "../../WebRtc/MediaManager"; import {mediaManager, ReportCallback, ShowReportCallBack} from "../../WebRtc/MediaManager";
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
import {GameConnexionTypes} from "../../Url/UrlManager";
import {connectionManager} from "../../Connexion/ConnectionManager";
export const MenuSceneName = 'MenuScene'; export const MenuSceneName = 'MenuScene';
const gameMenuKey = 'gameMenu'; const gameMenuKey = 'gameMenu';
const gameMenuIconKey = 'gameMenuIcon'; const gameMenuIconKey = 'gameMenuIcon';
const gameSettingsMenuKey = 'gameSettingsMenu'; const gameSettingsMenuKey = 'gameSettingsMenu';
const gameShare = 'gameShare'; const gameShare = 'gameShare';
const gameReport = 'gameReport';
const closedSideMenuX = -200; const closedSideMenuX = -200;
const openedSideMenuX = 0; const openedSideMenuX = 0;
@ -21,9 +24,11 @@ export class MenuScene extends Phaser.Scene {
private menuElement!: Phaser.GameObjects.DOMElement; private menuElement!: Phaser.GameObjects.DOMElement;
private gameQualityMenuElement!: Phaser.GameObjects.DOMElement; private gameQualityMenuElement!: Phaser.GameObjects.DOMElement;
private gameShareElement!: Phaser.GameObjects.DOMElement; private gameShareElement!: Phaser.GameObjects.DOMElement;
private gameReportElement!: Phaser.GameObjects.DOMElement;
private sideMenuOpened = false; private sideMenuOpened = false;
private settingsMenuOpened = false; private settingsMenuOpened = false;
private gameShareOpened = false; private gameShareOpened = false;
private gameReportOpened = false;
private gameQualityValue: number; private gameQualityValue: number;
private videoQualityValue: number; private videoQualityValue: number;
private menuButton!: Phaser.GameObjects.DOMElement; private menuButton!: Phaser.GameObjects.DOMElement;
@ -40,6 +45,7 @@ export class MenuScene extends Phaser.Scene {
this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html'); this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html');
this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html'); this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html');
this.load.html(gameShare, 'resources/html/gameShare.html'); this.load.html(gameShare, 'resources/html/gameShare.html');
this.load.html(gameReport, 'resources/html/gameReport.html');
} }
create() { create() {
@ -64,6 +70,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.input.keyboard.on('keyup-TAB', () => {
this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu(); this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu();
}); });
@ -98,6 +117,11 @@ export class MenuScene extends Phaser.Scene {
const adminSection = this.menuElement.getChildByID('adminConsoleSection') as HTMLElement; const adminSection = this.menuElement.getChildByID('adminConsoleSection') as HTMLElement;
adminSection.hidden = false; adminSection.hidden = false;
} }
//TODO bind with future metadata of card
//if (connectionManager.getConnexionType === GameConnexionTypes.anonymous){
const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement;
adminSection.hidden = false;
//}
this.tweens.add({ this.tweens.add({
targets: this.menuElement, targets: this.menuElement,
x: openedSideMenuX, x: openedSideMenuX,
@ -221,7 +245,75 @@ 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) { private onMenuClick(event:MouseEvent) {
if((event?.target as HTMLInputElement).classList.contains('not-button')){
return;
}
event.preventDefault(); event.preventDefault();
switch ((event?.target as HTMLInputElement).id) { switch ((event?.target as HTMLInputElement).id) {
@ -280,5 +372,6 @@ export class MenuScene extends Phaser.Scene {
private closeAll(){ private closeAll(){
this.closeGameQualityMenu(); this.closeGameQualityMenu();
this.closeGameShare(); this.closeGameShare();
this.closeGameReport();
} }
} }

View File

@ -42,7 +42,7 @@ class CoWebsiteManager {
this.opened = iframeStates.opened; this.opened = iframeStates.opened;
} }
public loadCoWebsite(url: string): void { public loadCoWebsite(url: string, allowPolicy?: string): void {
this.load(); this.load();
this.cowebsiteDiv.innerHTML = `<button class="close-btn" id="cowebsite-close"> this.cowebsiteDiv.innerHTML = `<button class="close-btn" id="cowebsite-close">
<img src="resources/logos/close.svg"> <img src="resources/logos/close.svg">
@ -56,6 +56,9 @@ class CoWebsiteManager {
const iframe = document.createElement('iframe'); const iframe = document.createElement('iframe');
iframe.id = 'cowebsite-iframe'; iframe.id = 'cowebsite-iframe';
iframe.src = url; iframe.src = url;
if (allowPolicy) {
iframe.allow = allowPolicy;
}
const onloadPromise = new Promise((resolve) => { const onloadPromise = new Promise((resolve) => {
iframe.onload = () => resolve(); iframe.onload = () => resolve();
}); });

View File

@ -1,5 +1,5 @@
import {HtmlUtils} from "./HtmlUtils"; import {HtmlUtils} from "./HtmlUtils";
import {mediaManager, ReportCallback} from "./MediaManager"; import {mediaManager, ReportCallback, ShowReportCallBack} from "./MediaManager";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {connectionManager} from "../Connexion/ConnectionManager"; import {connectionManager} from "../Connexion/ConnectionManager";
import {GameConnexionTypes} from "../Url/UrlManager"; import {GameConnexionTypes} from "../Url/UrlManager";
@ -99,7 +99,7 @@ export class DiscussionManager {
name: string|undefined, name: string|undefined,
img?: string|undefined, img?: string|undefined,
isMe: boolean = false, isMe: boolean = false,
reportCallback?: ReportCallback showReportCallBack?: ShowReportCallBack
) { ) {
const divParticipant: HTMLDivElement = document.createElement('div'); const divParticipant: HTMLDivElement = document.createElement('div');
divParticipant.classList.add('participant'); divParticipant.classList.add('participant');
@ -128,8 +128,8 @@ export class DiscussionManager {
reportBanUserAction.classList.add('report-btn') reportBanUserAction.classList.add('report-btn')
reportBanUserAction.innerText = 'Report'; reportBanUserAction.innerText = 'Report';
reportBanUserAction.addEventListener('click', () => { reportBanUserAction.addEventListener('click', () => {
if(reportCallback) { if(showReportCallBack) {
mediaManager.showReportModal(`${userId}`, name ?? '', reportCallback); showReportCallBack(`${userId}`, name);
}else{ }else{
console.info('report feature is not activated!'); console.info('report feature is not activated!');
} }

View File

@ -7,6 +7,14 @@ export class HtmlUtils {
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 {
const elem = document.querySelector<T>(selector);
if (HtmlUtils.isHtmlElement<T>(elem)) {
return elem;
}
throw new Error("Cannot find HTML element with selector '"+selector+"'");
}
public static removeElementByIdOrFail<T extends HTMLElement>(id: string): T { public static removeElementByIdOrFail<T extends HTMLElement>(id: string): T {
const elem = document.getElementById(id); const elem = document.getElementById(id);
if (HtmlUtils.isHtmlElement<T>(elem)) { if (HtmlUtils.isHtmlElement<T>(elem)) {

View File

@ -212,7 +212,7 @@ class LayoutManager {
* Tries to find the biggest available box of remaining space (this is a space where we can center the character) * Tries to find the biggest available box of remaining space (this is a space where we can center the character)
*/ */
public findBiggestAvailableArray(): {xStart: number, yStart: number, xEnd: number, yEnd: number} { public findBiggestAvailableArray(): {xStart: number, yStart: number, xEnd: number, yEnd: number} {
const game = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('game'); const game = HtmlUtils.querySelectorOrFail<HTMLCanvasElement>('#game canvas');
if (this.mode === LayoutMode.VideoChat) { if (this.mode === LayoutMode.VideoChat) {
const children = document.querySelectorAll<HTMLDivElement>('div.chat-mode > div'); const children = document.querySelectorAll<HTMLDivElement>('div.chat-mode > div');
const htmlChildren = Array.from(children.values()); const htmlChildren = Array.from(children.values());

View File

@ -3,6 +3,8 @@ import {HtmlUtils} from "./HtmlUtils";
import {discussionManager, SendMessageCallback} from "./DiscussionManager"; import {discussionManager, SendMessageCallback} from "./DiscussionManager";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {VIDEO_QUALITY_SELECT} from "../Administration/ConsoleGlobalMessageManager"; 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 declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
const localValueVideo = localStorage.getItem(VIDEO_QUALITY_SELECT); 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 StartScreenSharingCallback = (media: MediaStream) => void;
export type StopScreenSharingCallback = (media: MediaStream) => void; export type StopScreenSharingCallback = (media: MediaStream) => void;
export type ReportCallback = (message: string) => 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: 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!!!!) // 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<UpdatedLocalStreamCallback> = new Set<UpdatedLocalStreamCallback>(); updatedLocalStreamCallBacks : Set<UpdatedLocalStreamCallback> = new Set<UpdatedLocalStreamCallback>();
startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>(); startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>(); stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>();
private microphoneBtn: HTMLDivElement; private microphoneBtn: HTMLDivElement;
private cinemaBtn: HTMLDivElement; private cinemaBtn: HTMLDivElement;
private monitorBtn: HTMLDivElement; private monitorBtn: HTMLDivElement;
@ -469,7 +473,7 @@ export class MediaManager {
return this.getCamera(); return this.getCamera();
} }
addActiveVideo(userId: string, reportCallBack: ReportCallback|undefined, userName: string = ""){ addActiveVideo(userId: string, userName: string = "", anonymous: boolean = true){
this.webrtcInAudio.play(); this.webrtcInAudio.play();
userName = userName.toUpperCase(); userName = userName.toUpperCase();
@ -482,7 +486,13 @@ export class MediaManager {
<i id="name-${userId}" style="background-color: ${color};">${userName}</i> <i id="name-${userId}" style="background-color: ${color};">${userName}</i>
<img id="microphone-${userId}" src="resources/logos/microphone-close.svg"> <img id="microphone-${userId}" src="resources/logos/microphone-close.svg">
` + ` +
((reportCallBack!==undefined)?`<img id="report-${userId}" class="report active" src="resources/logos/report.svg">`:'') ((anonymous === false)?`
<button id="report-${userId}" class="report">
<img src="resources/logos/report.svg">
<span>Report</span>
</button>
`:''
)
+ +
`<video id="${userId}" autoplay></video> `<video id="${userId}" autoplay></video>
</div> </div>
@ -490,18 +500,23 @@ export class MediaManager {
layoutManager.add(DivImportance.Normal, userId, html); layoutManager.add(DivImportance.Normal, userId, html);
if (reportCallBack) {
const reportBtn = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(`report-${userId}`);
reportBtn.addEventListener('click', (e: MouseEvent) => {
e.preventDefault();
this.showReportModal(userId, userName, reportCallBack);
});
}
this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail<HTMLVideoElement>(userId)); this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail<HTMLVideoElement>(userId));
//permit to create participant in discussion part //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<HTMLImageElement>(`report-${userId}`);
reportBanUserAction.addEventListener('click', (e) => {
e.preventDefault();
showReportUser();
});
}
} }
addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){ addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){
@ -645,65 +660,8 @@ export class MediaManager {
return color; return color;
} }
public showReportModal(userId: string, userName: string, reportCallBack: ReportCallback){ public addNewParticipant(userId: number|string, name: string|undefined, img?: string, showReportUserCallBack?: ShowReportCallBack){
//create report text area discussionManager.addParticipant(userId, name, img, false, showReportUserCallBack);
const mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('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 removeParticipant(userId: number|string){ public removeParticipant(userId: number|string){
@ -769,6 +727,10 @@ export class MediaManager {
this.checkActiveUser(); this.checkActiveUser();
}, this.focused ? 10000 : 1000); }, this.focused ? 10000 : 1000);
} }
public setShowReportModalCallBacks(callback: ShowReportCallBack){
this.showReportModalCallBacks.add(callback);
}
} }
export const mediaManager = new MediaManager(); export const mediaManager = new MediaManager();

View File

@ -11,6 +11,8 @@ import {
import {ScreenSharingPeer} from "./ScreenSharingPeer"; import {ScreenSharingPeer} from "./ScreenSharingPeer";
import {MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer"; import {MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer";
import {RoomConnection} from "../Connexion/RoomConnection"; import {RoomConnection} from "../Connexion/RoomConnection";
import {connectionManager} from "../Connexion/ConnectionManager";
import {GameConnexionTypes} from "../Url/UrlManager";
export interface UserSimplePeerInterface{ export interface UserSimplePeerInterface{
userId: number; userId: number;
@ -134,11 +136,7 @@ export class SimplePeer {
mediaManager.removeActiveVideo("" + user.userId); mediaManager.removeActiveVideo("" + user.userId);
const reportCallback = this.enableReporting ? (comment: string) => { mediaManager.addActiveVideo("" + user.userId, name, connectionManager.getConnexionType === GameConnexionTypes.anonymous);
this.reportUser(user.userId, comment);
} : undefined;
mediaManager.addActiveVideo("" + user.userId, reportCallback, name);
const peer = new VideoPeer(user.userId, user.initiator ? user.initiator : false, this.Connection); 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 { 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 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)) { if (this.PeerScreenSharingConnectionArray.has(userId)) {

View File

@ -105,11 +105,12 @@ class AdminApi {
return res.data; 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`, { return Axios.post(`${ADMIN_API_URL}/api/report`, {
reportedUserUuid, reportedUserUuid,
reportedUserComment, reportedUserComment,
reporterUserUuid, reporterUserUuid,
reportWorldSlug
}, },
{ {
headers: {"Authorization": `${ADMIN_API_TOKEN}`} headers: {"Authorization": `${ADMIN_API_TOKEN}`}

View File

@ -304,7 +304,7 @@ export class SocketManager implements ZoneEventListener {
throw 'reported socket user not found'; throw 'reported socket user not found';
} }
//TODO report user on admin application //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) { } catch (e) {
console.error('An error occurred on "handleReportMessage"'); console.error('An error occurred on "handleReportMessage"');
console.error(e); console.error(e);