diff --git a/src/app/chatMetadata.js b/src/app/chatMetadata.js index 92fc1ce..c1a1d6a 100644 --- a/src/app/chatMetadata.js +++ b/src/app/chatMetadata.js @@ -26,8 +26,9 @@ class chatMetadata{ * @param {String} msg - Contents of the message, with links replaced with numbered file-seperator markers * @param {String} type - Message Type Identifier, used for client-side processing. * @param {Array} links - Array of URLs/Links included in the message. + * @param {Number} timestamp - UTC Epoch from when message was sent */ - constructor(user, flair, highLevel, msg, type, links){ + constructor(user, flair, highLevel, msg, type, links, timestamp = Date.now()){ /** * Name of user who sent the message */ @@ -57,6 +58,11 @@ class chatMetadata{ * Array of URLs/Links included in the message. */ this.links = links; + + /** + * Datetime message was sent + */ + this.timestamp = timestamp; } } diff --git a/src/schemas/channel/chatSchema.js b/src/schemas/channel/chatSchema.js index abeb9ab..641cd32 100644 --- a/src/schemas/channel/chatSchema.js +++ b/src/schemas/channel/chatSchema.js @@ -51,6 +51,10 @@ const chatSchema = new mongoose.Schema({ type: [linkSchema], required: true, }, + timestamp: { + type: mongoose.SchemaTypes.Number, + default: Date.now() + } }); module.exports = chatSchema; \ No newline at end of file diff --git a/src/views/partial/panels/settings.ejs b/src/views/partial/panels/settings.ejs index b0c9e9b..a467288 100644 --- a/src/views/partial/panels/settings.ejs +++ b/src/views/partial/panels/settings.ejs @@ -16,7 +16,7 @@ along with this program. If not, see . %>

Client Settings

-

Playeback Settings

+

Playback Settings

Youtube Player Type:

+ +

Chat nameplate:

+ +

Aspect Ratio Lock Limiter:

diff --git a/www/css/channel.css b/www/css/channel.css index 728201b..bd96277 100644 --- a/www/css/channel.css +++ b/www/css/channel.css @@ -13,6 +13,10 @@ 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 .*/ +:root{ + --chat-entry-flex-direction: row; +} + body{ height: 100%; } @@ -169,11 +173,23 @@ p.panel-head-element{ display: flex; align-content: center; font-size: 10pt; + flex-direction: var(--chat-entry-flex-direction); +} + +.chat-entry-nameplate, .pm-panel-sesh-entry-nameplate{ + display: flex; } .chat-entry-username, .pm-panel-sesh-entry-username{ margin: auto 0.2em auto 0; text-wrap: nowrap; + width: 100%; +} + +.chat-entry-timestamp, .pm-panel-sesh-entry-timestamp{ + /* Yeah, yeah, I know, it's gross to use float over flex XP*/ + float: right; + font-size: 8pt; } .chat-entry-body, .pm-panel-sesh-entry-body{ diff --git a/www/css/panel/settings.css b/www/css/panel/settings.css index 2b4f64a..0acb581 100644 --- a/www/css/panel/settings.css +++ b/www/css/panel/settings.css @@ -44,7 +44,6 @@ along with this program. If not, see .*/ .settings-panel-setting input[type=checkbox]{ flex: 0 0 auto; width: auto; - margin: auto; } .settings-panel-setting input[type=number]{ diff --git a/www/css/theme/movie-night.css b/www/css/theme/movie-night.css index 78c6cbb..5e4d372 100644 --- a/www/css/theme/movie-night.css +++ b/www/css/theme/movie-night.css @@ -542,6 +542,10 @@ select.panel-head-element{ text-decoration-color: var(--danger0-alt0); } +.chat-entry-timestamp, .pm-panel-sesh-entry-timestamp{ + color: var(--accent0-alt1); +} + /* popup */ .popup-backer{ diff --git a/www/css/theme/the-network-classic.css b/www/css/theme/the-network-classic.css index 6591897..5d231a9 100644 --- a/www/css/theme/the-network-classic.css +++ b/www/css/theme/the-network-classic.css @@ -452,6 +452,10 @@ p.channel-guide-entry-item{ background-color: var(--bg1-alt0); } +.chat-entry-timestamp, .pm-panel-sesh-entry-timestamp{ + color: var(--accent0-alt0); +} + .serverwhisper{ color: var(--accent0-alt1) } diff --git a/www/js/channel/channel.js b/www/js/channel/channel.js index 13d2bda..010c343 100644 --- a/www/js/channel/channel.js +++ b/www/js/channel/channel.js @@ -327,6 +327,26 @@ class channel{ //Set theme ThemeManager.setTheme(value); //We're fuckin' done here + return; + case 'nameplateFormat': + switch(value){ + //If we're throwin' the nameplate to the left + case 'left': + console.log("FUCK") + document.body.style.setProperty("--chat-entry-flex-direction", "row"); + break; + //If we're throwin' er' up top, or someones bein' some sorta smartass + default: + document.body.style.setProperty("--chat-entry-flex-direction", "column"); + break; + } + + //If the chatbox is loaded in + if(this.chatBox != null){ + //Re render any existing chats + this.chatBox.rerenderBuffer(); + } + return; case 'chatWidthMin': //If the chat isn't loaded @@ -391,6 +411,7 @@ class channel{ ["liveSyncTolerance", 10], ["syncDelta", 6], ["theme", "The Network (Classic)"], + ["nameplateFormat", "left"], ["chatWidthMin", 20], ["disablePortrait", false], ["disableSendButton", false], diff --git a/www/js/channel/chat.js b/www/js/channel/chat.js index b297963..88b271b 100644 --- a/www/js/channel/chat.js +++ b/www/js/channel/chat.js @@ -680,4 +680,24 @@ class chatBox{ this.chatBuffer.scrollTop = this.chatBuffer.scrollHeight - Math.round(this.chatBuffer.getBoundingClientRect().height); } } + + /** + * Re-render buffer from raw data (useful for settings changes) + */ + rerenderBuffer(){ + //Yank existing messages from buffer, converting nodelist to array to avoid annoying behavior in for loops + const buffer = Array.from(this.chatBuffer.children); + + //Iterate through all received messages + for(const message of buffer){ + //Pull raw message data from saved message + const rawData = JSON.parse(message.dataset.rawData); + + //Nuke the message + message.remove(); + + //Re-render chat out to the buffer + this.displayChat(rawData); + } + } } \ No newline at end of file diff --git a/www/js/channel/chatPostprocessor.js b/www/js/channel/chatPostprocessor.js index 1ab660c..50c37eb 100644 --- a/www/js/channel/chatPostprocessor.js +++ b/www/js/channel/chatPostprocessor.js @@ -97,8 +97,9 @@ class chatPostprocessor{ const classSuffix = pm ? 'pm-panel-sesh' : 'chat'; const classSuffixAlt = pm ? classSuffix : 'chat-panel'; - //Create chat-entry span + //Create chat-entry span, and store raw message data this.chatEntry = document.createElement('span'); + this.chatEntry.dataset.rawData = JSON.stringify(this.rawData); //Add classes to classlist this.chatEntry.classList.add( @@ -108,11 +109,19 @@ class chatPostprocessor{ `${classSuffix}-${color}` ); + //Create Date object from message timestamp + let timestamp = new Date(this.rawData.timestamp); + + //Create nameplate span to hold username and high level + let nameplate = document.createElement('span'); + nameplate.classList.add(`${classSuffixAlt}-buffer`, `${classSuffix}-entry-nameplate`, ); + nameplate.title = timestamp.toLocaleString(); + //Create high-level label let highLevel = document.createElement('p'); highLevel.classList.add(`${classSuffixAlt}-buffer`,`${classSuffix}-entry-high-level`,"high-level"); highLevel.textContent = utils.unescapeEntities(`${this.rawData.highLevel}`); - this.chatEntry.appendChild(highLevel); + nameplate.appendChild(highLevel); //If we're not using classic flair if(this.rawData.flair != "classic"){ @@ -120,7 +129,7 @@ class chatPostprocessor{ flair = `flair-${this.rawData.flair}`; } - //Create username label + //Create username label to the nameplate let userLabel = document.createElement('p'); userLabel.classList.add(`${classSuffixAlt}-buffer`, `${classSuffix}-entry-username`, ); @@ -129,11 +138,25 @@ class chatPostprocessor{ flairSpan.classList.add(`${classSuffix}-entry-flair-span`, flair); flairSpan.innerHTML = this.rawData.user; - //Inject flair span into user label before the colon - userLabel.innerHTML = `${flairSpan.outerHTML}: `; + let colonSpan = document.createElement('span'); + colonSpan.classList.add(`${classSuffix}-entry-colon-span`); + if(localStorage.getItem('nameplateFormat') == "left"){ + colonSpan.innerHTML = ': '; + }else if(localStorage.getItem('nameplateFormat') == "timestamp"){ + colonSpan.classList.add(`${classSuffix}-entry-timestamp`); + colonSpan.innerHTML = ` (${timestamp.toLocaleString()})`; + } - //Append user label - this.chatEntry.appendChild(userLabel); + + //Append flair and colon(or timestamp) spans to userLabel + userLabel.appendChild(flairSpan); + userLabel.appendChild(colonSpan); + + //Append user label to the nameplate + nameplate.appendChild(userLabel); + + //Append nameplate to the chat entry + this.chatEntry.appendChild(nameplate); //Create chat body this.chatBody = document.createElement('p'); diff --git a/www/js/channel/panels/settingsPanel.js b/www/js/channel/panels/settingsPanel.js index 62daf0e..f244114 100644 --- a/www/js/channel/panels/settingsPanel.js +++ b/www/js/channel/panels/settingsPanel.js @@ -62,6 +62,11 @@ class settingsPanel extends panelObj{ */ this.theme = this.panelDocument.querySelector("#settings-panel-theme"); + /** + * Chat nameplate format + */ + this.nameplateFormat = this.panelDocument.querySelector("#settings-panel-nameplate-format"); + /** * Chat Width Minimum while Size-Locked to Media Aspect Ratio */ @@ -144,6 +149,7 @@ class settingsPanel extends panelObj{ this.liveSyncTolerance.addEventListener('change', this.updateLiveSyncTolerance.bind(this)); this.syncDelta.addEventListener('change', this.updateSyncDelta.bind(this)); this.theme.addEventListener('change', this.updateTheme.bind(this)); + this.nameplateFormat.addEventListener('change', this.updateNameplateFormat.bind(this)); this.chatWidthMinimum.addEventListener('change', this.updateChatWidthMinimum.bind(this)); this.disablePortrait.addEventListener('change', this.updateDisablePortrait.bind(this)); this.disableSendButton.addEventListener('change', this.updateDisableSendButton.bind(this)); @@ -163,6 +169,7 @@ class settingsPanel extends panelObj{ this.liveSyncTolerance.value = localStorage.getItem("liveSyncTolerance"); this.syncDelta.value = localStorage.getItem("syncDelta"); this.theme.querySelector('select').value = localStorage.getItem("theme"); + this.nameplateFormat.querySelector('select').value = localStorage.getItem("nameplateFormat"); this.chatWidthMinimum.value = localStorage.getItem("chatWidthMin"); this.disablePortrait.checked = localStorage.getItem("disablePortrait") == 'true'; this.disableSendButton.checked = localStorage.getItem("disableSendButton") == 'true'; @@ -261,6 +268,15 @@ class settingsPanel extends panelObj{ client.processConfig("theme", theme); } + /** + * Update nameplate format + */ + updateNameplateFormat(){ + let format = this.nameplateFormat.querySelector('select').value; + localStorage.setItem("nameplateFormat", format); + client.processConfig("nameplateFormat", format); + } + /** * Handles Chat Width minimum Changes */