Added pop-out Canopy Panels.

Also added un-pin button to pinned Canopy Panel
This commit is contained in:
rainbownapkin 2024-11-16 12:34:24 -05:00
parent 9a17dc5c86
commit 3c185b4e28
10 changed files with 213 additions and 48 deletions

View file

@ -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 <https://www.gnu.org/licenses/>.*/
//config
const config = require('../../../config.json');
//popout panel container functions
module.exports.get = async function(req, res){
res.render('panels/popoutContainer', {instance: config.instanceName});
}

View file

@ -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;

View file

@ -19,6 +19,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<head>
<%- include('partial/styles', {instance, user}); %>
<link rel="stylesheet" type="text/css" href="/css/channel.css">
<link rel="stylesheet" type="text/css" href="/css/panel.css">
<title><%= instance %> - *DISCONNECTED*</title>
</head>
<body>
@ -69,9 +70,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<div class="chat-panel" id="chat-panel-main-div">
<div class="active-cpanel cpanel-div" id="cpanel-active-div">
<div class="active-cpanel cpanel-header-div" id="cpanel-active-header-div">
<i class="active-cpanel cpanel-header-icon cpanel-pin-icon bi-pin-angle-fill" id="cpanel-active-pin-icon"></i>
<p class="active-cpanel cpanel-title" id="cpanel-active-title">NULL PANEL</p>
<span class="active-cpanel cpanel-title-spacer" id="cpanel-active-title-spacer"></span>
<i class="active-cpanel cpanel-header-icon cpanel-pin-icon bi-pin-angle-fill" id="cpanel-active-pin-icon"></i>
<i class="active-cpanel cpanel-header-icon cpanel-popout-icon bi-window-stack" id="cpanel-active-popout-icon"></i>
<i class="active-cpanel cpanel-header-icon cpanel-close-icon bi-x" id="cpanel-active-close-icon"></i>
</div>
<div class="active-cpanel cpanel-doc" id="cpanel-active-doc">
@ -79,8 +81,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
</div>
<div class="pinned-cpanel cpanel-div" id="cpanel-pinned-div">
<div class="pinned-cpanel cpanel-header-div" id="cpanel-pinned-header-div">
<i class="pinned-cpanel cpanel-header-icon cpanel-unpin-icon bi-pin-fill" id="cpanel-pinned-unpin-icon"></i>
<p class="pinned-cpanel cpanel-title" id="cpanel-pinned-title">NULL PANEL</p>
<span class="pinned-cpanel cpanel-title-spacer" id="cpanel-pinned-title-spacer"></span>
<i class="pinned-cpanel cpanel-header-icon cpanel-popout-icon bi-window-stack" id="cpanel-pinned-popout-icon"></i>
<i class="pinned-cpanel cpanel-header-icon cpanel-close-icon bi-x" id="cpanel-pinned-close-icon"></i>
</div>
<div class="pinned-cpanel cpanel-doc" id="cpanel-pinned-doc">

View file

@ -0,0 +1,35 @@
<!--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 <https://www.gnu.org/licenses/>.-->
<!DOCTYPE html>
<html>
<head>
<%- include('../partial/styles'); %>
<link rel="stylesheet" type="text/css" href="/css/panel.css">
<title><%= instance %> - NULL_POPOUT</title>
</head>
<body>
<div class="cpanel-div" id="cpanel-div">
<div class="cpanel-header-div" id="cpanel-header-div">
<i class="cpanel cpanel-header-icon cpanel-pin-icon bi-pin-angle-fill" id="cpanel-pin-icon"></i>
<p class="cpanel-title" id="cpanel-title">NULL PANEL</p>
<span class="cpanel-title-spacer" id="cpanel-title-spacer"></span>
<i class="cpanel cpanel-header-icon cpanel-popin-icon bi-window-split" id="cpanel-popin-icon"></i>
</div>
<div class="cpanel-doc" id="cpanel-doc">
</div>
</div>
</body>
</html>

View file

@ -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%;
}

25
www/css/panel.css Normal file
View file

@ -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;
}

View file

@ -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`
});

View file

@ -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));

View file

@ -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();
}
}