From d8e5c64c139ac524f236a2632639a7b07705075a Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Tue, 30 Sep 2025 03:25:15 -0400 Subject: [PATCH 1/4] Starting work on client-side chat sesh handling --- www/js/channel/pmHandler.js | 81 ++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/www/js/channel/pmHandler.js b/www/js/channel/pmHandler.js index 956bd57..c26f47c 100644 --- a/www/js/channel/pmHandler.js +++ b/www/js/channel/pmHandler.js @@ -15,23 +15,100 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see .*/ /** - * Class for handling incoming Private Messages + * Class for handling incoming Private Messages for the entire client */ class pmHandler{ + /** + * Instantiates a new Private Message Handler object + * @param {channel} client - Parent client Management Object + */ constructor(client){ + /** + * Parent client management object + */ this.client = client; + /** + * PM Icon in the main chat bar + */ this.pmIcon = document.querySelector('#chat-panel-pm-icon'); + /** + * List of PM Sessions + */ + this.seshList = []; + this.defineListeners(); this.setupInput(); } + /** + * Defines network related event listeners for PM Handler + */ defineListeners(){ - + this.client.pmSocket.on("message", this.handlePM.bind(this)); + this.client.pmSocket.on("sent", this.handlePM.bind(this)); } + /** + * Defines inpet related event listeners for PM handler + */ setupInput(){ this.pmIcon.addEventListener("click", ()=>{this.client.cPanel.setActivePanel(new pmPanel(client))}); } + + /** + * Handles received Private Messages from the PM service on the server, organizing it into the proper session from the sesh list + * Or creating a new sesh where a matching one does not a exist + * @param {object} data - Private Message data from the server + */ + handlePM(data){ + //Store whether or not current message has been consumed by an existing sesh + let consumed = false; + + //For each existing sesh + for(let seshIndex in this.seshList){ + //Get current sesh + const sesh = this.seshList[seshIndex]; + + //Check to see if the length of sesh recipients equals current length (only check on arrays that actually make sense to save time) + if(sesh.recipients.length == data.recipients.length){ + /*Feels like cheating to have the JS engine to the hard bits by just telling it to sort them. + That being said, since the function is implemented into the JS Engine itself + It will be quicker than any custom comparison code we can write*/ + + //Sort recipient lists so lists with the same user will be equal when joined together in a string and compare, if they're the same... + if(sesh.recipients.sort().join() == data.recipients.sort().join()){ + //Dump collected message into the matching session + this.seshList[seshIndex].messages.push(data); + + //Let the rest of the method know that we've consumed this message + consumed = true; + } + } + } + + //If we made it through the loop without consuming the message + if(!consumed){ + //Add it to it's own fresh new sesh + this.seshList.push(new pmSesh(data)); + } + } +} + +/** + * Class which represents an existing Private Messaging session between two or more users + */ +class pmSesh{ + /** + * Instatiates a new pmSession object + * @param {Object} message - Initial Private Message object from server that initiated the session + */ + constructor(message){ + //Add recipients from message + this.recipients = message.recipients; + + //Add message to messages array + this.messages = [message]; + } } \ No newline at end of file From e2020406a7884815b8b83bd1758455faadf88081 Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Tue, 30 Sep 2025 04:39:17 -0400 Subject: [PATCH 2/4] Improved Private Message Session Management --- www/js/channel/pmHandler.js | 56 ++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/www/js/channel/pmHandler.js b/www/js/channel/pmHandler.js index c26f47c..09e1a3a 100644 --- a/www/js/channel/pmHandler.js +++ b/www/js/channel/pmHandler.js @@ -66,19 +66,37 @@ class pmHandler{ //Store whether or not current message has been consumed by an existing sesh let consumed = false; + //Create members array from scratch to avoid changing the input data for further processing + const members = []; + + //Manually iterate through recipients + for(const member of data.recipients){ + //check to make sure we're not adding ourselves + if(member != this.client.user.user){ + //Copy relevant array members by value instead of reference + members.push(member); + } + } + + //If this wasn't our message + if(data.sender != this.client.user.user){ + //Push sender onto members list + members.push(data.sender); + } + //For each existing sesh for(let seshIndex in this.seshList){ //Get current sesh const sesh = this.seshList[seshIndex]; //Check to see if the length of sesh recipients equals current length (only check on arrays that actually make sense to save time) - if(sesh.recipients.length == data.recipients.length){ + if(sesh.recipients.length == members.length){ /*Feels like cheating to have the JS engine to the hard bits by just telling it to sort them. That being said, since the function is implemented into the JS Engine itself It will be quicker than any custom comparison code we can write*/ //Sort recipient lists so lists with the same user will be equal when joined together in a string and compare, if they're the same... - if(sesh.recipients.sort().join() == data.recipients.sort().join()){ + if(sesh.recipients.sort().join() == members.sort().join()){ //Dump collected message into the matching session this.seshList[seshIndex].messages.push(data); @@ -91,7 +109,7 @@ class pmHandler{ //If we made it through the loop without consuming the message if(!consumed){ //Add it to it's own fresh new sesh - this.seshList.push(new pmSesh(data)); + this.seshList.push(new pmSesh(data, client)); } } } @@ -104,11 +122,35 @@ class pmSesh{ * Instatiates a new pmSession object * @param {Object} message - Initial Private Message object from server that initiated the session */ - constructor(message){ - //Add recipients from message - this.recipients = message.recipients; + constructor(message, client){ + /** + * Parent client management object + */ + this.client = client; - //Add message to messages array + /** + * Members of session excluding the currently logged in user + */ + this.recipients = []; + + //Manually iterate through recipients + for(const member of message.recipients){ + //check to make sure we're not adding ourselves + if(member != this.client.user.user){ + //Copy relevant array members by value instead of reference + this.recipients.push(member); + } + } + + //If this wasn't our message + if(message.sender != this.client.user.user){ + //Push sender onto members list + this.recipients.push(message.sender); + } + + /** + * Array containing all session messages + */ this.messages = [message]; } } \ No newline at end of file From a681bddbf78002a4f4f1bfafe5d88d5400c5a39f Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Tue, 30 Sep 2025 04:39:39 -0400 Subject: [PATCH 3/4] Cleaned up emotePanel.js --- www/js/channel/panels/emotePanel.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/www/js/channel/panels/emotePanel.js b/www/js/channel/panels/emotePanel.js index 98a412b..877b4f7 100644 --- a/www/js/channel/panels/emotePanel.js +++ b/www/js/channel/panels/emotePanel.js @@ -27,6 +27,13 @@ class emotePanel extends panelObj{ constructor(client, panelDocument){ super(client, "Emote Palette", "/panel/emote", panelDocument); + this.defineListeners(); + } + + /** + * Defines network related listeners + */ + defineListeners(){ this.client.socket.on("personalEmotes", this.renderEmoteLists.bind(this)); } From f109314163d5f7907b7c8e460c15f500e03c9d7f Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Tue, 30 Sep 2025 05:03:36 -0400 Subject: [PATCH 4/4] Added basic sesh list rendering to PM panel UX --- www/css/panel/pm.css | 18 +++++++++++-- www/css/theme/movie-night.css | 5 ++++ www/js/channel/panels/pmPanel.js | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/www/css/panel/pm.css b/www/css/panel/pm.css index 09b3c56..df2db42 100644 --- a/www/css/panel/pm.css +++ b/www/css/panel/pm.css @@ -22,6 +22,8 @@ along with this program. If not, see .*/ #pm-panel-sesh-list-container{ flex: 1; max-width: 10em; + width: calc(100% - 1.25em); + margin-left: 0.25em; } #pm-panel-sesh-container{ @@ -33,11 +35,11 @@ along with this program. If not, see .*/ } #pm-panel-start-sesh{ - width: calc(100% - 1.25em); - margin-left: 0.25em; padding: 0 0.5em; text-wrap: nowrap; display: flex; + font-size: 1.2em; + font-weight: bold; } #pm-panel-start-sesh span{ @@ -51,4 +53,16 @@ along with this program. If not, see .*/ #pm-panel-sesh-control-div{ margin: 0.5em; +} + +div.pm-panel-sesh-list-entry{ + padding: 0 0.5em; + display: flex; + flex-direction: row; +} + +div.pm-panel-sesh-list-entry, div.pm-panel-sesh-list-entry p{ + margin: 0; + text-wrap: nowrap; + text-align: center; } \ No newline at end of file diff --git a/www/css/theme/movie-night.css b/www/css/theme/movie-night.css index 1cfda84..96e5793 100644 --- a/www/css/theme/movie-night.css +++ b/www/css/theme/movie-night.css @@ -627,6 +627,11 @@ div.archived p{ border-bottom: 1px solid var(--accent0); } + +div.pm-panel-sesh-list-entry{ + border-bottom: 1px solid var(--accent0); +} + /* altcha theming*/ div.altcha{ box-shadow: 4px 4px 1px var(--bg1-alt0) inset; diff --git a/www/js/channel/panels/pmPanel.js b/www/js/channel/panels/pmPanel.js index 761c2d8..e502f4f 100644 --- a/www/js/channel/panels/pmPanel.js +++ b/www/js/channel/panels/pmPanel.js @@ -26,13 +26,26 @@ class pmPanel extends panelObj{ */ constructor(client, panelDocument){ super(client, "Private Messaging", "/panel/pm", panelDocument); + + this.defineListeners(); } closer(){ } docSwitch(){ + this.seshList = this.panelDocument.querySelector('#pm-panel-sesh-list'); + this.setupInput(); + + this.renderSeshList(); + } + + /** + * Defines network related event listeners + */ + defineListeners(){ + } /** @@ -40,4 +53,36 @@ class pmPanel extends panelObj{ */ setupInput(){ } + + /** + * Render out current sesh array to sesh list UI + */ + renderSeshList(){ + //For each session tracked by the pmHandler + for(const sesh of this.client.pmHandler.seshList){ + this.renderSeshListEntry(sesh); + } + } + + /** + * 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'); + + + //Create sesh label + const seshLabel = document.createElement('p'); + //Create human-readable label out of members array + seshLabel.textContent = utils.unescapeEntities(sesh.recipients.sort().join(', ')); + + //append sesh label to entry div + entryDiv.appendChild(seshLabel); + + //Append entry div to sesh list + this.seshList.appendChild(entryDiv); + } } \ No newline at end of file