git » sdk » commit 1e44213

Allow sending message correction

author Stephen Paul Weber
2023-11-30 06:36:10 UTC
committer Stephen Paul Weber
2023-12-11 15:59:52 UTC
parent 0e0c9c02496bb6f988cb2e68d5c9aae8d4989c7b

Allow sending message correction

xmpp/Chat.hx +45 -4
xmpp/ChatMessage.hx +1 -0
xmpp/Client.hx +9 -13
xmpp/persistence/browser.js +2 -1

diff --git a/xmpp/Chat.hx b/xmpp/Chat.hx
index ffadede..10cc2da 100644
--- a/xmpp/Chat.hx
+++ b/xmpp/Chat.hx
@@ -46,6 +46,8 @@ abstract class Chat {
 
 	abstract public function prepareIncomingMessage(message:ChatMessage, stanza:Stanza):ChatMessage;
 
+	abstract public function correctMessage(localId:String, message:ChatMessage):Void;
+
 	abstract public function sendMessage(message:ChatMessage):Void;
 
 	abstract public function getMessages(beforeId:Null<String>, beforeTime:Null<String>, handler:(Array<ChatMessage>)->Void):Void;
@@ -276,14 +278,35 @@ class DirectChat extends Chat {
 		return message;
 	}
 
-	public function sendMessage(message:ChatMessage):Void {
-		client.chatActivity(this);
+	private function prepareOutgoingMessage(message:ChatMessage) {
 		message.timestamp = message.timestamp ?? Date.format(std.Date.now());
 		message.direction = MessageSent;
 		message.from = client.jid;
 		message.sender = message.from.asBare();
 		message.replyTo = [message.sender];
 		message.recipients = getParticipants().map((p) -> JID.parse(p));
+		return message;
+	}
+
+	public function correctMessage(localId:String, message:ChatMessage) {
+		message = prepareOutgoingMessage(message);
+		persistence.correctMessage(client.accountId(), localId, message, (corrected) -> {
+			message.versions = corrected.versions;
+			for (recipient in message.recipients) {
+				message.to = recipient;
+				client.sendStanza(message.asStanza());
+			}
+			if (localId == lastMessage?.localId) {
+				setLastMessage(corrected);
+				client.trigger("chats/update", [this]);
+			}
+			client.notifyMessageHandlers(corrected);
+		});
+	}
+
+	public function sendMessage(message:ChatMessage):Void {
+		client.chatActivity(this);
+		message = prepareOutgoingMessage(message);
 		persistence.storeMessage(client.accountId(), message);
 		for (recipient in message.recipients) {
 			message.to = recipient;
@@ -534,8 +557,7 @@ class Channel extends Chat {
 		return message;
 	}
 
-	public function sendMessage(message:ChatMessage):Void {
-		client.chatActivity(this);
+	private function prepareOutgoingMessage(message:ChatMessage) {
 		message.timestamp = message.timestamp ?? Date.format(std.Date.now());
 		message.direction = MessageSent;
 		message.from = client.jid;
@@ -543,6 +565,25 @@ class Channel extends Chat {
 		message.replyTo = [message.sender];
 		message.to = JID.parse(chatId);
 		message.recipients = [message.to];
+		return message;
+	}
+
+	public function correctMessage(localId:String, message:ChatMessage) {
+		message = prepareOutgoingMessage(message);
+		persistence.correctMessage(client.accountId(), localId, message, (corrected) -> {
+			message.versions = corrected.versions;
+			client.sendStanza(message.asStanza("groupchat"));
+			if (localId == lastMessage?.localId) {
+				setLastMessage(corrected);
+				client.trigger("chats/update", [this]);
+			}
+			client.notifyMessageHandlers(corrected);
+		});
+	}
+
+	public function sendMessage(message:ChatMessage):Void {
+		client.chatActivity(this);
+		message = prepareOutgoingMessage(message);
 		persistence.storeMessage(client.accountId(), message);
 		client.sendStanza(message.asStanza("groupchat"));
 		setLastMessage(message);
diff --git a/xmpp/ChatMessage.hx b/xmpp/ChatMessage.hx
index b38c847..3f2267f 100644
--- a/xmpp/ChatMessage.hx
+++ b/xmpp/ChatMessage.hx
@@ -276,6 +276,7 @@ class ChatMessage {
 		if (to != null) attrs.set("to", to.asString());
 		if (localId != null) attrs.set("id", localId);
 		var stanza = new Stanza("message", attrs);
+		if (versions.length > 0) stanza.tag("replace", { xmlns: "urn:xmpp:message-correct:0", id: versions[versions.length-1].localId }).up();
 		if (threadId != null) stanza.textTag("thread", threadId);
 		if (recipients.length > 1) {
 			final addresses = stanza.tag("addresses", { xmlns: "http://jabber.org/protocol/address" });
diff --git a/xmpp/Client.hx b/xmpp/Client.hx
index 4ed1fdd..1c0cf16 100644
--- a/xmpp/Client.hx
+++ b/xmpp/Client.hx
@@ -76,11 +76,7 @@ class Client extends xmpp.EventEmitter {
 				accountId(),
 				data.id,
 				MessageDeliveredToServer,
-				(chatMessage) -> {
-					for (handler in chatMessageHandlers) {
-						handler(chatMessage);
-					}
-				}
+				notifyMessageHandlers
 			);
 			return EventHandled;
 		});
@@ -90,11 +86,7 @@ class Client extends xmpp.EventEmitter {
 				accountId(),
 				data.id,
 				MessageFailedToSend,
-				(chatMessage) -> {
-					for (handler in chatMessageHandlers) {
-						handler(chatMessage);
-					}
-				}
+				notifyMessageHandlers
 			);
 			return EventHandled;
 		});
@@ -158,9 +150,7 @@ class Client extends xmpp.EventEmitter {
 							if (chatMessage.versions.length < 1) chat.setUnreadCount(chatMessage.isIncoming() ? chat.unreadCount() + 1 : 0);
 							chatActivity(chat);
 						}
-						for (handler in chatMessageHandlers) {
-							handler(chatMessage);
-						}
+						notifyMessageHandlers(chatMessage);
 					};
 					chatMessage = chat.prepareIncomingMessage(chatMessage, stanza);
 					final replace = stanza.getChild("replace", "urn:xmpp:message-correct:0");
@@ -732,6 +722,12 @@ class Client extends xmpp.EventEmitter {
 		sendQuery(itemsGet);
 	}
 
+	public function notifyMessageHandlers(message: ChatMessage) {
+		for (handler in chatMessageHandlers) {
+			handler(message);
+		}
+	}
+
 	private function rosterGet() {
 		var rosterGet = new RosterGet();
 		rosterGet.onFinished(() -> {
diff --git a/xmpp/persistence/browser.js b/xmpp/persistence/browser.js
index 755e334..4f6c922 100644
--- a/xmpp/persistence/browser.js
+++ b/xmpp/persistence/browser.js
@@ -273,11 +273,12 @@ exports.xmpp.persistence = {
 				const store = tx.objectStore("messages");
 				promisifyRequest(store.index("localId").openCursor(IDBKeyRange.only([account, localId, message.chatId()]))).then((result) => {
 					if (result?.value && result.value.sender == message.senderId()) {
-						// Note, this strategy loses the ids of the replacement messages
+						// NOTE: this strategy loses the ids and timestamp of the replacement messages
 						const withAnnotation = serializeMessage(account, message);
 						withAnnotation.serverIdBy = result.value.serverIdBy;
 						withAnnotation.serverId = result.value.serverId;
 						withAnnotation.localId = result.value.localId;
+						withAnnotation.timestamp = result.value.timestamp; // Edited version is not newer
 						withAnnotation.versions = [{ ...result.value, versions: [] }].concat(result.value.versions || [])
 						result.update(withAnnotation);
 						callback(hydrateMessage(withAnnotation));