/*Canopy - The next generation of stoner streaming software Copyright (C) 2024 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 chatBox{ constructor(client){ //Client Object this.client = client //Booleans this.aspectLock = true; //clickDragger object this.clickDragger = new canopyUXUtils.clickDragger("#chat-panel-drag-handle", "#chat-panel-div"); this.chatPreprocessor = new chatPreprocessor(); //Element Nodes this.chatPanel = document.querySelector("#chat-panel-div"); this.highSelect = document.querySelector("#chat-panel-high-level-select"); this.flairSelect = document.querySelector("#chat-panel-flair-select"); this.chatBuffer = document.querySelector("#chat-panel-buffer-div"); this.chatPrompt = document.querySelector("#chat-panel-prompt"); this.settingsIcon = document.querySelector("#chat-panel-settings-icon"); this.adminIcon = document.querySelector("#chat-panel-admin-icon"); this.emoteIcon = document.querySelector("#chat-panel-emote-icon"); this.sendButton = document.querySelector("#chat-panel-send-button"); //Seems weird to stick this in here, but the split is dictated by chat width :P this.aspectLockIcon = document.querySelector("#media-panel-aspect-lock-icon"); this.hideChatIcon = document.querySelector("#chat-panel-div-hide"); this.showChatIcon = document.querySelector("#media-panel-show-chat-icon"); //Setup functions this.setupInput(); this.defineListeners(); this.sizeToAspect(); } setupInput(){ //Chat bar this.chatPrompt.addEventListener("keydown", this.send.bind(this)); this.sendButton.addEventListener("click", this.send.bind(this)); this.settingsIcon.addEventListener("click", ()=>{this.client.cPanel.setActivePanel(new panelObj)}); this.adminIcon.addEventListener("click", ()=>{this.client.cPanel.setActivePanel(new panelObj)}); this.emoteIcon.addEventListener("click", ()=>{this.client.cPanel.setActivePanel(new panelObj)}); //Header icons this.aspectLockIcon.addEventListener("click", this.lockAspect.bind(this)); this.showChatIcon.addEventListener("click", ()=>{this.toggleUI()}); this.hideChatIcon.addEventListener("click", ()=>{this.toggleUI()}); this.highSelect.addEventListener("change", this.setHighLevel.bind(this)); this.flairSelect.addEventListener("change", this.setFlair.bind(this)); //Clickdragger/Resize this.clickDragger.handle.addEventListener("mousedown", this.unlockAspect.bind(this)); window.addEventListener("resize", this.resizeAspect.bind(this)); } defineListeners(){ this.client.socket.on("chatMessage", (data) => { this.displayChat(data); }); } displayChat(chat){ //Create chat-entry span var chatEntry = document.createElement('span'); chatEntry.classList.add("chat-panel-buffer","chat-entry",`chat-entry-${chat.user}`); //Create high-level label var highLevel = document.createElement('p'); highLevel.classList.add("chat-panel-buffer","chat-entry-high-level","high-level"); highLevel.innerHTML = `${chat.highLevel}`; chatEntry.appendChild(highLevel); //Create username label var userLabel = document.createElement('p'); userLabel.classList.add("chat-panel-buffer","chat-entry-username"); if(chat.flair != "classic"){ var flair = `flair-${chat.flair}`; }else{ var flair = this.client.userList.colorMap.get(chat.user); } //Create color span var colorSpan = document.createElement('span'); colorSpan.classList.add("chat-entry-flair-span", flair); colorSpan.innerHTML = `${chat.user}`; userLabel.innerHTML = `${colorSpan.outerHTML}: `; chatEntry.appendChild(userLabel); //Create chat body var chatBody = document.createElement('p'); chatBody.classList.add("chat-panel-buffer","chat-entry-body"); chatBody.innerHTML = chat.msg; chatEntry.appendChild(chatBody); this.chatBuffer.appendChild(this.chatPreprocessor.preprocess(chatEntry)); } async send(event){ if((!event || !event.key || event.key == "Enter") && this.chatPrompt.value){ this.client.socket.emit("chatMessage",{msg: this.chatPrompt.value}); this.chatPrompt.value = ""; } } handleClientInfo(data){ this.updateFlairSelect(data.flairList, data.user.flair); this.updateHighSelect(data.user.highLevel); } setHighLevel(event){ const highLevel = event.target.value; this.client.socket.emit("setHighLevel", {highLevel}); } setFlair(event){ const flair = event.target.value; this.client.socket.emit("setFlair", {flair}); } updateHighSelect(highLevel){ this.highSelect.value = highLevel; } updateFlairSelect(flairList, flair){ //clear current flair select this.flairSelect.innerHTML = ""; //For each flair in flairlist flairList.forEach((flair) => { //Create an option var flairOption = document.createElement('option'); //Set the name and innerHTML flairOption.value = flair.name; flairOption.innerHTML = flair.displayName; //Append it to the select this.flairSelect.appendChild(flairOption); }); //Set the selected flair in the UI this.flairSelect.value = flair; //Re-style the UI, do this in two seperate steps in-case we're running for the first time and have nothing to replace. this.flairSelect.className = this.flairSelect.className.replace(/flair-\S*/, ""); this.flairSelect.classList.add(`flair-${flair}`); } lockAspect(event){ //prevent the user from breaking shit :P if(this.chatPanel.style.display != "none"){ this.aspectLock = true; this.aspectLockIcon.style.display = "none"; this.sizeToAspect(); } } unlockAspect(event){ this.aspectLock = false; this.aspectLockIcon.style.display = "inline"; } resizeAspect(event){ if(this.aspectLock){ this.sizeToAspect(); }else{ this.client.userList.clickDragger.fixCutoff(); } } sizeToAspect(){ if(this.chatPanel.style.display != "none"){ var targetVidWidth = this.client.player.getRatio() * this.chatPanel.getBoundingClientRect().height; const targetChatWidth = window.innerWidth - targetVidWidth; //This should be changeable in settings later on, for now it defaults to 20% const limit = window.innerWidth * .2; //Set width to target or 20vh depending on whether or not we've hit the width limit this.chatPanel.style.width = targetChatWidth > limit ? `${targetChatWidth}px` : '20vh'; //Fix busted layout var pageBreak = document.body.scrollWidth - document.body.getBoundingClientRect().width; this.chatPanel.style.width = `${this.chatPanel.getBoundingClientRect().width + pageBreak}px`; //This sometimes gets called before userList ahs been initiated :p if(this.client.userList != null){ this.client.userList.clickDragger.fixCutoff(); } } } toggleUI(show = !this.chatPanel.checkVisibility()){ if(show){ this.chatPanel.style.display = "flex"; this.showChatIcon.style.display = "none"; this.client.player.hideVideoIcon.style.display = "flex"; this.client.userList.clickDragger.fixCutoff(); }else{ this.chatPanel.style.display = "none"; this.showChatIcon.style.display = "flex"; this.client.player.hideVideoIcon.style.display = "none"; } } }