| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-11-09 03:34:43 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-11-09 03:34:43 UTC |
| parent | 333b32d491c25365a7be41842c65b0f2faf263f7 |
| xmpp/ChatMessage.hx | +1 | -0 |
| xmpp/Client.hx | +16 | -6 |
| xmpp/Persistence.hx | +1 | -0 |
| xmpp/persistence/browser.js | +44 | -16 |
diff --git a/xmpp/ChatMessage.hx b/xmpp/ChatMessage.hx index 50c8dc3..3e1eeb9 100644 --- a/xmpp/ChatMessage.hx +++ b/xmpp/ChatMessage.hx @@ -39,6 +39,7 @@ class ChatMessage { public var lang (default, null): Null<String> = null; public var direction: MessageDirection = MessageReceived; + public var versions: Array<ChatMessage> = []; public function new() { } diff --git a/xmpp/Client.hx b/xmpp/Client.hx index f0d0eaa..f65729f 100644 --- a/xmpp/Client.hx +++ b/xmpp/Client.hx @@ -108,13 +108,23 @@ class Client extends xmpp.EventEmitter { var chat = getChat(chatMessage.chatId()); if (chat == null && stanza.attr.get("type") != "groupchat") chat = getDirectChat(chatMessage.chatId()); if (chat != null) { + final updateChat = (chatMessage) -> { + if (chatMessage.versions.length < 1 || chat.lastMessageId() == chatMessage.serverId || chat.lastMessageId() == chatMessage.localId) { + chat.setLastMessage(chatMessage); + if (chatMessage.versions.length < 1) chat.setUnreadCount(chatMessage.isIncoming() ? chat.unreadCount() + 1 : 0); + chatActivity(chat); + } + for (handler in chatMessageHandlers) { + handler(chatMessage); + } + }; chatMessage = chat.prepareIncomingMessage(chatMessage, stanza); - chat.setLastMessage(chatMessage); - chat.setUnreadCount(chatMessage.isIncoming() ? chat.unreadCount() + 1 : 0); - if (chatMessage.serverId != null) persistence.storeMessage(accountId(), chatMessage); - chatActivity(chat); - for (handler in chatMessageHandlers) { - handler(chatMessage); + final replace = stanza.getChild("replace", "urn:xmpp:message-correct:0"); + if (replace == null || replace.attr.get("id") == null) { + if (chatMessage.serverId != null) persistence.storeMessage(accountId(), chatMessage); + updateChat(chatMessage); + } else { + persistence.correctMessage(accountId(), replace.attr.get("id"), chatMessage, updateChat); } } } diff --git a/xmpp/Persistence.hx b/xmpp/Persistence.hx index a252aee..febda3e 100644 --- a/xmpp/Persistence.hx +++ b/xmpp/Persistence.hx @@ -10,6 +10,7 @@ abstract class Persistence { abstract public function getChats(accountId: String, callback: (chats:Array<SerializedChat>)->Void):Void; abstract public function getChatsUnreadDetails(accountId: String, chats: Array<Chat>, callback: (details:Array<{ chatId: String, message: ChatMessage, unreadCount: Int }>)->Void):Void; abstract public function storeMessage(accountId: String, message: ChatMessage):Void; + abstract public function correctMessage(accountId: String, localId: String, message: ChatMessage, callback: (ChatMessage)->Void):Void; abstract public function getMessages(accountId: String, chatId: String, beforeId: Null<String>, beforeTime: Null<String>, callback: (messages:Array<ChatMessage>)->Void):Void; abstract public function getMediaUri(hashAlgorithm:String, hash:BytesData, callback: (uri:Null<String>)->Void):Void; abstract public function storeMedia(mime:String, bytes:BytesData, callback: ()->Void):Void; diff --git a/xmpp/persistence/browser.js b/xmpp/persistence/browser.js index 1ba4e84..1094cfb 100644 --- a/xmpp/persistence/browser.js +++ b/xmpp/persistence/browser.js @@ -74,9 +74,30 @@ exports.xmpp.persistence = { message.text = value.text; message.lang = value.lang; message.direction = value.direction == "MessageReceived" ? xmpp.MessageDirection.MessageReceived : xmpp.MessageDirection.MessageSent; + message.versions = (value.versions || []).map(hydrateMessage); return message; } + function serializeMessage(account, message) { + return { + ...message, + serverId: message.serverId || "", + serverIdBy: message.serverIdBy || "", + localId: message.localId || "", + syncPoint: !!message.syncPoint, + account: account, + chatId: message.chatId(), + to: message.to?.asString(), + from: message.from?.asString(), + sender: message.sender?.asString(), + recipients: message.recipients.map((r) => r.asString()), + replyTo: message.replyTo.map((r) => r.asString()), + timestamp: new Date(message.timestamp), + direction: message.direction.toString(), + versions: message.versions.map((m) => serializeMessage(account, m)), + } + } + return { test: function() { //messages = upgradeDb.createObjectStore("messages", { keyPath: ["account", "serverIdBy", "serverId", "localId"] }); @@ -197,25 +218,32 @@ exports.xmpp.persistence = { promisifyRequest(store.index("localId").get([account, message.chatId(), message.localId || []])).then((result) => { if (result && !message.isIncoming() && result.direction === "MessageSent") return; // duplicate, we trust our own stanza ids - store.put({ - ...message, - serverId: message.serverId || "", - serverIdBy: message.serverIdBy || "", - localId: message.localId || "", - syncPoint: !!message.syncPoint, - account: account, - chatId: message.chatId(), - to: message.to?.asString(), - from: message.from?.asString(), - sender: message.sender?.asString(), - recipients: message.recipients.map((r) => r.asString()), - replyTo: message.replyTo.map((r) => r.asString()), - timestamp: new Date(message.timestamp), - direction: message.direction.toString() - }); + store.put(serializeMessage(account, message)); }); }, + correctMessage: function(account, localId, message, callback) { + const tx = db.transaction(["messages"], "readwrite"); + const store = tx.objectStore("messages"); + const cursor = store.index("localId").openCursor(IDBKeyRange.only([account, message.chatId(), localId])); + cursor.onsuccess = (event) => { + if (event.target.result?.value && event.target.result.value.sender == message.senderId()) { + // Note, this strategy loses the ids of the replacement messages + const withAnnotation = serializeMessage(account, message); + withAnnotation.serverIdBy = event.target.result.value.serverIdBy; + withAnnotation.serverId = event.target.result.value.serverId; + withAnnotation.localId = event.target.result.value.localId; + withAnnotation.versions = [{ ...event.target.result.value, versions: [] }].concat(event.target.result.value.versions || []) + event.target.result.update(withAnnotation); + callback(hydrateMessage(withAnnotation)); + } else { + this.storeMessage(account, message); + callback(message); + } + }; + cursor.onerror = console.error; + }, + getMessages: function(account, chatId, beforeId, beforeTime, callback) { const beforeDate = beforeTime ? new Date(beforeTime) : []; const tx = db.transaction(["messages"], "readonly");