Per-Channel Emotes Implemented.

This commit is contained in:
rainbow napkin 2024-12-21 11:01:00 -05:00
parent 163cecf9f0
commit c3d016e1af
14 changed files with 483 additions and 20 deletions

View file

@ -539,8 +539,6 @@ class adminEmoteList{
//Strip name from element id
const name = event.target.id.replace('emote-list-delete-','');
console.log(name);
//Delete emote and pull list
const list = await adminUtil.deleteEmote(name);

View file

@ -15,6 +15,7 @@ class commandPreprocessor{
defineListeners(){
//When we receive site-wide emote list
this.client.socket.on("siteEmotes", this.setSiteEmotes.bind(this));
this.client.socket.on("chanEmotes", this.setChanEmotes.bind(this));
}
preprocess(command){
@ -53,23 +54,30 @@ class commandPreprocessor{
}
processEmotes(){
//Does checking each word for an emote use more or less cycles than running replace against each emote?
//Not sure, but it's sure as fuck easier to write it that way lmao
this.emotes.site.forEach((emote) => {
this.message = this.message.replaceAll(`[${emote.name}]`, emote.link);
});
//inject invisible whitespace in-between emotes to prevent from mushing links together
this.message = this.message.replaceAll('][','][');
//For each list of emotes
Object.keys(this.emotes).forEach((key) => {
//For each emote in the current list
this.emotes[key].forEach((emote) => {
//Inject emote links into the message
this.message = this.message.replaceAll(`[${emote.name}]`, emote.link);
});
})
}
processLinks(){
//Strip out file seperators in-case the user is being a smart-ass
this.message = this.message.replaceAll('␜','');
//Split message by links
var splitMessage = this.message.split(/(https?:\/\/[^\s]+)/g);
var splitMessage = this.message.split(/(https?:\/\/[^\s]+)/g);
//Create an empty array to hold links
this.links = [];
splitMessage.forEach((chunk, chunkIndex) => {
if(chunk.match(/(https?:\/\/[^\s]+)/g)){
//For each chunk that is a link
if(chunk.match(/(https?:\/\/[^\s]+)/g)){
//I looked online for obscure characters that no one would use to prevent people from chatting embed placeholders
//Then I found this fucker, turns out it's literally made for the job lmao (even if it was originally intended for paper/magnetic tape)
//Replace link with indexed placeholder
@ -92,6 +100,10 @@ class commandPreprocessor{
this.emotes.site = data;
}
setChanEmotes(data){
this.emotes.chan = data;
}
getEmoteByLink(link){
//Create an empty variable to hold the found emote
var foundEmote = null;

View file

@ -24,6 +24,7 @@ class channelSettingsPage{
this.permList = new permList(this.channel);
this.prefrenceList = new prefrenceList(this.channel);
this.tokeCommandList = new tokeCommandList(this.channel);
this.emoteList = new emoteList(this.channel);
this.deleteBtn = new deleteBtn(this.channel);
}
}
@ -413,6 +414,124 @@ class tokeCommandList{
}
}
class emoteList{
constructor(channel){
this.channel = channel
this.linkPrompt = document.querySelector('#new-emote-link-input');
this.namePrompt = document.querySelector('#new-emote-name-input');
this.addButton = document.querySelector('#new-emote-button');
this.emoteList = document.querySelector('#emote-list');
//Setup input
this.setupInput();
//Pull and render emote list
this.updateList();
}
setupInput(){
this.addButton.addEventListener('click', this.addEmote.bind(this));
}
async deleteEmote(event){
//Strip name from element id
const name = event.target.id.replace('emote-list-delete-','');
//Delete emote and pull list
const list = await utils.ajax.deleteChanEmote(this.channel, name);
//If we received a list
if(list != null){
//Pass updated liste to renderEmoteList function instead of pulling it twice
this.renderEmoteList(list);
}
}
async addEmote(event){
//Add emote to list and ingest returned updates list
const list = await utils.ajax.addChanEmote(this.channel, this.namePrompt.value, this.linkPrompt.value);
//If we received a list
if(list != null){
//Pass updated liste to renderEmoteList function instead of pulling it twice
this.renderEmoteList(list);
//Clear out the prompts
this.namePrompt.value = '';
this.linkPrompt.value = '';
}
}
async updateList(){
const list = await utils.ajax.getChanEmotes(this.channel);
this.renderEmoteList(list);
}
renderEmoteList(list){
//Clear the current list
this.emoteList.innerHTML = "";
//For each emote in the list
list.forEach((emote) => {
//Create span to hold emote
const emoteDiv = document.createElement('div');
emoteDiv.classList.add('emote-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 title span
const titleSpan = document.createElement('span');
titleSpan.classList.add('emote-list-title');
//Create paragraph tag
const emoteTitle = document.createElement('p');
//Set title class
emoteTitle.classList.add('emote-list-title');
//Set emote title
emoteTitle.innerHTML = `[${emote.name}]`;
//Create delete icon
const deleteIcon = document.createElement('i');
//Set delete icon id and class
deleteIcon.classList.add('bi-trash-fill', 'emote-list-delete');
deleteIcon.id = `emote-list-delete-${emote.name}`;
//Add delete icon event listener
deleteIcon.addEventListener('click',this.deleteEmote.bind(this));
//Add the emote media to the emote span
emoteDiv.appendChild(emoteMedia);
//Add title paragraph node
titleSpan.appendChild(emoteTitle);
//Add trash icon node
titleSpan.appendChild(deleteIcon);
//Add title span
emoteDiv.appendChild(titleSpan);
//Append the mote span to the emote list
this.emoteList.appendChild(emoteDiv);
});
}
}
class deleteBtn{
constructor(channel){
this.channel = channel;
@ -444,12 +563,11 @@ class deleteAccountPopup{
}
setupInput(){
this.prompt.addEventListener("keydown", this.nukeAccount.bind(this));
this.prompt.addEventListener("keydown", this.nukeChannel.bind(this));
}
async nukeAccount(event){
async nukeChannel(event){
if(event.key == "Enter"){
console.log(this.channel);
if(this.channel === event.target.value){
await utils.ajax.deleteChannel(this.channel, event.target.value);
}

View file

@ -561,6 +561,54 @@ class canopyAjaxUtils{
utils.ux.displayResponseError(await response.json());
}
}
async getChanEmotes(chanName){
var response = await fetch(`/api/channel/emote?chanName=${chanName}`,{
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
if(response.status == 200){
return await response.json();
}else{
utils.ux.displayResponseError(await response.json());
}
}
async addChanEmote(chanName, emoteName, link){
var response = await fetch('/api/channel/emote',{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({chanName, emoteName, link})
});
if(response.status == 200){
return await response.json();
}else{
utils.ux.displayResponseError(await response.json());
}
}
async deleteChanEmote(chanName, emoteName){
var response = await fetch('/api/channel/emote',{
method: "DELETE",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({chanName, emoteName})
});
if(response.status == 200){
return await response.json();
}else{
utils.ux.displayResponseError(await response.json());
}
}
}
const utils = new canopyUtils()