import {HtmlUtils} from "./HtmlUtils"; import {mediaManager, ReportCallback} from "./MediaManager"; import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; import {connectionManager} from "../Connexion/ConnectionManager"; import {GameConnexionTypes} from "../Url/UrlManager"; export type SendMessageCallback = (message:string) => void; 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(); private userInputManager?: UserInputManager; constructor() { this.mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); this.createDiscussPart(''); //todo: why do we always use empty string? } 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.clearAllInputKeyboard(); } } inputMessage.onblur = () => { if(this.userInputManager) { this.userInputManager.initKeyBoardEvent(); } } 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|string, name: string|undefined, img?: string|undefined, isMe: boolean = false, reportCallback?: ReportCallback ) { 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 ) { const reportBanUserAction: HTMLButtonElement = document.createElement('button'); reportBanUserAction.classList.add('report-btn') reportBanUserAction.innerText = 'Report'; reportBanUserAction.addEventListener('click', () => { if(reportCallback) { mediaManager.showReportModal(`${userId}`, name ?? '', reportCallback); }else{ console.info('report feature is not activated!'); } }); 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})`; } private escapeHtml(html: string): string { const textReturn : HTMLSpanElement = document.createElement('span'); const text = document.createTextNode(html); textReturn.innerText = text; const p = document.createElement('p'); p.appendChild(text); return p.innerHTML; } private urlify(text: string) : string { const urlRegex = /(https?:\/\/[^\s]+)/g; text = this.escapeHtml(text); return text.replace(urlRegex, (url: string) => { return '' + url + ''; }); } 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 = 'Moi'; } pMessage.innerHTML = `${name} ${date.getHours()}:${date.getMinutes()} `; divMessage.appendChild(pMessage); const userMessage: HTMLParagraphElement = document.createElement('p'); userMessage.innerHTML = this.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(); } } export const discussionManager = new DiscussionManager();