/*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 emotePanel extends panelObj{ constructor(client, panelDocument){ super(client, "Emote Palette", "/panel/emote", panelDocument); this.client.socket.on("personalEmotes", this.renderEmoteLists.bind(this)); } closer(){ this.client.socket.off("personalEmotes", this.renderEmoteLists.bind(this)); } docSwitch(){ this.siteEmoteTitle = this.panelDocument.querySelector('#site-emotes-title'); this.chanEmoteTitle = this.panelDocument.querySelector('#chan-emotes-title'); this.personalEmoteTitle = this.panelDocument.querySelector('#personal-emotes-title'); this.siteEmoteToggle = this.panelDocument.querySelector('#site-emotes-toggle'); this.chanEmoteToggle = this.panelDocument.querySelector('#chan-emotes-toggle'); this.personalEmoteToggle = this.panelDocument.querySelector('#personal-emotes-toggle'); this.siteEmoteList = this.panelDocument.querySelector('#emote-panel-site-list'); this.chanEmoteList = this.panelDocument.querySelector('#emote-panel-chan-list'); this.personalEmoteSection = this.panelDocument.querySelector('#emote-panel-personal-section'); this.personalEmoteList = this.panelDocument.querySelector('#emote-panel-personal-list'); this.searchPrompt = this.panelDocument.querySelector('#emote-panel-search-prompt'); this.personalEmoteLinkPrompt = this.panelDocument.querySelector('#new-emote-link-input'); this.personalEmoteNamePrompt = this.panelDocument.querySelector('#new-emote-name-input'); this.personalEmoteAddButton = this.panelDocument.querySelector('#new-emote-button'); this.setupInput(); this.renderEmoteLists(); } setupInput(){ //Make sure to remove any event listeners in-case we moving an already instantiated panel this.siteEmoteToggle.removeEventListener("click", this.toggleSiteEmotes.bind(this)); this.siteEmoteToggle.addEventListener("click", this.toggleSiteEmotes.bind(this)); this.chanEmoteToggle.removeEventListener("click", this.toggleChanEmotes.bind(this)); this.chanEmoteToggle.addEventListener("click", this.toggleChanEmotes.bind(this)); this.personalEmoteToggle.removeEventListener("click", this.togglePersonalEmotes.bind(this)); this.personalEmoteToggle.addEventListener("click", this.togglePersonalEmotes.bind(this)); this.siteEmoteTitle.removeEventListener("click", this.toggleSiteEmotes.bind(this)); this.siteEmoteTitle.addEventListener("click", this.toggleSiteEmotes.bind(this)); this.chanEmoteTitle.removeEventListener("click", this.toggleChanEmotes.bind(this)); this.chanEmoteTitle.addEventListener("click", this.toggleChanEmotes.bind(this)); this.personalEmoteTitle.removeEventListener("click", this.togglePersonalEmotes.bind(this)); this.personalEmoteTitle.addEventListener("click", this.togglePersonalEmotes.bind(this)); this.searchPrompt.removeEventListener('keyup', this.renderEmoteLists.bind(this)); this.searchPrompt.addEventListener('keyup', this.renderEmoteLists.bind(this)); this.personalEmoteAddButton.removeEventListener("click", this.addPersonalEmote.bind(this)); this.personalEmoteAddButton.addEventListener("click", this.addPersonalEmote.bind(this)); } toggleSiteEmotes(event){ this.toggleEmotes(this.siteEmoteToggle, this.siteEmoteList); } toggleChanEmotes(event){ this.toggleEmotes(this.chanEmoteToggle, this.chanEmoteList); } togglePersonalEmotes(event){ this.toggleEmotes(this.personalEmoteToggle, this.personalEmoteSection); } toggleEmotes(icon, list){ if(list.checkVisibility()){ icon.classList.replace('bi-caret-down-fill','bi-caret-left-fill'); list.style.display = 'none'; }else{ icon.classList.replace('bi-caret-left-fill', 'bi-caret-down-fill'); list.style.display = 'grid'; } } useEmote(emote){ //If we're using this from the active panel if(this.client.cPanel.activePanel == this){ //Close it this.client.cPanel.hideActivePanel(); } //Add the emote to the chatbox prompt this.client.chatBox.catChat(`[${emote}]`); } addPersonalEmote(event){ //Collect input const name = this.personalEmoteNamePrompt.value; const link = this.personalEmoteLinkPrompt.value; //Empty out prompts this.personalEmoteNamePrompt.value = ''; this.personalEmoteLinkPrompt.value = ''; //Send emote to server this.client.socket.emit("addPersonalEmote", {name, link}); } deletePersonalEmote(name){ //send out delete this.client.socket.emit('deletePersonalEmote', {name}); } renderEmoteLists(){ //if we've initialized the search prompt (wont happen yet first run) if(this.searchPrompt != null){ //Get the search value var search = this.searchPrompt.value; } //pull emote lists from the command preprocessor var siteEmotes = this.client.chatBox.commandPreprocessor.emotes.site; var chanEmotes = this.client.chatBox.commandPreprocessor.emotes.chan; var personalEmotes = this.client.chatBox.commandPreprocessor.emotes.personal; //If we have a search bar and a search in the search bar if(search != null && search != ''){ //filter emote lists using the filterQuery function siteEmotes = siteEmotes.filter(filterQuery); chanEmotes = chanEmotes.filter(filterQuery); personalEmotes = personalEmotes.filter(filterQuery); function filterQuery(emote){ //return true for anyany case-insensitive matches return (emote.name.toLowerCase().match(search.toLowerCase())) != null; } } //render out the emote lists this.renderEmotes(siteEmotes, this.siteEmoteList); this.renderEmotes(chanEmotes, this.chanEmoteList); this.renderEmotes(personalEmotes, this.personalEmoteList, true); } renderEmotes(emoteList, container, personal = false){ //Clear out the container container.innerHTML = ''; //If we have two or less emotes if(emoteList.length <= 2){ //Set the container display to flex container.style.display = 'flex'; //otherwise }else{ //Set the container display to grid container.style.display = 'grid'; } //For each emote emoteList.forEach((emote) => { //Create div to hold emote span const emoteDiv = document.createElement('div'); emoteDiv.classList.add('emote-panel-list-emote'); const emoteSpan = document.createElement('span'); emoteSpan.classList.add('emote-panel-list-emote'); //If we have a low emote count if(emoteList.length <= 2){ //render them huuuuuge emoteDiv.classList.add('emote-panel-list-big-emote'); emoteSpan.classList.add('emote-panel-list-big-emote'); } //If the emote is an image if(emote.type == 'image'){ //Create image node var emoteMedia = document.createElement('img'); //if emote is a video }else if(emote.type == 'video'){ //create video node var emoteMedia = document.createElement('video'); //Set video properties emoteMedia.autoplay = true; emoteMedia.muted = true; emoteMedia.controls = false; emoteMedia.loop = true; } //set media link as source emoteMedia.src = emote.link; //Set media class emoteMedia.classList.add('emote-list-media'); //if we have a low emote count if(emoteList.length <= 2){ //render them huuuuuge emoteMedia.classList.add('emote-list-big-media'); } //Create paragraph tag const emoteTitle = document.createElement('p'); //Set title class emoteTitle.classList.add('emote-list-title'); //Set emote title emoteTitle.textContent = utils.unescapeEntities(`[${emote.name}]`); //if we're rendering personal emotes if(personal){ //create span to hold trash icon const trashSpan = document.createElement('span'); trashSpan.classList.add('emote-list-trash-icon'); //Create trash icon const trashIcon = document.createElement('i'); trashIcon.classList.add('emote-list-trash-icon', 'bi-trash-fill'); trashIcon.id = `emote-list-trash-icon-${emote.name}`; //add deletePersonalEmote event listener trashIcon.addEventListener('click', ()=>{this.deletePersonalEmote(emote.name)}); //Add trash icon to trash span trashSpan.appendChild(trashIcon); //append trash span to emote div emoteDiv.appendChild(trashSpan); } //Add the emote media to the emote span emoteSpan.appendChild(emoteMedia); //Add title paragraph node emoteSpan.appendChild(emoteTitle); //Add useEmote event listener emoteSpan.addEventListener('click', ()=>{this.useEmote(emote.name)}); //Add emote span to the emote div emoteDiv.appendChild(emoteSpan); //Append the mote span to the emote list container.appendChild(emoteDiv); }) } }