| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-10-11 20:14:30 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-10-11 23:15:00 UTC |
| parent | e52cfd0a574d9e94b7040319b5fac4fed64b440f |
| xmpp/Chat.hx | +35 | -14 |
| xmpp/Client.hx | +18 | -6 |
| xmpp/Persistence.hx | +3 | -0 |
| xmpp/persistence/browser.js | +37 | -5 |
diff --git a/xmpp/Chat.hx b/xmpp/Chat.hx index 0eda2d2..9fd8a0d 100644 --- a/xmpp/Chat.hx +++ b/xmpp/Chat.hx @@ -12,24 +12,17 @@ import xmpp.jingle.Session; import xmpp.queries.MAMQuery; using Lambda; -enum ChatType { - ChatTypeDirect; - ChatTypeGroup; - ChatTypePublic; -} - abstract class Chat { private var client:Client; private var stream:GenericStream; private var persistence:Persistence; private var avatarSha1:Null<BytesData> = null; - private var caps:Map<String, Caps> = []; + private var caps:haxe.DynamicAccess<Caps> = {}; private var trusted:Bool = false; public var chatId(default, null):String; - public var type(default, null):Null<ChatType>; public var jingleSessions: Map<String, xmpp.jingle.Session> = []; - private function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, type:ChatType) { + private function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String) { this.client = client; this.stream = stream; this.persistence = persistence; @@ -44,10 +37,6 @@ abstract class Chat { abstract public function getParticipants():Array<String>; - public function isDirectChat():Bool { return type.match(ChatTypeDirect); }; - public function isGroupChat():Bool { return type.match(ChatTypeGroup); }; - public function isPublicChat():Bool { return type.match(ChatTypePublic); }; - public function setCaps(resource:String, caps:Caps) { this.caps.set(resource, caps); } @@ -143,10 +132,11 @@ abstract class Chat { } } +@:expose class DirectChat extends Chat { private var displayName:String; public function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String) { - super(client, stream, persistence, chatId, ChatTypeDirect); + super(client, stream, persistence, chatId); this.displayName = chatId; } @@ -222,3 +212,34 @@ class DirectChat extends Chat { } } } + +@:expose +class SerializedChat { + public final chatId:String; + public final trusted:Bool; + public final avatarSha1:Null<BytesData>; + public final caps:haxe.DynamicAccess<Caps>; + public final displayName:Null<String>; + public final klass:String; + + public function new(chatId: String, trusted: Bool, avatarSha1: Null<BytesData>, caps: haxe.DynamicAccess<Caps>, displayName: Null<String>, klass: String) { + this.chatId = chatId; + this.trusted = trusted; + this.avatarSha1 = avatarSha1; + this.caps = caps; + this.displayName = displayName; + this.klass = klass; + } + + public function toDirectChat(client: Client, stream: GenericStream, persistence: Persistence) { + if (klass != "DirectChat") throw "Not a direct chat: " + klass; + final chat = new DirectChat(client, stream, persistence, chatId); + if (displayName != null) chat.setDisplayName(displayName); + if (avatarSha1 != null) chat.setAvatarSha1(avatarSha1); + chat.setTrusted(trusted); + for (resource => c in caps) { + chat.setCaps(resource, c); + } + return chat; + } +} diff --git a/xmpp/Client.hx b/xmpp/Client.hx index 7a740d2..d0b90d6 100644 --- a/xmpp/Client.hx +++ b/xmpp/Client.hx @@ -54,13 +54,20 @@ class Client extends xmpp.EventEmitter { } public function start() { - persistence.getLogin(jid, (login) -> { - if (login.token == null) { - stream.on("auth/password-needed", (data)->this.trigger("auth/password-needed", { jid: this.jid })); - } else { - stream.on("auth/password-needed", (data)->this.stream.trigger("auth/password", { password: login.token })); + persistence.getChats(jid, (protoChats) -> { + for (protoChat in protoChats) { + chats.push(protoChat.toDirectChat(this, stream, persistence)); } - stream.connect(login.clientId == null ? jid : jid + "/" + login.clientId); + this.trigger("chats/update", chats); + + persistence.getLogin(jid, (login) -> { + if (login.token == null) { + stream.on("auth/password-needed", (data)->this.trigger("auth/password-needed", { jid: this.jid })); + } else { + stream.on("auth/password-needed", (data)->this.stream.trigger("auth/password", { password: login.token })); + } + stream.connect(login.clientId == null ? jid : jid + "/" + login.clientId); + }); }); } @@ -145,6 +152,7 @@ class Client extends xmpp.EventEmitter { } final chat = this.getDirectChat(JID.parse(pubsubEvent.getFrom()).asBare().asString(), false); chat.setAvatarSha1(avatarSha1); + persistence.storeChat(jid, chat); persistence.getMediaUri("sha-1", avatarSha1, (uri) -> { if (uri == null) { final pubsubGet = new PubsubGet(pubsubEvent.getFrom(), "urn:xmpp:avatar:data", avatarSha1Hex); @@ -267,11 +275,13 @@ class Client extends xmpp.EventEmitter { if (discoGet.getResult() != null) { persistence.storeCaps(discoGet.getResult()); chat.setCaps(JID.parse(stanza.attr.get("from")).resource, discoGet.getResult()); + persistence.storeChat(jid, chat); } }); sendQuery(discoGet); } else { chat.setCaps(JID.parse(stanza.attr.get("from")).resource, caps); + persistence.storeChat(jid, chat); } }); return EventHandled; @@ -313,6 +323,7 @@ class Client extends xmpp.EventEmitter { } } var chat = new DirectChat(this, this.stream, this.persistence, chatId); + persistence.storeChat(jid, chat); chats.unshift(chat); if (triggerIfNew) this.trigger("chats/update", [chat]); return chat; @@ -449,6 +460,7 @@ class Client extends xmpp.EventEmitter { var chat = getDirectChat(item.jid, false); chat.setTrusted(item.subscription == "both" || item.subscription == "from"); if (item.fn != null && item.fn != "") chat.setDisplayName(item.fn); + persistence.storeChat(jid, chat); } this.trigger("chats/update", chats); }); diff --git a/xmpp/Persistence.hx b/xmpp/Persistence.hx index 80bc318..3ef9b06 100644 --- a/xmpp/Persistence.hx +++ b/xmpp/Persistence.hx @@ -2,9 +2,12 @@ package xmpp; import haxe.io.BytesData; import xmpp.ChatMessage; +import xmpp.Chat; abstract class Persistence { abstract public function lastId(accountId: String, chatId: Null<String>, callback:(serverId:Null<String>)->Void):Void; + abstract public function storeChat(accountId: String, chat: Chat):Void; + abstract public function getChats(accountId: String, callback: (chats:Array<SerializedChat>)->Void):Void; abstract public function storeMessage(accountId: String, message: ChatMessage):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; diff --git a/xmpp/persistence/browser.js b/xmpp/persistence/browser.js index 5fa6b7b..bf81fbe 100644 --- a/xmpp/persistence/browser.js +++ b/xmpp/persistence/browser.js @@ -10,18 +10,21 @@ exports.xmpp.persistence = { dbOpenReq.onupgradeneeded = (event) => { const upgradeDb = event.target.result; if (!db.objectStoreNames.contains("messages")) { - const store = upgradeDb.createObjectStore("messages", { keyPath: "serverId" }); - store.createIndex("account", ["account", "timestamp"]); - store.createIndex("chats", ["account", "chatId", "timestamp"]); - store.createIndex("localId", ["account", "chatId", "localId"]); + const messages = upgradeDb.createObjectStore("messages", { keyPath: "serverId" }); + messages.createIndex("account", ["account", "timestamp"]); + messages.createIndex("chats", ["account", "chatId", "timestamp"]); + messages.createIndex("localId", ["account", "chatId", "localId"]); } if (!db.objectStoreNames.contains("keyvaluepairs")) { upgradeDb.createObjectStore("keyvaluepairs"); } + if (!db.objectStoreNames.contains("chats")) { + upgradeDb.createObjectStore("chats", { keyPath: ["account", "chatId"] }); + } }; dbOpenReq.onsuccess = (event) => { db = event.target.result; - if (!db.objectStoreNames.contains("messages") || !db.objectStoreNames.contains("keyvaluepairs")) { + if (!db.objectStoreNames.contains("messages") || !db.objectStoreNames.contains("keyvaluepairs") || !db.objectStoreNames.contains("chats")) { db.close(); openDb(db.version + 1); } @@ -69,6 +72,35 @@ exports.xmpp.persistence = { } }, + storeChat: function(account, chat) { + const tx = db.transaction(["chats"], "readwrite"); + const store = tx.objectStore("chats"); + + store.put({ + account: account, + chatId: chat.chatId, + trusted: chat.trusted, + avatarSha1: chat.avatarSha1, + caps: chat.caps, + displayName: chat.displayName, + class: chat instanceof xmpp.DirectChat ? "DirectChat" : "Chat" + }); + }, + + getChats: function(account, callback) { + const tx = db.transaction(["chats"], "readonly"); + const store = tx.objectStore("chats"); + const range = IDBKeyRange.bound([account], [account, []]); + promisifyRequest(store.getAll(range)).then((result) => callback(result.map((r) => new xmpp.SerializedChat( + r.chatId, + r.trusted, + r.avatarSha1, + r.caps, + r.displayName, + r.class + )))); + }, + storeMessage: function(account, message) { const tx = db.transaction(["messages"], "readwrite"); const store = tx.objectStore("messages");