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",