diff --git a/front/dist/resources/html/gameReport.html b/front/dist/resources/html/gameReport.html index 9a761c32..59ca3592 100644 --- a/front/dist/resources/html/gameReport.html +++ b/front/dist/resources/html/gameReport.html @@ -12,10 +12,6 @@ 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); @@ -30,6 +26,9 @@ text-align: center; text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff; } + #gameReport h3 { + margin: 0; + } #gameReport textarea { font-size: 70%; background: linear-gradient(top, #d6d7d7, #dee0e0); @@ -51,15 +50,17 @@ } #gameReport button { margin-top: 10px; - background-color: black; + font-size: 60%; + background-color: #dc3545; color: white; border-radius: 7px; - padding-bottom: 4px; - width: 60px; + padding: 3px 10px 3px 10px; } #gameReport button#gameReportFormCancel { background-color: #c7c7c700; color: #292929; + display: block; + float: right; } #gameReport section a{ text-align: center; @@ -74,8 +75,11 @@ #gameReport section.text-center{ text-align: center; } - #gameReport section p{ + #gameReport p{ font-size: 8px; + margin: 3px 0 0 0; + } + #gameReport form p{ margin: 0px 70px; } #gameReport section p.err{ @@ -87,18 +91,32 @@ } -
+ + diff --git a/front/dist/resources/html/gameShare.html b/front/dist/resources/html/gameShare.html index 4e487328..21c65014 100644 --- a/front/dist/resources/html/gameShare.html +++ b/front/dist/resources/html/gameShare.html @@ -14,9 +14,6 @@ width: 298px; height: 150px; } - #gameShare .cautiousText { - font-size: 50%; - } #gameShare h1 { background-image: linear-gradient(top, #f1f3f3, #d4dae0); border-bottom: 1px solid #a6abaf; diff --git a/front/dist/resources/logos/blockSign.svg b/front/dist/resources/logos/blockSign.svg new file mode 100644 index 00000000..c64ba294 --- /dev/null +++ b/front/dist/resources/logos/blockSign.svg @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/front/dist/resources/logos/blockingIcon.png b/front/dist/resources/logos/blockingIcon.png new file mode 100644 index 00000000..ef5f66cc Binary files /dev/null and b/front/dist/resources/logos/blockingIcon.png differ diff --git a/front/dist/resources/logos/cancel.png b/front/dist/resources/logos/cancel.png new file mode 100644 index 00000000..5bf9b6d2 Binary files /dev/null and b/front/dist/resources/logos/cancel.png differ diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 111700d1..4bf05455 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -65,6 +65,12 @@ body .message-info.warning{ padding: 10px; z-index: 2; } +.video-container img.block-logo { + left: 30%; + bottom: 15%; + width: 150px; + height: 150px; +} .video-container button.report{ display: block; @@ -91,7 +97,7 @@ body .message-info.warning{ } .video-container button.report:hover { - width: 94px; + width: 150px; } .video-container button.report img{ @@ -111,6 +117,9 @@ body .message-info.warning{ font-size: 16px; cursor: url('/resources/logos/cursor_pointer.png'), pointer; } +.video-container img.active { + display: block !important; +} .video-container video{ height: 100%; @@ -188,10 +197,7 @@ video#myCamVideo{ transition: all .2s; right: 224px; } -/*.btn-call{ - transition: all .1s; - left: 0px; -}*/ + .btn-cam-action div img{ height: 22px; width: 30px; diff --git a/front/package.json b/front/package.json index 41932ca9..0e50ba80 100644 --- a/front/package.json +++ b/front/package.json @@ -29,6 +29,7 @@ "phaser": "3.24.1", "queue-typescript": "^1.0.1", "quill": "^1.3.7", + "rxjs": "^6.6.3", "simple-peer": "^9.6.2", "socket.io-client": "^2.3.0", "webpack-require-http": "^0.4.3" diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index bd330ad9..8eb7462f 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -464,7 +464,8 @@ export class RoomConnection implements RoomConnection { }); } - public getUserId(): number|null { + public getUserId(): number { + if (this.userId === null) throw 'UserId cannot be null!' return this.userId; } diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index 822e4e36..81ea00e1 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -1,7 +1,6 @@ import {GameScene} from "../Game/GameScene"; import {PointInterface} from "../../Connexion/ConnexionModels"; import {Character} from "../Entity/Character"; -import {Sprite} from "./Sprite"; /** * Class representing the sprite of a remote player (a player that plays on another computer) @@ -23,6 +22,11 @@ export class RemotePlayer extends Character { //set data this.userId = userId; + + //todo: implement on click action + /*this.playerName.setInteractive(); + this.playerName.on('pointerup', () => { + });*/ } updatePosition(position: PointInterface): void { diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index acc1ec3d..ab25c338 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -2,17 +2,16 @@ import {LoginScene, LoginSceneName} from "../Login/LoginScene"; import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene"; import {gameManager} from "../Game/GameManager"; import {localUserStore} from "../../Connexion/LocalUserStore"; -import {mediaManager, ReportCallback, ShowReportCallBack} from "../../WebRtc/MediaManager"; -import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; -import {GameConnexionTypes} from "../../Url/UrlManager"; +import {mediaManager} from "../../WebRtc/MediaManager"; +import {gameReportKey, gameReportRessource, ReportMenu} from "./ReportMenu"; import {connectionManager} from "../../Connexion/ConnectionManager"; +import {GameConnexionTypes} from "../../Url/UrlManager"; 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; @@ -24,11 +23,10 @@ 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 gameReportElement!: ReportMenu; private sideMenuOpened = false; private settingsMenuOpened = false; private gameShareOpened = false; - private gameReportOpened = false; private gameQualityValue: number; private videoQualityValue: number; private menuButton!: Phaser.GameObjects.DOMElement; @@ -45,21 +43,21 @@ 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'); + this.load.html(gameReportKey, gameReportRessource); } create() { this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey); this.menuElement.setOrigin(0); - this.revealMenusAfterInit(this.menuElement, 'gameMenu'); + MenuScene.revealMenusAfterInit(this.menuElement, 'gameMenu'); const middleX = (window.innerWidth / 3) - 298; this.gameQualityMenuElement = this.add.dom(middleX, -400).createFromCache(gameSettingsMenuKey); - this.revealMenusAfterInit(this.gameQualityMenuElement, 'gameQuality'); + MenuScene.revealMenusAfterInit(this.gameQualityMenuElement, 'gameQuality'); this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare); - this.revealMenusAfterInit(this.gameShareElement, gameShare); + MenuScene.revealMenusAfterInit(this.gameShareElement, gameShare); this.gameShareElement.addListener('click'); this.gameShareElement.on('click', (event:MouseEvent) => { event.preventDefault(); @@ -70,18 +68,11 @@ 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(); - } + this.gameReportElement = new ReportMenu(this, connectionManager.getConnexionType === GameConnexionTypes.anonymous); + mediaManager.setShowReportModalCallBacks((userId, userName) => { + this.closeAll(); + this.gameReportElement.open(parseInt(userId), userName); }); - mediaManager.setShowReportModalCallBacks(this.openGameReport.bind(this)); this.input.keyboard.on('keyup-TAB', () => { this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu(); @@ -96,7 +87,8 @@ export class MenuScene extends Phaser.Scene { this.menuElement.on('click', this.onMenuClick.bind(this)); } - private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) { + //todo put this method in a parent menuElement class + static revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) { //Dom elements will appear inside the viewer screen when creating before being moved out of it, which create a flicker effect. //To prevent this, we put a 'hidden' attribute on the root element, we remove it only after the init is done. setTimeout(() => { @@ -245,71 +237,6 @@ 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.clearAllKeys(); - - 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; @@ -372,6 +299,6 @@ export class MenuScene extends Phaser.Scene { private closeAll(){ this.closeGameQualityMenu(); this.closeGameShare(); - this.closeGameReport(); + this.gameReportElement.close(); } } diff --git a/front/src/Phaser/Menu/ReportMenu.ts b/front/src/Phaser/Menu/ReportMenu.ts new file mode 100644 index 00000000..bee86c35 --- /dev/null +++ b/front/src/Phaser/Menu/ReportMenu.ts @@ -0,0 +1,119 @@ +import {MenuScene} from "./MenuScene"; +import {gameManager} from "../Game/GameManager"; +import {blackListManager} from "../../WebRtc/BlackListManager"; + +export const gameReportKey = 'gameReport'; +export const gameReportRessource = 'resources/html/gameReport.html'; + +export class ReportMenu extends Phaser.GameObjects.DOMElement { + private opened: boolean = false; + + private userId!: number; + private userName!: string|undefined; + private anonymous: boolean; + + constructor(scene: Phaser.Scene, anonymous: boolean) { + super(scene, -2000, -2000); + this.anonymous = anonymous; + this.createFromCache(gameReportKey); + + if (this.anonymous) { + const divToHide = this.getChildByID('reportSection') as HTMLElement; + divToHide.hidden = true; + const textToHide = this.getChildByID('askActionP') as HTMLElement; + textToHide.hidden = true; + } + + scene.add.existing(this); + MenuScene.revealMenusAfterInit(this, gameReportKey); + + this.addListener('click'); + this.on('click', (event:MouseEvent) => { + event.preventDefault(); + if ((event?.target as HTMLInputElement).id === 'gameReportFormSubmit') { + this.submitReport(); + } else if((event?.target as HTMLInputElement).id === 'gameReportFormCancel') { + this.close(); + } else if((event?.target as HTMLInputElement).id === 'toggleBlockButton') { + this.toggleBlock(); + } + }); + } + + public open(userId: number, userName: string|undefined): void { + if (this.opened) { + this.close(); + return; + } + + this.userId = userId; + this.userName = userName; + + const mainEl = this.getChildByID('gameReport') as HTMLElement; + this.x = this.getCenteredX(mainEl); + this.y = this.getHiddenY(mainEl); + + const gameTitleReport = this.getChildByID('nameReported') as HTMLElement; + gameTitleReport.innerText = userName || ''; + + const blockButton = this.getChildByID('toggleBlockButton') as HTMLElement; + blockButton.innerText = blackListManager.isBlackListed(this.userId) ? 'Unblock this user' : 'Block this user'; + + this.opened = true; + + gameManager.getCurrentGameScene(this.scene).userInputManager.clearAllKeys(); + + this.scene.tweens.add({ + targets: this, + y: this.getCenteredY(mainEl), + duration: 1000, + ease: 'Power3' + }); + } + + public close(): void { + this.opened = false; + gameManager.getCurrentGameScene(this.scene).userInputManager.initKeyBoardEvent(); + const mainEl = this.getChildByID('gameReport') as HTMLElement; + this.scene.tweens.add({ + targets: this, + y: this.getHiddenY(mainEl), + duration: 1000, + ease: 'Power3' + }); + } + + //todo: into a parent class? + private getCenteredX(mainEl: HTMLElement): number { + return window.innerWidth / 4 - mainEl.clientWidth / 2; + } + private getHiddenY(mainEl: HTMLElement): number { + return - mainEl.clientHeight - 50; + } + private getCenteredY(mainEl: HTMLElement): number { + return window.innerHeight / 4 - mainEl.clientHeight / 2; + } + + private toggleBlock(): void { + !blackListManager.isBlackListed(this.userId) ? blackListManager.blackList(this.userId) : blackListManager.cancelBlackList(this.userId); + this.close(); + } + + private submitReport(): void{ + const gamePError = this.getChildByID('gameReportErr') as HTMLParagraphElement; + gamePError.innerText = ''; + gamePError.style.display = 'none'; + const gameTextArea = this.getChildByID('gameReportInput') as HTMLInputElement; + const gameIdUserReported = this.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.scene).connection.emitReportPlayerMessage( + parseInt(gameIdUserReported.value), + gameTextArea.value + ); + this.close(); + } +} \ No newline at end of file diff --git a/front/src/WebRtc/BlackListManager.ts b/front/src/WebRtc/BlackListManager.ts new file mode 100644 index 00000000..65efef3a --- /dev/null +++ b/front/src/WebRtc/BlackListManager.ts @@ -0,0 +1,24 @@ +import {Subject} from 'rxjs'; + +class BlackListManager { + private list: number[] = []; + public onBlockStream: Subject