diff --git a/src/app/channel/activeChannel.js b/src/app/channel/activeChannel.js index be2121b..7abbc5b 100644 --- a/src/app/channel/activeChannel.js +++ b/src/app/channel/activeChannel.js @@ -55,6 +55,7 @@ module.exports = class{ //Make sure the client receives important client-info before userlist //await this.sendClientMetadata(userDB, socket); await userObj.sendClientMetadata(); + await userObj.sendSiteEmotes(); //Send out the userlist this.broadcastUserList(socket.chan); diff --git a/src/app/channel/channelManager.js b/src/app/channel/channelManager.js index d786e50..ebb886d 100644 --- a/src/app/channel/channelManager.js +++ b/src/app/channel/channelManager.js @@ -16,6 +16,7 @@ along with this program. If not, see .*/ //Local Imports const channelModel = require('../../schemas/channel/channelSchema'); +const emoteModel = require('../../schemas/emoteSchema'); const {userModel} = require('../../schemas/userSchema'); const loggerUtils = require('../../utils/loggerUtils'); const activeChannel = require('./activeChannel'); @@ -181,4 +182,12 @@ module.exports = class{ //crawl through connections and kick user this.crawlConnections(user,(foundUser)=>{foundUser.disconnect(reason)}); } + + async broadcastSiteEmotes(){ + //Get emote list from DB + const emoteList = await emoteModel.getEmotes(); + + //Broadcast that sumbitch + this.io.sockets.emit('siteEmotes', emoteList); + } } \ No newline at end of file diff --git a/src/app/channel/connectedUser.js b/src/app/channel/connectedUser.js index 3f444c0..6977e66 100644 --- a/src/app/channel/connectedUser.js +++ b/src/app/channel/connectedUser.js @@ -16,6 +16,7 @@ along with this program. If not, see .*/ //local imports const flairModel = require('../../schemas/flairSchema'); +const emoteModel = require('../../schemas/emoteSchema'); const permissionModel = require('../../schemas/permissionSchema'); module.exports = class{ @@ -87,6 +88,14 @@ module.exports = class{ this.emit("clientMetadata", {user: userObj, flairList}); } + async sendSiteEmotes(){ + //Get emote list from DB + const emoteList = await emoteModel.getEmotes(); + + //Send it off to the user + this.emit('siteEmotes', emoteList); + } + updateFlair(flair){ this.flair = flair; diff --git a/src/app/channel/tokebot.js b/src/app/channel/tokebot.js index a48b335..536d007 100644 --- a/src/app/channel/tokebot.js +++ b/src/app/channel/tokebot.js @@ -53,7 +53,7 @@ module.exports = class tokebot{ tokeProcessor(commandObj){ //Check for site-wide toke commands - if(this.tokeCommands.indexOf(commandObj.argumentArray[0]) != -1){ + if(this.tokeCommands.indexOf(commandObj.argumentArray[0].toLowerCase()) != -1){ //Seems lame to set a bool in an if statement but this would've made a really ugly turinary var foundToke = true; }else{ @@ -62,7 +62,7 @@ module.exports = class tokebot{ //Check if they're using a channel-only toke //This should be safe to do without a null check but someone prove me wrong lmao - var foundToke = (activeChan.tokeCommands.indexOf(commandObj.argumentArray[0]) != -1); + var foundToke = (activeChan.tokeCommands.indexOf(commandObj.argumentArray[0].toLowerCase()) != -1); } @@ -76,7 +76,7 @@ module.exports = class tokebot{ this.tokeCounter = this.tokeTime; //Add the toking user to the tokers map - this.tokers.set(commandObj.socket.user.user, commandObj.argumentArray[0]); + this.tokers.set(commandObj.socket.user.user, commandObj.argumentArray[0].toLowerCase()); //kick-off the count-down this.tokeTimer = setTimeout(this.countdown.bind(this), 1000) @@ -91,7 +91,7 @@ module.exports = class tokebot{ this.chatHandler.relayTokeCallout(`${commandObj.socket.user.user} has joined the toke from #${commandObj.socket.chan}! Post !${commandObj.argumentArray[0]} to take part!`); //Add the toking user to the tokers map - this.tokers.set(commandObj.socket.user.user, commandObj.argumentArray[0]); + this.tokers.set(commandObj.socket.user.user, commandObj.argumentArray[0].toLowerCase()); //If the user is already in the toke }else{ //Tell them to fuck off diff --git a/src/schemas/emoteSchema.js b/src/schemas/emoteSchema.js index d3c5855..782aaca 100644 --- a/src/schemas/emoteSchema.js +++ b/src/schemas/emoteSchema.js @@ -19,6 +19,7 @@ const {mongoose} = require('mongoose'); //Local Imports const defaultEmote = require("../../defaultEmotes.json"); +const server = require('../server'); const typeEnum = ["image", "video"]; @@ -39,6 +40,19 @@ const emoteSchema = new mongoose.Schema({ } }); +//pre-save function +emoteSchema.post('save', async function (next){ + //broadcast updated emotes + server.channelManager.broadcastSiteEmotes(); +}); + +//post-delete function (document not query) +emoteSchema.post('deleteOne', {document: true}, async function (next){ + //broadcast updated emotes + server.channelManager.broadcastSiteEmotes(); +}); + +//statics emoteSchema.statics.loadDefaults = async function(){ //Make sure registerEmote function is happy const _this = this; diff --git a/www/css/adminPanel.css b/www/css/adminPanel.css index ab577d8..98fdd00 100644 --- a/www/css/adminPanel.css +++ b/www/css/adminPanel.css @@ -102,19 +102,14 @@ img.admin-list-entry-item{ width: 11em; } -#toke-command-list-div{ - /*Maybe I suck at CSS but I can't get relative percentage-based heights to play nice here. - Either way sizing this by total viewheight isn't too big a deal. At least here...*/ - min-height: 9em; - max-height: 20vh; -} - div.toke-command-list{ display: grid; grid-template-columns: repeat(auto-fit, minmax(15em, 1fr)); text-align: center; padding: 1em; overflow-y: auto; + min-height: 0.8em; + max-height: 5.5em; } span.toke-command-list{ @@ -131,16 +126,13 @@ i.toke-command-list{ margin: 0.2em; } -#emote-list-div{ - height: 30vh; -} - #emote-list{ display: grid; grid-template-columns: repeat(auto-fit, minmax(10em, 1fr)); padding: 1em; overflow-y: auto; justify-items: center; + max-height: 19.8em; } div.emote-list-emote{ diff --git a/www/css/channel.css b/www/css/channel.css index 5ccbffb..d845251 100644 --- a/www/css/channel.css +++ b/www/css/channel.css @@ -269,11 +269,12 @@ span.user-entry{ .toke{ text-align: center; margin: 0.2em auto; - font-size: 9pt; + font-size: 9.5pt; } -.tokewhisper{ +.serverwhisper{ font-size: 9pt; + font-style: italic; } .chat-img, .chat-video{ diff --git a/www/css/theme/movie-night.css b/www/css/theme/movie-night.css index 3642b77..7de2eca 100644 --- a/www/css/theme/movie-night.css +++ b/www/css/theme/movie-night.css @@ -26,8 +26,9 @@ along with this program. If not, see .*/ --bg2-alt0: rgb(200, 200, 200); --bg2-alt1: rgb(180, 180, 180); - --accent0: rgb(48, 47, 47); + --accent0: rgb(47, 47, 47); --accent0-alt0: rgb(34, 34, 34); + --accent0-alt1: rgb(70, 70, 70); --accent1: rgb(245, 245, 245); --accent1-alt0: rgb(185, 185, 185); --accent2: var(--accent0-alt0); @@ -240,6 +241,10 @@ p.channel-guide-entry-item{ border-bottom: 1px solid var(--bg2-alt1); } +.serverwhisper{ + color: var(--accent0-alt1) +} + .userlist-color0{/*green0*/ color: var(--userlist-color0); text-shadow: none; diff --git a/www/js/channel/chat.js b/www/js/channel/chat.js index fc1fb10..df8e38d 100644 --- a/www/js/channel/chat.js +++ b/www/js/channel/chat.js @@ -27,7 +27,7 @@ class chatBox{ //Preprocessor objects this.commandPreprocessor = new commandPreprocessor(client); - this.chatPostprocessor = new chatPostprocessor(); + this.chatPostprocessor = new chatPostprocessor(client); //Element Nodes this.chatPanel = document.querySelector("#chat-panel-div"); diff --git a/www/js/channel/chatPostprocessor.js b/www/js/channel/chatPostprocessor.js index bafec34..8000da7 100644 --- a/www/js/channel/chatPostprocessor.js +++ b/www/js/channel/chatPostprocessor.js @@ -1,5 +1,6 @@ class chatPostprocessor{ - constructor(){ + constructor(client){ + this.client = client; } preprocess(chatEntry, rawData){ @@ -91,6 +92,15 @@ class chatPostprocessor{ img.classList.add('chat-img'); img.src = wordObj.link; + //Look for an emote by link since emotes are tx'd as bare links + const emote = this.client.chatBox.commandPreprocessor.getEmoteByLink(wordObj.link); + + //If this is a known emote + if(emote != null){ + //Set the hover text to the emote's name + img.title = `[${emote.name}]`; + } + //Append node to chatBody injectNode(wordObj, img); }else if(wordObj.type == 'video'){ @@ -103,6 +113,15 @@ class chatPostprocessor{ vid.loop = true; vid.muted = true; + //Look for an emote by link since emotes are tx'd as bare links + const emote = this.client.chatBox.commandPreprocessor.getEmoteByLink(wordObj.link); + + //If this is a known emote + if(emote != null){ + //Set the hover text to the emote's name + vid.title = `[${emote.name}]`; + } + injectNode(wordObj, vid); } }); @@ -216,7 +235,7 @@ class chatPostprocessor{ userNode.classList.add('announcement-title'); this.chatBody.classList.add('announcement-body'); this.chatEntry.classList.add('announcement'); - }else if(this.rawData.type == "toke" || this.rawData.type == "tokewhisper"){ + }else if(this.rawData.type == "toke"){ //Squash the high-level this.chatEntry.querySelector('.high-level').remove(); @@ -224,7 +243,16 @@ class chatPostprocessor{ this.chatEntry.querySelector('.chat-entry-username').remove(); //Add toke/tokewhisper class - this.chatBody.classList.add(this.rawData.type); + this.chatBody.classList.add("toke"); + }else if(this.rawData.type == "tokewhisper"){ + //Squash the high-level + this.chatEntry.querySelector('.high-level').remove(); + + //remove the username + this.chatEntry.querySelector('.chat-entry-username').remove(); + + //Add toke/tokewhisper class + this.chatBody.classList.add("tokewhisper","serverwhisper"); } } } \ No newline at end of file diff --git a/www/js/channel/commandPreprocessor.js b/www/js/channel/commandPreprocessor.js index 34e2410..b5e1ec9 100644 --- a/www/js/channel/commandPreprocessor.js +++ b/www/js/channel/commandPreprocessor.js @@ -2,6 +2,19 @@ class commandPreprocessor{ constructor(client){ this.client = client; this.commandProcessor = new commandProcessor(client); + this.emotes = { + site: [], + chan: [], + personal: [] + } + + //define listeners + this.defineListeners(); + } + + defineListeners(){ + //When we receive site-wide emote list + this.client.socket.on("siteEmotes", this.setSiteEmotes.bind(this)); } preprocess(command){ @@ -13,6 +26,7 @@ class commandPreprocessor{ if(this.sendFlag){ this.message = command; + this.processEmotes(); this.processLinks(); this.sendRemoteCommand(); } @@ -38,6 +52,14 @@ 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); + }); + } + processLinks(){ //Strip out file seperators in-case the user is being a smart-ass this.message = this.message.replaceAll('␜',''); @@ -66,6 +88,25 @@ class commandPreprocessor{ this.client.socket.emit("chatMessage",{msg: this.message, links: this.links}); } + setSiteEmotes(data){ + this.emotes.site = data; + } + + getEmoteByLink(link){ + //Create an empty variable to hold the found emote + var foundEmote = null; + + //For every site-wide emote + this.emotes.site.forEach((emote) => { + //if we found a match + if(emote.link == link){ + //return the match + foundEmote = emote; + } + }); + + return foundEmote; + } }