diff --git a/assets/chat/js/chat.js b/assets/chat/js/chat.js index 8d3bf22a..d01aa2f0 100644 --- a/assets/chat/js/chat.js +++ b/assets/chat/js/chat.js @@ -128,6 +128,7 @@ class Chat { this.source.on('DISPATCH', (data) => this.onDISPATCH(data)); this.source.on('CLOSE', (data) => this.onCLOSE(data)); this.source.on('NAMES', (data) => this.onNAMES(data)); + this.source.on('HISTORY', (data) => this.onHISTORY(data)); this.source.on('PIN', (data) => this.onPIN(data)); this.source.on('QUIT', (data) => this.onQUIT(data)); this.source.on('MSG', (data) => this.onMSG(data)); @@ -607,15 +608,6 @@ class Chat { .catch(() => {}); } - async loadHistory() { - return fetch(`${this.config.api.base}/api/chat/history`) - .then((res) => res.json()) - .then((json) => { - this.setHistory(json); - }) - .catch(() => {}); - } - async loadWhispers() { fetch(`${this.config.api.base}/api/messages/unread`, { credentials: 'include', @@ -651,17 +643,6 @@ class Chat { return this; } - setHistory(history) { - if (history && history.length > 0) { - this.backlogloading = true; - history.forEach((line) => this.source.parseAndDispatch({ data: line })); - this.backlogloading = false; - MessageBuilder.element('
').into(this); - this.mainwindow.update(true); - } - return this; - } - saveSettings() { if (this.authenticated) { if (this.settings.get('profilesettings')) { @@ -1129,6 +1110,15 @@ class Chat { } } + onHISTORY(messages) { + if (messages && messages.length > 0) { + this.backlogloading = true; + messages.forEach((data) => this.source.parseAndDispatch({ data })); + this.backlogloading = false; + this.mainwindow.update(true); + } + } + onPIN(msg) { if (this.pinnedMessage?.uuid === msg.uuid) return; this.pinnedMessage?.unpin(); diff --git a/assets/chat/js/messages/ChatMessage.js b/assets/chat/js/messages/ChatMessage.js index da62f3b0..0e63a18c 100644 --- a/assets/chat/js/messages/ChatMessage.js +++ b/assets/chat/js/messages/ChatMessage.js @@ -1,4 +1,5 @@ import moment from 'moment'; +import md5 from 'md5'; import ChatUIMessage from './ChatUIMessage'; import MessageTypes from './MessageTypes'; import { @@ -41,6 +42,9 @@ export default class ChatMessage extends ChatUIMessage { this.ignored = false; this.censorType = null; this.watching = null; + this.md5 = md5( + `${this.timestamp.valueOf()}${this.user?.id ?? ''}${this.message}`, + ); } html(chat = null) { diff --git a/assets/chat/js/window.js b/assets/chat/js/window.js index 30703321..b1b5a1ff 100644 --- a/assets/chat/js/window.js +++ b/assets/chat/js/window.js @@ -75,15 +75,45 @@ class ChatWindow extends EventEmitter { } addMessage(chat, message) { + // Return if message is already in chat. + if (this.containsMessage(message)) return; + message.ui = message.html(chat); message.afterRender(chat); - this.messages.push(message); - this.lastmessage = message; - this.lines.append(message.ui); + + // Get index of where the message should be based on timestamp. + const index = this.messages.findLastIndex( + (m) => m.timestamp.valueOf() <= message.timestamp.valueOf(), + ); + + /** + * If message index is < 0 then add message to the top of chat. + * + * If message index + 1 >= the length of messages array, + * it is a new message so add to bottom of chat. + * + * Otherwise the message is inbetween so insert in correct place. + */ + if (index < 0) { + this.lines.prepend(message.ui); + this.messages.unshift(message); + } else if (index + 1 >= this.messages.length) { + this.lines.append(message.ui); + this.messages.push(message); + this.lastmessage = message; + } else { + this.lines.insertBefore(message.ui, this.messages[index + 1].ui); + this.messages.splice(index + 1, 0, message); + } + this.linecount += 1; this.cleanupThrottle(); } + containsMessage(message) { + return this.messages.find((msg) => msg.md5 === message.md5); + } + getlines(sel) { return this.lines.querySelectorAll(sel); } diff --git a/assets/demo.js b/assets/demo.js index 5c81dd7c..7ece78e9 100644 --- a/assets/demo.js +++ b/assets/demo.js @@ -29,7 +29,6 @@ switch ((chat.reqParam('t') || 'embed').toUpperCase()) { chat.applySettings(false); }) .then(() => chat.loadEmotesAndFlairs()) - .then(() => chat.loadHistory()) .then(() => chat.connect()); break; @@ -52,7 +51,6 @@ switch ((chat.reqParam('t') || 'embed').toUpperCase()) { chat .withGui(embedHtml) .then(() => chat.loadEmotesAndFlairs()) - .then(() => chat.loadHistory()) .then(() => chat.connect()); break; } diff --git a/package-lock.json b/package-lock.json index 01316b99..a668a2a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "dotenv": "^16.0.3", "jquery": "^3.6.0", + "md5": "^2.3.0", "moment": "~2.30.1", "normalize.css": "~8.0.1", "overlayscrollbars": "^2.0.3", @@ -4697,6 +4698,14 @@ "node": ">=10" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -5313,6 +5322,14 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/css-loader": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", @@ -8229,8 +8246,7 @@ "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "node_modules/is-callable": { "version": "1.2.7", @@ -11202,6 +11218,16 @@ "tmpl": "1.0.5" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -18453,6 +18479,11 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" + }, "chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -18920,6 +18951,11 @@ "which": "^2.0.1" } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==" + }, "css-loader": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", @@ -21018,8 +21054,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { "version": "1.2.7", @@ -23154,6 +23189,16 @@ "tmpl": "1.0.5" } }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", diff --git a/package.json b/package.json index 575d58fa..b4302cdb 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "dotenv": "^16.0.3", "jquery": "^3.6.0", + "md5": "^2.3.0", "moment": "~2.30.1", "normalize.css": "~8.0.1", "overlayscrollbars": "^2.0.3",