diff --git a/assets/chat/css/style.scss b/assets/chat/css/style.scss index 00834d9c..6cc58035 100644 --- a/assets/chat/css/style.scss +++ b/assets/chat/css/style.scss @@ -1,6 +1,8 @@ @import '../../common'; @import '../../icons/icons'; @import '~normalize.css'; +@import '~tippy.js/dist/tippy.css'; +@import '~tippy.js/dist/svg-arrow.css'; @import url('https://fonts.googleapis.com/css?family=Roboto:400,500,600,700'); *, @@ -200,6 +202,11 @@ hr { center; background-size: contain; } + #chat-watching-focus-btn .btn-icon { + background: transparent url('../img/icon-watching.svg') no-repeat center + center; + background-size: contain; + } #chat-settings-btn .btn-icon { background: transparent url('../img/icon-settings.svg') no-repeat center center; @@ -836,8 +843,20 @@ hr { } } +.watching-focus { + .msg-emote, + .msg-user { + opacity: 0.3; + + &.watching-same, + &.msg-highlight { + opacity: 1; + } + } +} + /* Focus or highlight a line */ -.focus .msg-chat { +.focus:not(.watching-focus) .msg-chat { opacity: 0.3; &.msg-pinned { @@ -2162,3 +2181,13 @@ body.pref-fontscale[data-fontscale='10'] { font-family: 'Among Us'; src: url('../../../fonts/AmongUs-Regular.ttf') format('truetype'); } + +.tippy-box[data-theme~='dgg'] { + text-align: center; + background-color: white; + color: #000; +} + +.tippy-box[data-theme~='dgg'] > .tippy-svg-arrow { + fill: white; +} diff --git a/assets/chat/img/icon-watching.svg b/assets/chat/img/icon-watching.svg new file mode 100644 index 00000000..35acc10f --- /dev/null +++ b/assets/chat/img/icon-watching.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/chat/js/chat.js b/assets/chat/js/chat.js index 5c59477c..81a40d04 100644 --- a/assets/chat/js/chat.js +++ b/assets/chat/js/chat.js @@ -2,6 +2,7 @@ import { fetch } from 'whatwg-fetch'; import $ from 'jquery'; import { debounce } from 'throttle-debounce'; import moment from 'moment'; +import tippy, { roundArrow } from 'tippy.js'; import { KEYCODES, DATE_FORMATS, @@ -102,6 +103,7 @@ class Chat { this.regexhighlightnicks = null; this.regexhighlightself = null; this.replyusername = null; + this.watchingfocus = false; // An interface to tell the chat to do things via chat commands, or via emit // e.g. control.emit('CONNECT', 'ws://localhost:9001') is essentially chat.cmdCONNECT('ws://localhost:9001') @@ -265,6 +267,16 @@ class Chat { return link.sheet; })(); + // Tooltips + tippy('[data-tippy-content]', { + arrow: roundArrow, + delay: 0, + duration: 0, + maxWidth: 250, + hideOnClick: false, + theme: 'dgg', + }); + this.ishidden = (document.visibilityState || 'visible') !== 'visible'; this.output = this.ui.find('#chat-output-frame'); this.input = this.ui.find('#chat-input-control'); @@ -352,6 +364,12 @@ class Chat { } }); + // Watching focus + this.ui.on('click touch', '#chat-watching-focus-btn', () => { + this.watchingfocus = !this.watchingfocus; + this.ui.toggleClass('watching-focus', this.watchingfocus); + }); + // Chat focus / menu close when clicking on some areas let downinoutput = false; this.output.on('mousedown', () => { @@ -687,6 +705,12 @@ class Chat { ) { user.createdDate = data.createdDate; } + if ( + Object.hasOwn(data, 'watching') && + !user.equalWatching(data.watching) + ) { + user.watching = data.watching; + } } return user; } @@ -1057,10 +1081,21 @@ class Chat { ) { if (win.lastmessage.type === MessageTypes.EMOTE) { win.lastmessage.incEmoteCount(); + + if (this.user.equalWatching(usr.watching)) { + win.lastmessage.ui.classList.toggle('watching-same', true); + } + this.mainwindow.update(); } else { win.removeLastMessage(); - MessageBuilder.emote(textonly, data.timestamp, 2).into(this); + const msg = MessageBuilder.emote(textonly, data.timestamp, 2).into( + this, + ); + + if (this.user.equalWatching(usr.watching)) { + msg.ui.classList.add('watching-same'); + } } } else if (!this.resolveMessage(data.nick, data.data)) { MessageBuilder.message(data.data, usr, data.timestamp).into(this); @@ -1394,6 +1429,9 @@ class Chat { onUPDATEUSER(data) { if (this.user?.id === data.id) { this.setUser(data); + for (const window of this.windows.values()) { + window.updateMessages(this); + } } } diff --git a/assets/chat/js/messages/ChatMessage.js b/assets/chat/js/messages/ChatMessage.js index f5a2d5ff..e277b6ca 100644 --- a/assets/chat/js/messages/ChatMessage.js +++ b/assets/chat/js/messages/ChatMessage.js @@ -40,6 +40,7 @@ export default class ChatMessage extends ChatUIMessage { this.unformatted = unformatted; this.ignored = false; this.censorType = null; + this.watching = null; } html(chat = null) { @@ -146,4 +147,11 @@ export default class ChatMessage extends ChatUIMessage { this.ui.classList.toggle('msg-own', isOwn); this.isown = isOwn; } + + setWatching(user) { + this.ui.classList.toggle( + 'watching-same', + user.equalWatching(this.watching), + ); + } } diff --git a/assets/chat/js/messages/ChatUserMessage.js b/assets/chat/js/messages/ChatUserMessage.js index 2cf48031..0beb5297 100644 --- a/assets/chat/js/messages/ChatUserMessage.js +++ b/assets/chat/js/messages/ChatUserMessage.js @@ -37,6 +37,13 @@ export default class ChatUserMessage extends ChatMessage { if (this.user && this.user.username) { classes.push(...this.user.features); attr['data-username'] = this.user.username; + + if (this.user.watching) { + this.watching = this.user.watching; + if (chat.user.equalWatching(this.user.watching)) { + classes.push('watching-same'); + } + } } if (this.mentioned && this.mentioned.length > 0) attr['data-mentioned'] = this.mentioned.join(' ').toLowerCase(); diff --git a/assets/chat/js/user.js b/assets/chat/js/user.js index f8c069f3..fe6987b5 100644 --- a/assets/chat/js/user.js +++ b/assets/chat/js/user.js @@ -39,6 +39,12 @@ class ChatUser { */ features = []; + /** + * User's watching embed. + * @type {?Object} + */ + watching = null; + /** * @param {string|User} user */ @@ -52,6 +58,7 @@ class ChatUser { this.username = this.displayName.toLowerCase(); this.createdDate = user.createdDate || ''; this.features = user.features || []; + this.watching = user.watching || null; } } @@ -111,6 +118,13 @@ class ChatUser { } return 0; } + + equalWatching(embed) { + return ( + this.watching?.platform === embed?.platform && + this.watching?.id === embed?.id + ); + } } export default ChatUser; diff --git a/assets/chat/js/window.js b/assets/chat/js/window.js index be45b57a..4b1f7da9 100644 --- a/assets/chat/js/window.js +++ b/assets/chat/js/window.js @@ -135,6 +135,7 @@ class ChatWindow extends EventEmitter { message.highlight(chat.shouldHighlightMessage(message)); message.setTag(chat.taggednicks.get(username)); message.setTagTitle(chat.taggednotes.get(username)); + message.setWatching(chat.user); if (message.moderated) { message.censor(parseInt(chat.settings.get('showremoved') || '1', 10)); diff --git a/assets/views/embed.html b/assets/views/embed.html index 2d95389b..20f96e01 100644 --- a/assets/views/embed.html +++ b/assets/views/embed.html @@ -51,6 +51,13 @@
+ + + diff --git a/package-lock.json b/package-lock.json index 55f7f638..5805adc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,10 +6,9 @@ "packages": { "": { "name": "dgg-chat-gui", - "version": "2.37.0", + "version": "2.38.1", "license": "SEE LICENSE IN ", "dependencies": { - "bufferutil": "^4.0.3", "dotenv": "^16.0.3", "jquery": "^3.6.0", "moment": "~2.29.1", @@ -17,6 +16,7 @@ "overlayscrollbars": "^2.0.3", "sass-loader": "^13.2.0", "throttle-debounce": "~5.0.0", + "tippy.js": "^6.3.7", "whatwg-fetch": "^3.6.2" }, "devDependencies": { @@ -2748,6 +2748,15 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -13140,6 +13149,14 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -16167,6 +16184,11 @@ "fastq": "^1.6.0" } }, + "@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -23906,6 +23928,14 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "requires": { + "@popperjs/core": "^2.9.0" + } + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/package.json b/package.json index ccac2e2b..e96cc93a 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "overlayscrollbars": "^2.0.3", "sass-loader": "^13.2.0", "throttle-debounce": "~5.0.0", + "tippy.js": "^6.3.7", "whatwg-fetch": "^3.6.2" }, "devDependencies": { @@ -87,4 +88,4 @@ "prettier -cwu --loglevel silent ." ] } -} +} \ No newline at end of file