Started work on emote panel in admin page.

This commit is contained in:
rainbow napkin 2024-12-18 07:05:29 -05:00
parent 255e6e0d7f
commit 90d67024b7
8 changed files with 107 additions and 22 deletions

View file

@ -29,6 +29,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<%- include('partial/adminPanel/permList', {permList, rankEnum}) %> <%- include('partial/adminPanel/permList', {permList, rankEnum}) %>
<%- include('partial/adminPanel/userBanList') %> <%- include('partial/adminPanel/userBanList') %>
<%- include('partial/adminPanel/tokeCommandList') %> <%- include('partial/adminPanel/tokeCommandList') %>
<%- include('partial/adminPanel/emoteList') %>
</div> </div>
</body> </body>
<footer> <footer>

View file

@ -0,0 +1,28 @@
<!--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/>.-->
<div id="emote-list-div" class="admin-list-div">
<h3>Emote List:</h3>
<div class="control-prompt">
<!-- Probably not the cleanest way to do this but fuggit -->
[<input placeholder="Emote Name..." id="new-emote-name-input" class="control-prompt">]
<button id="new-emote-button" class="positive-button">Add</button>
</div>
<div class="control-prompt">
<input placeholder="Emote Link..." id="new-emote-link-input" class="control-prompt">
</div>
<div class="dynamic-container" id="emote-list">
</div>
</div>

View file

@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<div id="toke-command-list-div" class="admin-list-div"> <div id="toke-command-list-div" class="admin-list-div">
<h3>Toke Command List:</h3> <h3>Toke Command List:</h3>
<div class="control-prompt"> <div class="control-prompt">
<input placeholder="Add Command..." id="new-toke-command-input" class="control-prompt"> !<input placeholder="Add Command..." id="new-toke-command-input" class="control-prompt">
<button id="new-toke-command-button" class="positive-button">Add</button> <button id="new-toke-command-button" class="positive-button">Add</button>
</div> </div>
<div class="dynamic-container toke-command-list"> <div class="dynamic-container toke-command-list">

View file

@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<div id="toke-command-list-div" class="admin-list-div"> <div id="toke-command-list-div" class="admin-list-div">
<h3>Toke Command List:</h3> <h3>Toke Command List:</h3>
<div class="control-prompt"> <div class="control-prompt">
<input placeholder="Add Command..." id="new-toke-command-input" class="control-prompt"> !<input placeholder="Add Command..." id="new-toke-command-input" class="control-prompt">
<button id="new-toke-command-button" class="positive-button">Add</button> <button id="new-toke-command-button" class="positive-button">Add</button>
</div> </div>
<div class="dynamic-container toke-command-list"> <div class="dynamic-container toke-command-list">

View file

@ -262,6 +262,10 @@ span.user-entry{
margin: 0.5em; margin: 0.5em;
} }
#chat-panel-prompt{
margin-left: 0.5em;
}
.toke{ .toke{
text-align: center; text-align: center;
margin: 0.2em auto; margin: 0.2em auto;

View file

@ -61,7 +61,6 @@ i.control-prompt{
} }
input.control-prompt{ input.control-prompt{
margin-left: 0.5em;
width: 100%; width: 100%;
flex: 1; flex: 1;
} }

View file

@ -517,8 +517,57 @@ class adminTokeCommandList{
} }
} }
class adminEmoteList{
constructor(){
this.linkPrompt = document.querySelector('new-emote-link-input');
this.namePrompt = document.querySelector('new-emote-link-input');
this.emoteList = document.querySelector('#emote-list');
this.updateList();
}
async updateList(){
const list = await adminUtil.getEmotes();
this.renderEmoteList(list);
}
renderEmoteList(list){
//Clear the current list
this.emoteList.innerHTML = "";
//For each emote in the list
list.forEach((emote) => {
const emoteSpan = document.createElement('span');
emoteSpan.classList.add('emote-list-emote');
//If the emote is an image
if(emote.type == 'image'){
//Create image node
var emoteMedia = document.createElement('img');
//add link as source
emoteMedia.src = emote.link;
//if emote is a video
}else if(emote.type == 'video'){
//create video node
var emoteMedia = document.createElement('video');
//Add video link
emoteMeida.src = emote.link;
//Set video properties
emoteMedia.autoplay = true;
emoteMedia.muted = true;
emoteMedia.controls = false;
}
emoteSpan.appendChild(emoteMedia);
this.emoteList.appendChild(emoteSpan);
});
}
}
const adminUtil = new canopyAdminUtils(); const adminUtil = new canopyAdminUtils();
const userList = new adminUserList(); const userList = new adminUserList();
const permissionList = new adminPermissionList(); const permissionList = new adminPermissionList();
const userBanList = new adminUserBanList(); const userBanList = new adminUserBanList();
const tokeCommandList = new adminTokeCommandList(); const tokeCommandList = new adminTokeCommandList();
const emoteList = new adminEmoteList();

View file

@ -7,9 +7,9 @@ class chatPostprocessor{
//Set current chat nodes //Set current chat nodes
this.chatEntry = chatEntry; this.chatEntry = chatEntry;
this.chatBody = this.chatEntry.querySelector(".chat-entry-body"); this.chatBody = this.chatEntry.querySelector(".chat-entry-body");
//Split the chat body into an array of objects representing each word
//We could pass this through arguments but these functions wont be very interoperable anyways since they expect a purpose-made hashtable //Split the chat message into an array of objects representing each word
this.splitBody(); this.splitMessage();
//Inject links into un-processed placeholders //Inject links into un-processed placeholders
this.processLinks(); this.processLinks();
@ -26,9 +26,9 @@ class chatPostprocessor{
return this.chatEntry; return this.chatEntry;
} }
splitBody(){ splitMessage(){
//Create an empty array to hold the body //Create an empty array to hold the body
this.bodyArray = []; this.messageArray = [];
//Split string by word-boundries, with negative lookaheads to exclude file seperators so we don't split link placeholders //Split string by word-boundries, with negative lookaheads to exclude file seperators so we don't split link placeholders
const splitString = this.rawData.msg.split(/(?<!␜)\b/g); const splitString = this.rawData.msg.split(/(?<!␜)\b/g);
@ -41,7 +41,7 @@ class chatPostprocessor{
} }
//Add it to our body array //Add it to our body array
this.bodyArray.push(wordObj); this.messageArray.push(wordObj);
}); });
} }
@ -51,7 +51,7 @@ class chatPostprocessor{
const _this = this; const _this = this;
//For each word object //For each word object
this.bodyArray.forEach((wordObj) => { this.messageArray.forEach((wordObj) => {
if(wordObj.type == 'word'){ if(wordObj.type == 'word'){
//Inject current wordObj string into the chat body //Inject current wordObj string into the chat body
injectString(wordObj.string); injectString(wordObj.string);
@ -60,7 +60,7 @@ class chatPostprocessor{
const link = document.createElement('a'); const link = document.createElement('a');
link.classList.add('chat-link'); link.classList.add('chat-link');
link.href = wordObj.link; link.href = wordObj.link;
//Use textContent to be safe since links can't be escaped //Use textContent to be safe since links can't be escaped serverside
link.textContent = wordObj.link; link.textContent = wordObj.link;
//Append node to chatBody //Append node to chatBody
@ -70,7 +70,7 @@ class chatPostprocessor{
const badLink = document.createElement('a'); const badLink = document.createElement('a');
badLink.classList.add('chat-dead-link', 'danger-link'); badLink.classList.add('chat-dead-link', 'danger-link');
badLink.href = wordObj.link; badLink.href = wordObj.link;
//Use textContent to be safe since links can't be escaped //Use textContent to be safe since links can't be escaped serverside
badLink.textContent = wordObj.link; badLink.textContent = wordObj.link;
//Append node to chatBody //Append node to chatBody
@ -91,7 +91,6 @@ class chatPostprocessor{
img.classList.add('chat-img'); img.classList.add('chat-img');
img.src = wordObj.link; img.src = wordObj.link;
//stringArray.push(wordObj.string.replace('␜',img.outerHTML));
//Append node to chatBody //Append node to chatBody
injectNode(wordObj, img); injectNode(wordObj, img);
}else if(wordObj.type == 'video'){ }else if(wordObj.type == 'video'){
@ -104,7 +103,6 @@ class chatPostprocessor{
vid.loop = true; vid.loop = true;
vid.muted = true; vid.muted = true;
//stringArray.push(wordObj.string.replace('␜',vid.outerHTML));
injectNode(wordObj, vid); injectNode(wordObj, vid);
} }
}); });
@ -115,8 +113,9 @@ class chatPostprocessor{
if(typeof item == "string"){ if(typeof item == "string"){
//Add it to the chat's innerHTML (it should already be escaped by the server) //Add it to the chat's innerHTML (it should already be escaped by the server)
this.chatBody.innerHTML += item; this.chatBody.innerHTML += item;
//Otherwise it should be a DOM node
}else{ }else{
//Otherwise it should be a DOM node, therefore we should append it //Append the node to our chat body
this.chatBody.appendChild(item); this.chatBody.appendChild(item);
} }
}) })
@ -151,12 +150,14 @@ class chatPostprocessor{
addWhitespace(){ addWhitespace(){
//for each word object in the body //for each word object in the body
this.bodyArray.forEach((wordObj, wordIndex) => { this.messageArray.forEach((wordObj, wordIndex) => {
//if the word object hasn't been pre-processed elsewhere //if the word object hasn't been pre-processed elsewhere
if(wordObj.type == "word"){ if(wordObj.type == "word"){
//Create an empty array to hold our word
var wordArray = []; var wordArray = [];
//Add invisible whitespace in-between characters to keep it from breaking page layout //For each character in the string of the current word object
this.bodyArray[wordIndex].string.split("").forEach((char, charIndex) => { this.messageArray[wordIndex].string.split("").forEach((char, charIndex) => {
//push the current character to the wordArray
wordArray.push(char); wordArray.push(char);
//After eight characters //After eight characters
if(charIndex > 8){ if(charIndex > 8){
@ -166,7 +167,8 @@ class chatPostprocessor{
}); });
this.bodyArray[wordIndex].string = wordArray.join(""); //Join the wordArray into a single string, and use it to set the current wordObject's string
this.messageArray[wordIndex].string = wordArray.join("");
} }
}); });
} }
@ -178,12 +180,14 @@ class chatPostprocessor{
return; return;
} }
//For every link received in this message
this.rawData.links.forEach((link, linkIndex) => { this.rawData.links.forEach((link, linkIndex) => {
this.bodyArray.forEach((wordObj, wordIndex) => { //For every word obj in the message array
this.messageArray.forEach((wordObj, wordIndex) => {
//Check current wordobj for link (placeholder may contain whitespace with it) //Check current wordobj for link (placeholder may contain whitespace with it)
if(wordObj.string.match(`${linkIndex}`)){ if(wordObj.string.match(`${linkIndex}`)){
//Set current word object in the body array to the new link object //Set current word object in the body array to the new link object
this.bodyArray[wordIndex] = { this.messageArray[wordIndex] = {
//Don't want to use a numbered placeholder to make this easier during body injection //Don't want to use a numbered placeholder to make this easier during body injection
//but we also don't want to clobber any surrounding whitespace //but we also don't want to clobber any surrounding whitespace
string: wordObj.string.replace(`${linkIndex}`, '␜'), string: wordObj.string.replace(`${linkIndex}`, '␜'),