From 3c185b4e2859ae08a0e61920b4eaebf1450ce28f Mon Sep 17 00:00:00 2001 From: rainbownapkin Date: Sat, 16 Nov 2024 12:34:24 -0500 Subject: [PATCH] Added pop-out Canopy Panels. Also added un-pin button to pinned Canopy Panel --- ...laceholder.js => placeholderController.js} | 0 .../panel/popoutContainerController.js | 23 +++ src/routers/panelRouter.js | 4 +- src/views/channel.ejs | 6 +- src/views/panels/popoutContainer.ejs | 35 +++++ www/css/channel.css | 28 +--- www/css/panel.css | 25 ++++ www/js/channel/channel.js | 1 - www/js/channel/chat.js | 8 +- www/js/channel/cpanel.js | 131 +++++++++++++++--- 10 files changed, 213 insertions(+), 48 deletions(-) rename src/controllers/panel/{placeholder.js => placeholderController.js} (100%) create mode 100644 src/controllers/panel/popoutContainerController.js create mode 100644 src/views/panels/popoutContainer.ejs create mode 100644 www/css/panel.css diff --git a/src/controllers/panel/placeholder.js b/src/controllers/panel/placeholderController.js similarity index 100% rename from src/controllers/panel/placeholder.js rename to src/controllers/panel/placeholderController.js diff --git a/src/controllers/panel/popoutContainerController.js b/src/controllers/panel/popoutContainerController.js new file mode 100644 index 0000000..78082b8 --- /dev/null +++ b/src/controllers/panel/popoutContainerController.js @@ -0,0 +1,23 @@ +/*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 .*/ + +//config +const config = require('../../../config.json'); + +//popout panel container functions +module.exports.get = async function(req, res){ + res.render('panels/popoutContainer', {instance: config.instanceName}); +} \ No newline at end of file diff --git a/src/routers/panelRouter.js b/src/routers/panelRouter.js index cd6add6..4b5d9d3 100644 --- a/src/routers/panelRouter.js +++ b/src/routers/panelRouter.js @@ -19,12 +19,14 @@ const { Router } = require('express'); //local imports -const placeholderController = require("../controllers/panel/placeholder"); +const placeholderController = require("../controllers/panel/placeholderController"); +const popoutContainerController = require("../controllers/panel/popoutContainerController"); //globals const router = Router(); //routing functions router.get('/placeholder', placeholderController.get); +router.get('/popoutContainer', popoutContainerController.get); module.exports = router; diff --git a/src/views/channel.ejs b/src/views/channel.ejs index f6e1e0c..7826f96 100644 --- a/src/views/channel.ejs +++ b/src/views/channel.ejs @@ -19,6 +19,7 @@ along with this program. If not, see .--> <%- include('partial/styles', {instance, user}); %> + <%= instance %> - *DISCONNECTED* @@ -69,9 +70,10 @@ along with this program. If not, see .-->
+

NULL PANEL

- +
@@ -79,8 +81,10 @@ along with this program. If not, see .-->
+

NULL PANEL

+
diff --git a/src/views/panels/popoutContainer.ejs b/src/views/panels/popoutContainer.ejs new file mode 100644 index 0000000..bc5068d --- /dev/null +++ b/src/views/panels/popoutContainer.ejs @@ -0,0 +1,35 @@ + + + + + <%- include('../partial/styles'); %> + + <%= instance %> - NULL_POPOUT + + +
+
+ +

NULL PANEL

+ + +
+
+
+
+ + \ No newline at end of file diff --git a/www/css/channel.css b/www/css/channel.css index 56bef44..2fa00c7 100644 --- a/www/css/channel.css +++ b/www/css/channel.css @@ -214,12 +214,6 @@ input#chat-panel-prompt{ display: none; } -.cpanel-div{ - display: none; - flex-direction: column; - overflow: hidden; - width: 30%; -} #cpanel-active-div{ position: absolute; z-index: 3; @@ -228,23 +222,7 @@ input#chat-panel-prompt{ left: 0; } -.cpanel-header-div{ - display: flex; - margin: 0 auto; - width: 96%; - height: 1.2em; -} - -.cpanel-title{ - margin: 0; - white-space: nowrap; - overflow: hidden; -} - -.cpanel-title-spacer{ - flex: 1; -} - -.cpanel-header-icon{ - cursor: pointer; +.cpanel-div{ + display: none; + width: 30%; } \ No newline at end of file diff --git a/www/css/panel.css b/www/css/panel.css new file mode 100644 index 0000000..6c73c33 --- /dev/null +++ b/www/css/panel.css @@ -0,0 +1,25 @@ +.cpanel-div{ + flex-direction: column; + overflow: hidden; +} + +.cpanel-header-div{ + display: flex; + margin: 0 auto; + width: calc(100% - 0.5em); + height: 1.2em; +} + +.cpanel-title{ + margin: 0; + white-space: nowrap; + overflow: hidden; +} + +.cpanel-title-spacer{ + flex: 1; +} + +.cpanel-header-icon{ + cursor: pointer; +} \ No newline at end of file diff --git a/www/js/channel/channel.js b/www/js/channel/channel.js index 2e1979e..5aa4ec5 100644 --- a/www/js/channel/channel.js +++ b/www/js/channel/channel.js @@ -39,7 +39,6 @@ class channel{ } defineListeners(){ - //This function should serve mostly to glue functions from channel and it's children to it's socket's listeners. this.socket.on("connect", () => { document.title = `${this.channelName} - Connected` }); diff --git a/www/js/channel/chat.js b/www/js/channel/chat.js index ff2011f..435ecf6 100644 --- a/www/js/channel/chat.js +++ b/www/js/channel/chat.js @@ -27,10 +27,13 @@ class chatBox{ //Element Nodes this.chatPanel = document.querySelector("#chat-panel-div"); + this.highLevel = document.querySelector("#chat-panel-high-level-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"); - this.highLevel = document.querySelector("#chat-panel-high-level-select"); //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"); @@ -46,6 +49,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)}); //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 ed144ea..ae3ec3e 100644 --- a/www/js/channel/cpanel.js +++ b/www/js/channel/cpanel.js @@ -30,11 +30,14 @@ class cPanel{ this.activePanelTitle = document.querySelector("#cpanel-active-title"); this.activePanelDoc = document.querySelector("#cpanel-active-doc"); this.activePanelPinIcon = document.querySelector("#cpanel-active-pin-icon"); + this.activePanelPopoutIcon = document.querySelector("#cpanel-active-popout-icon"); this.activePanelCloseIcon = document.querySelector("#cpanel-active-close-icon"); //Pinned Panel this.pinnedPanelDiv = document.querySelector("#cpanel-pinned-div"); this.pinnedPanelTitle = document.querySelector("#cpanel-pinned-title"); this.pinnedPanelDoc = document.querySelector("#cpanel-pinned-doc"); + this.activePanelUnpinIcon = document.querySelector("#cpanel-pinned-unpin-icon"); + this.pinnedPanelPopoutIcon = document.querySelector("#cpanel-pinned-popout-icon"); this.pinnedPanelCloseIcon = document.querySelector("#cpanel-pinned-close-icon"); this.setupInput(); @@ -42,16 +45,20 @@ class cPanel{ setupInput(){ this.activePanelCloseIcon.addEventListener("click", this.hideActivePanel.bind(this)); - this.activePanelPinIcon.addEventListener("click", this.pinActivePanel.bind(this)); + this.activePanelPinIcon.addEventListener("click", this.pinPanel.bind(this)); + this.activePanelPopoutIcon.addEventListener("click", this.popActivePanel.bind(this)); this.pinnedPanelCloseIcon.addEventListener("click", this.hidePinnedPanel.bind(this)); + this.activePanelUnpinIcon.addEventListener("click", this.unpinPanel.bind(this)); + this.pinnedPanelPopoutIcon.addEventListener("click", this.popPinnedPanel.bind(this)); } - async setActivePanel(panel){ + async setActivePanel(panel, panelBody){ //Set active panel this.activePanel = panel; //Grab panel hypertext content and load it into div - this.activePanelDoc.innerHTML = await this.activePanel.getPage(); + this.activePanelDoc.innerHTML = (panelBody == null || panelBody == "") ? await this.activePanel.getPage() : panelBody; + //Display panel this.activePanelDiv.style.display = "flex"; @@ -66,23 +73,17 @@ class cPanel{ this.activePanel = null; } - pinActivePanel(){ - //Yoink panel object - this.pinnedPanel = this.activePanel - - //Yoink Content and Title - this.pinnedPanelTitle.textContent = this.pinnedPanel.name; - this.pinnedPanelDoc.innerHTML = this.activePanelDoc.innerHTML; - - //Do a switchem - this.pinnedPanelDiv.style.display = "flex"; + pinPanel(){ + this.setPinnedPanel(this.activePanel, this.activePanelDoc.innerHTML); this.hideActivePanel(); - - //Re-Run panel init function - this.pinnedPanel.panelInit(); } - async setPinnedPanel(panel){ + popActivePanel(){ + this.popPanel(this.activePanel, this.activePanelDoc.innerHTML); + this.hideActivePanel(); + } + + async setPinnedPanel(panel, panelBody){ //Set pinned panel this.pinnedPanel = panel; @@ -90,7 +91,7 @@ class cPanel{ this.pinnedPanelTitle.textContent = this.pinnedPanel.name; //Grab panel hypertext content and load it into div - this.pinnedPanelDoc.innerHTML = await this.pinnedPanel.getPage(); + this.pinnedPanelDoc.innerHTML = (panelBody == null || panelBody == "") ? await this.pinnedPanel.getPage() : panelBody; //Display panel this.pinnedPanelDiv.style.display = "flex"; @@ -104,13 +105,29 @@ class cPanel{ this.pinnedPanel = null; } + unpinPanel(){ + this.setActivePanel(this.pinnedPanel, this.pinnedPanelDoc.innerHTML); + this.hidePinnedPanel(); + } + + popPinnedPanel(){ + this.popPanel(this.pinnedPanel, this.pinnedPanelDoc.innerHTML); + this.hidePinnedPanel(); + } + + popPanel(panel, panelBody){ + var newPanel = new poppedPanel(panel, panelBody, this) + + this.poppedPanels.push(newPanel); + } } class panelObj{ - constructor(name = "Placeholder Panel", pageURL = "/panel/placeholder"){ + constructor(name = "Placeholder Panel", pageURL = "/panel/placeholder", panelDocument = window.document){ this.name = name; this.pageURL = pageURL; + this.panelDocument = panelDocument; } async getPage(){ @@ -123,4 +140,80 @@ class panelObj{ panelInit(){ } +} + +class poppedPanel{ + constructor(panel, panelBody, cPanel){ + //Set Panel Object + this.panel = panel; + //Set Panel Body + this.panelBody = panelBody; + //Set Window Placeholder + this.window = null; + + //Element Node Placeholders + this.pinnedPanelDiv = null; + this.pinnedPanelTitle = null; + this.pinnedPanelDoc = null; + this.pinnedPanelCloseIcon = null; + + //Functions + this.cPanel = cPanel; + + //Continue constructor asynchrnously + this.asyncConstructor(); + } + + async asyncConstructor(){ + //Set panel body properly + this.panelBody = (this.panelBody == null || this.panelBody == "") ? await this.panel.getPage() : this.panelBody; + + //Pop the panel + this.popContainer(); + } + + popContainer(){ + //Set Window Object + this.window = window.open("/panel/popoutContainer","",`menubar=no,height=850,width=600`); + this.window.addEventListener("load", this.fillContainer.bind(this)); + } + + fillContainer(){ + //Set Element Nodes + this.panelDiv = this.window.document.querySelector("#cpanel-div"); + this.panelTitle = this.window.document.querySelector("#cpanel-title"); + this.panelDoc = this.window.document.querySelector("#cpanel-doc"); + this.panelPopinIcon = this.window.document.querySelector("#cpanel-popin-icon"); + this.panelPinIcon = this.window.document.querySelector("#cpanel-pin-icon"); + + //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; + + this.setupInput(); + } + + setupInput(){ + this.panelPopinIcon.addEventListener("click", this.unpop.bind(this)); + this.panelPinIcon.addEventListener("click", this.pin.bind(this)); + this.window.addEventListener("unload", this.closer.bind(this)); + } + + closer(){ + this.cPanel.poppedPanels.splice(this.cPanel.poppedPanels.indexOf(this),1); + } + + unpop(){ + //Set active panel + this.cPanel.setActivePanel(this.panel, this.panelDoc.innerHTML); + this.window.close(); + } + + pin(){ + this.cPanel.setPinnedPanel(this.panel, this.panelDoc.innerHTML); + this.window.close(); + } + } \ No newline at end of file