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/gameMenu.html b/front/dist/resources/html/gameMenu.html
index 88c76ca2..665d4316 100644
--- a/front/dist/resources/html/gameMenu.html
+++ b/front/dist/resources/html/gameMenu.html
@@ -15,6 +15,14 @@
#gameMenu section {
margin: 10px;
}
+ section#socialLinks{
+ position: absolute;
+ margin-bottom: 0;
+ }
+ section#socialLinks img{
+ width: 32px;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
+ }
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/logo.png b/front/dist/resources/logos/logo.png
new file mode 100644
index 00000000..f4440ad5
Binary files /dev/null and b/front/dist/resources/logos/logo.png differ
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/objects/facebook-icon.png b/front/dist/resources/objects/facebook-icon.png
new file mode 100644
index 00000000..7b74b9bf
Binary files /dev/null and b/front/dist/resources/objects/facebook-icon.png differ
diff --git a/front/dist/resources/objects/talk.png b/front/dist/resources/objects/talk.png
index b9ecdb30..bc06d3b0 100644
Binary files a/front/dist/resources/objects/talk.png and b/front/dist/resources/objects/talk.png differ
diff --git a/front/dist/resources/objects/twitter-icon.png b/front/dist/resources/objects/twitter-icon.png
new file mode 100644
index 00000000..f2fa90f1
Binary files /dev/null and b/front/dist/resources/objects/twitter-icon.png differ
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css
index 2e2c6c10..111700d1 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{
@@ -352,6 +390,7 @@ body {
#cowebsite {
position: fixed;
transition: transform 0.5s;
+ background-color: white;
}
#cowebsite.loading {
background-color: gray;
@@ -1078,7 +1117,7 @@ div.modal-report-user{
white-space: pre-wrap;
word-wrap: break-word;
}
-.discussion .messages .message p.a{
+.discussion .messages .message p a{
color: white;
}
diff --git a/front/src/Phaser/Components/Loader.ts b/front/src/Phaser/Components/Loader.ts
index ab9c0d95..0eda58dc 100644
--- a/front/src/Phaser/Components/Loader.ts
+++ b/front/src/Phaser/Components/Loader.ts
@@ -1,14 +1,50 @@
+import ImageFrameConfig = Phaser.Types.Loader.FileTypes.ImageFrameConfig;
-export const addLoader = (scene:Phaser.Scene): void => {
- const loadingText = scene.add.text(scene.game.renderer.width / 2, 200, 'Loading');
+const LogoNameIndex: string = 'logoLoading';
+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((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();
+ 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) => {
progress.clear();
- progress.fillStyle(0xffffff, 1);
- progress.fillRect(0, 270, 800 * value, 60);
+ progress.fillStyle(0xBBBBBB, 1);
+ progress.fillRect((scene.game.renderer.width - loadingBarWidth) / 2, scene.game.renderer.height / 2 + 50, loadingBarWidth * value, loadingBarHeight);
});
scene.load.on('complete', () => {
- loadingText.destroy();
+ if(loadingText){
+ loadingText.destroy();
+ }
+ promiseLoadLogoTexture.then((resLoadingImage: Phaser.GameObjects.Image) => {
+ resLoadingImage.destroy();
+ });
progress.destroy();
+ progressContainer.destroy();
});
-}
\ No newline at end of file
+}
diff --git a/front/src/Phaser/Components/OpenChatIcon.ts b/front/src/Phaser/Components/OpenChatIcon.ts
index bf293bab..1e9429e8 100644
--- a/front/src/Phaser/Components/OpenChatIcon.ts
+++ b/front/src/Phaser/Components/OpenChatIcon.ts
@@ -3,14 +3,12 @@ import {discussionManager} from "../../WebRtc/DiscussionManager";
export const openChatIconName = 'openChatIcon';
export class OpenChatIcon extends Phaser.GameObjects.Image {
constructor(scene: Phaser.Scene, x: number, y: number) {
- super(scene, x, y, openChatIconName);
+ super(scene, x, y, openChatIconName, 3);
scene.add.existing(this);
this.setScrollFactor(0, 0);
this.setOrigin(0, 1);
- this.displayWidth = 30;
- this.displayHeight = 30;
this.setInteractive();
- this.setVisible(false)
+ this.setVisible(false);
this.setDepth(99999);
this.on("pointerup", () => discussionManager.showDiscussionPart());
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 4f959ee6..5ed2db87 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;
@@ -644,7 +644,7 @@ export class GameScene extends ResizableScene implements CenterListener {
coWebsiteManager.closeCoWebsite();
}else{
const openWebsiteFunction = () => {
- coWebsiteManager.loadCoWebsite(newValue as string);
+ coWebsiteManager.loadCoWebsite(newValue as string, allProps.get('openWebsitePolicy') as string | undefined);
layoutManager.removeActionButton('openWebsite', this.userInputManager);
};
@@ -1202,6 +1202,7 @@ export class GameScene extends ResizableScene implements CenterListener {
private reposition(): void {
this.presentationModeSprite.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
this.updateCameraOffset();
diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts
index 8bd64cd1..872af30c 100644
--- a/front/src/Phaser/Menu/MenuScene.ts
+++ b/front/src/Phaser/Menu/MenuScene.ts
@@ -2,14 +2,17 @@ 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";
+import {GameConnexionTypes} from "../../Url/UrlManager";
+import {connectionManager} from "../../Connexion/ConnectionManager";
export const MenuSceneName = 'MenuScene';
const gameMenuKey = 'gameMenu';
const gameMenuIconKey = 'gameMenuIcon';
const gameSettingsMenuKey = 'gameSettingsMenu';
const gameShare = 'gameShare';
+const gameReport = 'gameReport';
const closedSideMenuX = -200;
const openedSideMenuX = 0;
@@ -21,9 +24,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 +45,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 +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.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu();
});
@@ -98,6 +117,11 @@ export class MenuScene extends Phaser.Scene {
const adminSection = this.menuElement.getChildByID('adminConsoleSection') as HTMLElement;
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({
targets: this.menuElement,
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) {
+ if((event?.target as HTMLInputElement).classList.contains('not-button')){
+ return;
+ }
event.preventDefault();
switch ((event?.target as HTMLInputElement).id) {
@@ -280,5 +372,6 @@ export class MenuScene extends Phaser.Scene {
private closeAll(){
this.closeGameQualityMenu();
this.closeGameShare();
+ this.closeGameReport();
}
}
diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts
index e9564222..ef73ac1d 100644
--- a/front/src/WebRtc/CoWebsiteManager.ts
+++ b/front/src/WebRtc/CoWebsiteManager.ts
@@ -42,7 +42,7 @@ class CoWebsiteManager {
this.opened = iframeStates.opened;
}
- public loadCoWebsite(url: string): void {
+ public loadCoWebsite(url: string, allowPolicy?: string): void {
this.load();
this.cowebsiteDiv.innerHTML = `