git » sdk » commit 0fee5af

Sync MAM into persistence

author Stephen Paul Weber
2023-09-13 00:50:37 UTC
committer Stephen Paul Weber
2023-09-13 00:50:37 UTC
parent 794ec7e6b045da357bdf3859c49f0a523e6d91d3

Sync MAM into persistence

Makefile +1 -0
xmpp/Client.hx +22 -1
xmpp/MessageSync.hx +1 -0
xmpp/Persistence.hx +8 -0
xmpp/persistence/browser.js +57 -0
xmpp/queries/MAMQuery.hx +3 -6

diff --git a/Makefile b/Makefile
index c0f8e56..bcc7d8e 100644
--- a/Makefile
+++ b/Makefile
@@ -14,4 +14,5 @@ browser.js:
 	haxe browser.hxml
 	echo "var exports = {};" > browser.js
 	sed -e 's/hxEnums\["xmpp.EventResult"\] = {/hxEnums["xmpp.EventResult"] = $$hx_exports.xmpp.EventResult = {/'< browser.haxe.js >> browser.js
+	cat xmpp/persistence/*.js >> browser.js
 	echo "export const { xmpp } = exports;" >> browser.js
diff --git a/xmpp/Client.hx b/xmpp/Client.hx
index 7ea1947..3223345 100644
--- a/xmpp/Client.hx
+++ b/xmpp/Client.hx
@@ -14,10 +14,12 @@ class Client extends xmpp.EventEmitter {
 	private var chatMessageHandlers: Array<(ChatMessage)->Void> = [];
 	public var jid(default,null):String;
 	private var chats: ChatList = [];
+	private var persistence: Persistence;
 
-	public function new(jid: String) {
+	public function new(jid: String, persistence: Persistence) {
 		super();
 		this.jid = jid;
+		this.persistence = persistence;
 		stream = new Stream();
 		stream.on("status/online", this.onConnected);
 		stream.on("auth/password-needed", (data)->this.trigger("auth/password-needed", { jid: this.jid }));
@@ -48,6 +50,7 @@ class Client extends xmpp.EventEmitter {
 
 		stream.sendStanza(new Stanza("presence")); // Set self to online
 		rosterGet();
+		sync();
 		return this.trigger("status/online", {});
 	}
 
@@ -100,4 +103,22 @@ class Client extends xmpp.EventEmitter {
 		});
 		sendQuery(rosterGet);
 	}
+
+	private function sync() {
+		persistence.lastId(jid, null, function(lastId) {
+			var sync = new MessageSync(
+				this,
+				stream,
+				lastId == null ? {} : { page: { after: lastId } }
+			);
+			sync.setNewestPageFirst(false);
+			sync.onMessages((messageList) -> {
+				for (message in messageList.messages) {
+					persistence.storeMessage(jid, message);
+				}
+				if (sync.hasMore()) sync.fetchNext();
+			});
+			sync.fetchNext();
+		});
+	}
 }
diff --git a/xmpp/MessageSync.hx b/xmpp/MessageSync.hx
index c60408e..b1ec461 100644
--- a/xmpp/MessageSync.hx
+++ b/xmpp/MessageSync.hx
@@ -88,6 +88,7 @@ class MessageSync {
 			var result = query.getResult();
 			if(result != null) {
 				complete = result.complete;
+				lastPage = result.page;
 			}
 			handler({
 				sync: this,
diff --git a/xmpp/Persistence.hx b/xmpp/Persistence.hx
new file mode 100644
index 0000000..d344fb9
--- /dev/null
+++ b/xmpp/Persistence.hx
@@ -0,0 +1,8 @@
+package xmpp;
+
+import xmpp.ChatMessage;
+
+abstract class Persistence {
+	abstract public function lastId(accountId: String, chatId: Null<String>, callback:(serverId:Null<String>)->Void):Void;
+	abstract public function storeMessage(accountId: String, message: ChatMessage):Void;
+}
diff --git a/xmpp/persistence/browser.js b/xmpp/persistence/browser.js
new file mode 100644
index 0000000..594bdd4
--- /dev/null
+++ b/xmpp/persistence/browser.js
@@ -0,0 +1,57 @@
+// This example persistence driver is written in JavaScript
+// so that SDK users can easily see how to write their own
+
+exports.xmpp.persistence = {
+  browser: (dbname) => {
+    var db = null;
+    var dbOpenReq = indexedDB.open(dbname, 1);
+    dbOpenReq.onerror = console.error;
+    dbOpenReq.onupgradeneeded = (event) => {
+      const upgradeDb = event.target.result;
+      const store = upgradeDb.createObjectStore("messages", { keyPath: "serverId" });
+      store.createIndex("account", ["timestamp", "account"]);
+      store.createIndex("conversation", ["timestamp", "account", "conversation"]);
+    };
+    dbOpenReq.onsuccess = (event) => {
+      db = event.target.result;
+    };
+
+    return {
+	   lastId: function(account, jid, callback) {
+		  const tx = db.transaction(["messages"], "readonly");
+		  const store = tx.objectStore("messages");
+		  var cursor = null;
+		  if (jid === null) {
+		    cursor = store.index("account").openCursor(
+			   IDBKeyRange.bound([new Date(0), account], [new Date("9999-01-01"), account]),
+			   "prev"
+		    );
+		  } else {
+		    cursor = store.index("conversation").openCursor(
+			   IDBKeyRange.bound([new Date(0), account, jid], [new Date("9999-01-01"), account, jid]),
+			   "prev"
+		    );
+		  }
+		  cursor.onsuccess = (event) => {
+		    callback(event.target.result ? event.target.result.value.serverId : null);
+		  }
+		  cursor.onerror = (event) => {
+		    console.error(event);
+		    callback(null);
+		  }
+	   },
+
+	   storeMessage: function(account, message) {
+		  const tx = db.transaction(["messages"], "readwrite");
+		  const store = tx.objectStore("messages");
+		  store.put({
+		    ...message,
+		    account: account,
+		    conversation: message.conversation(),
+		    timestamp: new Date(message.timestamp),
+		    direction: message.direction.toString()
+		  });
+      }
+	 }
+  }
+};
diff --git a/xmpp/queries/MAMQuery.hx b/xmpp/queries/MAMQuery.hx
index 5d83b14..0ea1bd6 100644
--- a/xmpp/queries/MAMQuery.hx
+++ b/xmpp/queries/MAMQuery.hx
@@ -25,10 +25,7 @@ typedef MAMQueryParams = {
 
 typedef MAMQueryResult = {
 	var complete : Bool;
-	var page : {
-		var firstId : String;
-		var lastId : String;
-	};
+	var page : ResultSetPageResult;
 };
 
 class MAMQuery extends GenericQuery {
@@ -114,8 +111,8 @@ class MAMQuery extends GenericQuery {
 			result = {
 				complete: fin.attr.get("complete") == "true" || fin.attr.get("complete") == "1",
 				page: {
-					firstId: rsmInfo.getChildText("first"),
-					lastId: rsmInfo.getChildText("last"),
+					first: rsmInfo.getChildText("first"),
+					last: rsmInfo.getChildText("last"),
 				}
 			};
 		}