From 633884534cc79b5a8711b002283a85e3c70892c6 Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Sat, 21 Dec 2024 13:43:28 -0500 Subject: [PATCH] Started work on emotes panel --- src/controllers/panel/emoteController.js | 20 ++++ src/routers/panelRouter.js | 2 + src/views/channel.ejs | 1 + src/views/partial/panels/emote.ejs | 34 +++++++ www/css/panel/emote.css | 37 ++++++++ www/css/theme/movie-night.css | 10 ++ www/js/channel/chat.js | 6 +- www/js/channel/cpanel.js | 23 ++++- www/js/channel/panels/emotePanel.js | 116 +++++++++++++++++++++++ 9 files changed, 242 insertions(+), 7 deletions(-) create mode 100644 src/controllers/panel/emoteController.js create mode 100644 src/views/partial/panels/emote.ejs create mode 100644 www/css/panel/emote.css create mode 100644 www/js/channel/panels/emotePanel.js diff --git a/src/controllers/panel/emoteController.js b/src/controllers/panel/emoteController.js new file mode 100644 index 0000000..1a0ab88 --- /dev/null +++ b/src/controllers/panel/emoteController.js @@ -0,0 +1,20 @@ +/*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 .*/ + +//root index functions +module.exports.get = async function(req, res){ + res.render('partial/panels/emote', {}); +} \ No newline at end of file diff --git a/src/routers/panelRouter.js b/src/routers/panelRouter.js index 4b5d9d3..273cbab 100644 --- a/src/routers/panelRouter.js +++ b/src/routers/panelRouter.js @@ -20,6 +20,7 @@ const { Router } = require('express'); //local imports const placeholderController = require("../controllers/panel/placeholderController"); +const emoteController = require("../controllers/panel/emoteController"); const popoutContainerController = require("../controllers/panel/popoutContainerController"); //globals @@ -27,6 +28,7 @@ const router = Router(); //routing functions router.get('/placeholder', placeholderController.get); +router.get('/emote', emoteController.get); router.get('/popoutContainer', popoutContainerController.get); module.exports = router; diff --git a/src/views/channel.ejs b/src/views/channel.ejs index bdb9d1b..b310b43 100644 --- a/src/views/channel.ejs +++ b/src/views/channel.ejs @@ -127,6 +127,7 @@ along with this program. If not, see .--> + \ No newline at end of file diff --git a/src/views/partial/panels/emote.ejs b/src/views/partial/panels/emote.ejs new file mode 100644 index 0000000..cc5d3f2 --- /dev/null +++ b/src/views/partial/panels/emote.ejs @@ -0,0 +1,34 @@ + + + + +

Site Emotes

+ +
+
+ + +

Channel Emotes

+ +
+
+ + +

Personal Emotes

+ +
+
\ No newline at end of file diff --git a/www/css/panel/emote.css b/www/css/panel/emote.css new file mode 100644 index 0000000..519550e --- /dev/null +++ b/www/css/panel/emote.css @@ -0,0 +1,37 @@ +.title-span{ + display: flex; + flex-direction: row; + text-wrap: nowrap; +} + +.title-filler-span{ + flex: 1; + height: 1px; + margin: auto 0.5em; +} + +.emote-panel-list{ + display: grid; + grid-template-columns: repeat(auto-fit, minmax(9em, 1fr)); + padding: 1em; + justify-items: center; +} + +div.emote-panel-list-emote{ + width: 9em; + display: flex; + flex-direction: column; + padding: 0.5em 0; + margin: 0.5em; +} + +p.emote-list-title{ + margin: 0; + text-align: center; +} + +.emote-list-media{ + max-height: 8em; + max-width: 8em; + margin: auto; +} \ No newline at end of file diff --git a/www/css/theme/movie-night.css b/www/css/theme/movie-night.css index 7de2eca..e0580d8 100644 --- a/www/css/theme/movie-night.css +++ b/www/css/theme/movie-night.css @@ -340,4 +340,14 @@ select.panel-head-element{ #delete-account-popup-title, #delete-channel-popup-title{ color: var(--danger0); +} + +/* panel */ +.title-filler-span{ + background-color: var(--accent0); +} + + +div.emote-panel-list-emote{ + border: 1px solid var(--accent0); } \ No newline at end of file diff --git a/www/js/channel/chat.js b/www/js/channel/chat.js index df8e38d..5a5bb23 100644 --- a/www/js/channel/chat.js +++ b/www/js/channel/chat.js @@ -54,9 +54,9 @@ class chatBox{ //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)}); + this.settingsIcon.addEventListener("click", ()=>{this.client.cPanel.setActivePanel(new panelObj(client))}); + this.adminIcon.addEventListener("click", ()=>{this.client.cPanel.setActivePanel(new panelObj(client))}); + this.emoteIcon.addEventListener("click", ()=>{this.client.cPanel.setActivePanel(new emotePanel(client))}); //Header icons this.aspectLockIcon.addEventListener("click", this.lockAspect.bind(this)); diff --git a/www/js/channel/cpanel.js b/www/js/channel/cpanel.js index 1ad698b..1b90943 100644 --- a/www/js/channel/cpanel.js +++ b/www/js/channel/cpanel.js @@ -69,11 +69,16 @@ class cPanel{ this.activePanelTitle.textContent = this.activePanel.name; //Call panel initialization function - this.activePanel.panelInit(); + this.activePanel.panelDocument = this.activePanelDoc; + this.activePanel.docSwitch(); } hideActivePanel(){ + //Hide the panel this.activePanelDiv.style.display = "none"; + //Clear out the panel + this.activePanelDoc.innerHTML = ''; + //Set active panel to null this.activePanel = null; } @@ -101,7 +106,8 @@ class cPanel{ this.pinnedPanelDiv.style.display = "flex"; //Call panel initialization function - this.pinnedPanel.panelInit(); + this.pinnedPanel.panelDocument = this.pinnedPanelDoc; + this.pinnedPanel.docSwitch(); //Resize to window/content this.pinnedPanelDragger.fixCutoff(); @@ -131,10 +137,11 @@ class cPanel{ } class panelObj{ - constructor(name = "Placeholder Panel", pageURL = "/panel/placeholder", panelDocument = window.document){ + constructor(client, name = "Placeholder Panel", pageURL = "/panel/placeholder", panelDocument = window.document){ this.name = name; this.pageURL = pageURL; this.panelDocument = panelDocument; + this.client = client; } async getPage(){ @@ -145,7 +152,7 @@ class panelObj{ return await response.text(); } - panelInit(){ + docSwitch(){ } } @@ -195,10 +202,15 @@ class poppedPanel{ //Set Window Title this.window.document.title = this.window.document.title.replace("NULL_POPOUT", `${this.panel.name} (${client.channelName})`); + //Set Panel Content this.panelTitle.innerText = this.panel.name; this.panelDoc.innerHTML = this.panelBody; + //Set panel object document and call the related function + this.panel.panelDocument = this.window.document; + this.panel.docSwitch(); + this.setupInput(); } @@ -215,11 +227,14 @@ class poppedPanel{ unpop(){ //Set active panel this.cPanel.setActivePanel(this.panel, this.panelDoc.innerHTML); + + //Close the popped window this.window.close(); } pin(){ this.cPanel.setPinnedPanel(this.panel, this.panelDoc.innerHTML); + this.window.close(); } diff --git a/www/js/channel/panels/emotePanel.js b/www/js/channel/panels/emotePanel.js new file mode 100644 index 0000000..ea8a55d --- /dev/null +++ b/www/js/channel/panels/emotePanel.js @@ -0,0 +1,116 @@ +class emotePanel extends panelObj{ + constructor(client, panelDocument){ + super(client, "Emote Panel", "/panel/emote", panelDocument); + } + + docSwitch(){ + 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.personalEmoteList = this.panelDocument.querySelector('#emote-panel-personal-list'); + + this.setupInput(); + + this.renderEmotes(this.client.chatBox.commandPreprocessor.emotes.site, this.siteEmoteList); + this.renderEmotes(this.client.chatBox.commandPreprocessor.emotes.chan, this.chanEmoteList); + this.renderEmotes(this.client.chatBox.commandPreprocessor.emotes.personal, this.personalEmoteList); + } + + 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)); + } + + toggleSiteEmotes(event){ + this.toggleEmotes(this.siteEmoteToggle, this.siteEmoteList); + } + + toggleChanEmotes(event){ + this.toggleEmotes(this.chanEmoteToggle, this.chanEmoteList); + } + + togglePersonalEmotes(event){ + this.toggleEmotes(this.personalEmoteToggle, this.personalEmoteList); + } + + 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(client.cPanel.activePanel == this){ + //Close it + this.client.cPanel.hideActivePanel(); + } + + //Add the emote to the chatbox prompt + this.client.chatBox.chatPrompt.value += `[${emote}]`; + } + + renderEmotes(emoteList, container){ + //Clear out the container + container.innerHTML = ''; + + //For each emote + emoteList.forEach((emote) => { + //Create div to hold emote + const emoteDiv = document.createElement('div'); + emoteDiv.classList.add('emote-panel-list-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'); + + //Create paragraph tag + const emoteTitle = document.createElement('p'); + //Set title class + emoteTitle.classList.add('emote-list-title'); + //Set emote title + emoteTitle.innerHTML = `[${emote.name}]`; + + //Add the emote media to the emote span + emoteDiv.appendChild(emoteMedia); + //Add title paragraph node + emoteDiv.appendChild(emoteTitle); + + //Add useEmote event listener + emoteDiv.addEventListener('click', ()=>{this.useEmote(emote.name)}); + + //Append the mote span to the emote list + container.appendChild(emoteDiv); + }) + } +} \ No newline at end of file