/*Canopy - The next generation of stoner streaming software Copyright (C) 2024-2025 Rainbownapkin and the TTN Community This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see .*/ /** * Class representing the settings panel * @extends panelObj */ class pmPanel extends panelObj{ /** * Instantiates a new Panel Object * @param {channel} client - Parent client Management Object * @param {Document} panelDocument - Panel Document */ constructor(client, panelDocument){ super(client, "Private Messaging", "/panel/pm", panelDocument); /** * String to hold name of currently active sesh */ this.activeSesh = ""; this.defineListeners(); } closer(){ } docSwitch(){ this.startSeshButton = this.panelDocument.querySelector('#pm-panel-start-sesh'); this.seshList = this.panelDocument.querySelector('#pm-panel-sesh-list'); this.seshBuffer = this.panelDocument.querySelector('#pm-panel-sesh-buffer'); this.seshPrompt = this.panelDocument.querySelector('#pm-panel-message-prompt'); this.seshSendButton = this.panelDocument.querySelector('#pm-panel-send-button'); this.setupInput(); this.renderSeshList(); //If we have an active sesh if(this.activeSesh != null && this.activeSesh != ""){ //Render messages this.renderMessages(); } //Call derived method super.docSwitch(); } /** * Defines network related event listeners */ defineListeners(){ this.client.pmSocket.on("message", this.handlePM.bind(this)); this.client.pmSocket.on("sent", this.handlePM.bind(this)); } /** * Defines input-related event handlers */ setupInput(){ this.startSeshButton.addEventListener('click', this.startSesh.bind(this)); this.seshPrompt.addEventListener("keydown", this.send.bind(this)); this.seshSendButton.addEventListener("click", this.send.bind(this)); } startSesh(event){ new startSeshPopup(event, this.client, this.renderSeshList.bind(this), this.ownerDoc); } handlePM(data){ const nameObj = pmHandler.genSeshName(data); //If this message is for the active sesh if(nameObj.name == this.activeSesh){ //Render out the newest message this.renderMessage(data); }else{ //pull current session entry if it exists const curEntry = this.panelDocument.querySelector(`[data-id="${nameObj.name}"]`); //If it doesn't exist if(curEntry == null){ //Re-render out the sesh list this.renderSeshList(); } } } /** * sends private message from sesh prompt to server * @param {Event} event - Event passed down from Event Handler */ send(event){ if((!event || !event.key || event.key == "Enter") && this.seshPrompt.value && this.activeSesh != ''){ //Pull current sesh from sesh list const sesh = this.client.pmHandler.seshList.get(this.activeSesh); //Send message out to server this.client.pmSocket.emit("pm", { recipients: sesh.recipients, msg: this.seshPrompt.value }); //Clear our prompt this.seshPrompt.value = ""; } } /** * Render out current sesh array to sesh list UI */ renderSeshList(){ //Clear out the sesh list this.seshList.innerHTML = ""; //Assemble temporary array from client PM Handler sesh list const seshList = Array.from(this.client.pmHandler.seshList); //If we have existing sessions and no active sesh if(this.activeSesh == "" && seshList[0] != null){ //Enable UI elements this.seshPrompt.disabled = false; this.seshSendButton.disabled = false; //Render out messages this.renderMessages(); //Set the first one as active this.activeSesh = seshList[0][1].id; } //For each session tracked by the pmHandler for(const seshEntry of seshList){ this.renderSeshListEntry(seshEntry[1]); } } /** * Renders out a given messaging sesh to the sesh list UI */ renderSeshListEntry(sesh){ //Create container div const entryDiv = document.createElement('div'); //Set conatiner div classes entryDiv.classList.add('pm-panel-sesh-list-entry','interactive'); //Set dataset sesh name entryDiv.dataset.id = sesh.id; //If the current entry is the active sesh if(sesh.id == this.activeSesh){ //mark it as such entryDiv.classList.add('positive'); } //Create sesh label const seshLabel = document.createElement('p'); //Create human-readable label out of members array seshLabel.textContent = utils.unescapeEntities(sesh.id); //append sesh label to entry div entryDiv.appendChild(seshLabel); //Append entry div to sesh list this.seshList.appendChild(entryDiv); //Add input-related event listener for entry div entryDiv.addEventListener('click', this.selectSesh.bind(this)); } selectSesh(event){ //Attempt to pull previously active sesh item from list const wasActive = this.panelDocument.querySelector('.positive'); //If there was an active sesh if(wasActive != null){ //Remove active sesh class from old item wasActive.classList.remove('positive'); } //Pull new active sesh name from target dataset this.activeSesh = event.target.dataset.id; //Set new sesh as active sesh event.target.classList.add('positive'); //Re-render message buffer this.renderMessages(); } renderMessages(){ //Empty out the sesh buffer this.seshBuffer.innerHTML = ""; //Pull sesh from pmHandler const sesh = this.client.pmHandler.seshList.get(this.activeSesh); //If the sesh is real if(sesh != null){ //for each message in the sesh for(const message of sesh.messages){ //Render out messages to the buffer this.renderMessage(message); } } } renderMessage(message){ const msgSpan = document.createElement('span'); const msgSender = document.createElement('p'); msgSender.innerText = utils.unescapeEntities(`${message.sender}:`); msgSender.classList.add('pm-panel-sesh-message-sender'); const msgContent = document.createElement('p'); msgContent.innerText = utils.unescapeEntities(message.msg); msgContent.classList.add('pm-panel-sesh-message-content'); msgSpan.appendChild(msgSender); msgSpan.appendChild(msgContent); this.seshBuffer.appendChild(msgSpan); } } /** * Class representing pop-up dialogue to start a private messaging sesh */ class startSeshPopup{ /** * Instantiates a new schedule media Pop-up * @param {Event} event - Event passed down from Event Listener * @param {channel} client - Parent Client Management Object * @param {String} url - URL/link to media to queue * @param {String} title - Title of media to queue * @param {Function} cb - Callback function, passed upon pop-up creation * @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up */ constructor(event, client, cb, doc){ /** * Parent Client Management Object */ this.client = client; /** * Callback function, passed upon pop-up creation */ this.cb = cb; //Create media popup and call async constructor when done //unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :( /** * canopyUXUtils.popup() object */ this.popup = new canopyUXUtils.popup('/startChatSesh', true, this.asyncConstructor.bind(this), doc); } /** * Continuation of object construction, called after child popup object construction */ asyncConstructor(){ //Grab required UI elements this.startSeshButton = this.popup.contentDiv.querySelector('#pm-sesh-popup-button'); this.usernamePrompt = this.popup.contentDiv.querySelector('#pm-sesh-popup-prompt'); //Setup input this.setupInput(); } /** * Defines input-related Event Handlers */ setupInput(){ //Setup input this.startSeshButton.addEventListener('click', this.startSesh.bind(this)); this.popup.popupDiv.addEventListener('keydown', this.startSesh.bind(this)); } /** * Handles sending request to schedule item to the queue * @param {Event} event - Event passed down from Event Listener */ startSesh(event){ //If we clicked or hit enter if(event.key == null || event.key == "Enter"){ /* //Cook a new sesh from const newSesh = new pmSesh({ //Split usernames by space sender: this.client.user.user, recipients: this.usernamePrompt.value.split(" ") }); //Pop new sesh into pmHandler this.client.pmHandler.seshList.set(newSesh.id, newSesh); */ //Send message out to server this.client.pmSocket.emit("pm", { recipients: this.usernamePrompt.value.split(" "), msg: "" }); //If we have a function if(typeof this.cb == "function"){ //Call any callbacks we where given this.cb(); } //Close the popup this.popup.closePopup(); } } }