| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-09-13 00:50:37 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-09-13 00:50:37 UTC |
| parent | 794ec7e6b045da357bdf3859c49f0a523e6d91d3 |
| 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"), } }; }