| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-03-16 02:55:17 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-03-16 13:49:15 UTC |
| parent | 00d93f96985cf05efd56ccad73275d5d84d28baf |
| borogove/Chat.hx | +19 | -7 |
| borogove/Persistence.hx | +3 | -3 |
| borogove/persistence/Dummy.hx | +3 | -3 |
| borogove/persistence/IDB.js | +10 | -21 |
| borogove/persistence/Sqlite.hx | +10 | -22 |
diff --git a/borogove/Chat.hx b/borogove/Chat.hx index f74e680..3c67453 100644 --- a/borogove/Chat.hx +++ b/borogove/Chat.hx @@ -867,7 +867,7 @@ abstract class Chat { } private function recomputeUnread(): Promise<Any> { - return persistence.getMessagesBefore(client.accountId(), chatId, null, null).then((messages) -> { + return persistence.getMessagesBefore(client.accountId(), chatId, null).then((messages) -> { var i = messages.length; while (--i >= 0) { if (messages[i].serverId == readUpToId || !messages[i].isIncoming()) break; @@ -965,7 +965,9 @@ class DirectChat extends Chat { @HaxeCBridge.noemit // on superclass as abstract public function getMessagesBefore(before: Null<ChatMessage>):Promise<Array<ChatMessage>> { - return persistence.getMessagesBefore(client.accountId(), chatId, before?.serverId ?? before?.localId, before?.timestamp).then((messages) -> + if (before.chatId() != chatId) throw "Cannot look before from a different chat"; + + return persistence.getMessagesBefore(client.accountId(), chatId, before).then((messages) -> if (messages.length > 0) { Promise.resolve(messages); } else { @@ -979,10 +981,12 @@ class DirectChat extends Chat { @HaxeCBridge.noemit // on superclass as abstract public function getMessagesAfter(after: Null<ChatMessage>):Promise<Array<ChatMessage>> { + if (after.chatId() != chatId) throw "Cannot look after from a different chat"; if (after != null && lastMessage != null && lastMessage.canReplace(after) && !syncing()) { return Promise.resolve([]); } - return persistence.getMessagesAfter(client.accountId(), chatId, after?.serverId ?? after?.localId, after?.timestamp).then((messages) -> + + return persistence.getMessagesAfter(client.accountId(), chatId, after).then((messages) -> if (messages.length > 0) { Promise.resolve(messages); } else { @@ -996,8 +1000,10 @@ class DirectChat extends Chat { @HaxeCBridge.noemit // on superclass as abstract public function getMessagesAround(around: ChatMessage):Promise<Array<ChatMessage>> { + if (around.chatId() != chatId) throw "Cannot look around from a different chat"; + // TODO: fetch more from MAM if nothing locally? - return persistence.getMessagesAround(client.accountId(), chatId, around.serverId ?? around.localId, around.timestamp); + return persistence.getMessagesAround(client.accountId(), around); } @:allow(borogove) @@ -1632,7 +1638,9 @@ trace("XYZZY no MUC avatar locally matching so fetch vcard", chatId, avatarSha1H @HaxeCBridge.noemit // on superclass as abstract public function getMessagesBefore(before: Null<ChatMessage>):Promise<Array<ChatMessage>> { - return persistence.getMessagesBefore(client.accountId(), chatId, before?.serverId ?? before?.localId, before?.timestamp).then((messages) -> + if (before.chatId() != chatId) throw "Cannot look before from a different chat"; + + return persistence.getMessagesBefore(client.accountId(), chatId, before).then((messages) -> if (messages.length > 0) { Promise.resolve(messages); } else { @@ -1651,10 +1659,12 @@ trace("XYZZY no MUC avatar locally matching so fetch vcard", chatId, avatarSha1H @HaxeCBridge.noemit // on superclass as abstract public function getMessagesAfter(after: Null<ChatMessage>):Promise<Array<ChatMessage>> { + if (after.chatId() != chatId) throw "Cannot look after from a different chat"; if (after != null && lastMessage != null && lastMessage.canReplace(after) && !syncing()) { return Promise.resolve([]); } - return persistence.getMessagesAfter(client.accountId(), chatId, after?.serverId ?? after?.localId, after?.timestamp).then((messages) -> + + return persistence.getMessagesAfter(client.accountId(), chatId, after).then((messages) -> if (messages.length > 0) { Promise.resolve(messages); } else { @@ -1673,8 +1683,10 @@ trace("XYZZY no MUC avatar locally matching so fetch vcard", chatId, avatarSha1H @HaxeCBridge.noemit // on superclass as abstract public function getMessagesAround(around: ChatMessage):Promise<Array<ChatMessage>> { + if (around.chatId() != chatId) throw "Cannot look around from a different chat"; + // TODO: fetch more from MAM if nothing locally - return persistence.getMessagesAround(client.accountId(), chatId, around.serverId ?? around.localId, around.timestamp); + return persistence.getMessagesAround(client.accountId(), around); } @:allow(borogove) diff --git a/borogove/Persistence.hx b/borogove/Persistence.hx index 1ab8fbf..177ee67 100644 --- a/borogove/Persistence.hx +++ b/borogove/Persistence.hx @@ -33,9 +33,9 @@ interface Persistence { public function updateMessage(accountId: String, message: ChatMessage):Void; public function updateMessageStatus(accountId: String, localId: String, status:borogove.Message.MessageStatus, statusText: Null<String>): Promise<ChatMessage>; public function getMessage(accountId: String, chatId: String, serverId: Null<String>, localId: Null<String>): Promise<Null<ChatMessage>>; - public function getMessagesBefore(accountId: String, chatId: String, beforeId: Null<String>, beforeTime: Null<String>): Promise<Array<ChatMessage>>; - public function getMessagesAfter(accountId: String, chatId: String, afterId: Null<String>, afterTime: Null<String>): Promise<Array<ChatMessage>>; - public function getMessagesAround(accountId: String, chatId: String, aroundId: Null<String>, aroundTime: Null<String>): Promise<Array<ChatMessage>>; + public function getMessagesBefore(accountId: String, chatId: String, before: Null<ChatMessage>): Promise<Array<ChatMessage>>; + public function getMessagesAfter(accountId: String, chatId: String, afterId: Null<ChatMessage>): Promise<Array<ChatMessage>>; + public function getMessagesAround(accountId: String, around: ChatMessage): Promise<Array<ChatMessage>>; public function hasMedia(hashAlgorithm:String, hash:BytesData): Promise<Bool>; public function storeMedia(mime:String, bytes:BytesData): Promise<Bool>; public function removeMedia(hashAlgorithm:String, hash:BytesData):Void; diff --git a/borogove/persistence/Dummy.hx b/borogove/persistence/Dummy.hx index 7451752..50261ab 100644 --- a/borogove/persistence/Dummy.hx +++ b/borogove/persistence/Dummy.hx @@ -57,17 +57,17 @@ class Dummy implements Persistence { } @HaxeCBridge.noemit - public function getMessagesBefore(accountId: String, chatId: String, beforeId: Null<String>, beforeTime: Null<String>): Promise<Array<ChatMessage>> { + public function getMessagesBefore(accountId: String, chatId: String, before: Null<ChatMessage>): Promise<Array<ChatMessage>> { return Promise.resolve([]); } @HaxeCBridge.noemit - public function getMessagesAfter(accountId: String, chatId: String, afterId: Null<String>, afterTime: Null<String>): Promise<Array<ChatMessage>> { + public function getMessagesAfter(accountId: String, chatId: String, after: Null<ChatMessage>): Promise<Array<ChatMessage>> { return Promise.resolve([]); } @HaxeCBridge.noemit - public function getMessagesAround(accountId: String, chatId: String, aroundId: Null<String>, aroundTime: Null<String>): Promise<Array<ChatMessage>> { + public function getMessagesAround(accountId: String, around: Null<ChatMessage>): Promise<Array<ChatMessage>> { return Promise.resolve([]); } diff --git a/borogove/persistence/IDB.js b/borogove/persistence/IDB.js index 8abd974..c48c1df 100644 --- a/borogove/persistence/IDB.js +++ b/borogove/persistence/IDB.js @@ -531,47 +531,36 @@ export default async (dbname, media, tokenize, stemmer) => { throw "Message not found: " + localId; }, - getMessagesBefore: async function(account, chatId, beforeId, beforeTime) { - // TODO: if beforeId is present but beforeTime is null, lookup time - const bound = beforeTime ? new Date(beforeTime) : []; + getMessagesBefore: async function(account, chatId, before) { + const bound = before ? new Date(before.timestamp) : []; const tx = db.transaction(["messages"], "readonly"); const store = tx.objectStore("messages"); const cursor = store.index("chats").openCursor( IDBKeyRange.bound([account, chatId], [account, chatId, bound]), "prev" ); - const messages = await this.getMessagesFromCursor(cursor, beforeId, bound); + const messages = await this.getMessagesFromCursor(cursor, before?.serverId || before?.localId, bound); return messages.reverse(); }, - getMessagesAfter: async function(account, chatId, afterId, afterTime) { - // TODO: if afterId is present but afterTime is null, lookup time - const bound = afterTime ? [new Date(afterTime)] : []; + getMessagesAfter: async function(account, chatId, after) { + const bound = after ? [new Date(after.timestamp)] : []; const tx = db.transaction(["messages"], "readonly"); const store = tx.objectStore("messages"); const cursor = store.index("chats").openCursor( IDBKeyRange.bound([account, chatId, ...bound], [account, chatId, []]), "next" ); - return this.getMessagesFromCursor(cursor, afterId, bound[0]); + return this.getMessagesFromCursor(cursor, after?.serverId || after?.localId, bound[0]); }, - getMessagesAround: async function(account, chatId, id, timeArg) { - if (!id && !timeArg) throw "Around what?"; - - const time = await ( - timeArg ? Promise.resolve(timeArg) : - this.getMessage(account, chatId, id, null).then((m) => - m ? m.timestamp : this.getMessage(account, chatId, null, id).then((m2) => m2?.timestamp) - ) - ); - if (!time) return []; - - const before = this.getMessagesBefore(account, chatId, id, time); + getMessagesAround: async function(account, around) { + const chatId = around.chatId(); + const before = this.getMessagesBefore(account, chatId, around); const tx = db.transaction(["messages"], "readonly"); const store = tx.objectStore("messages"); const cursor = store.index("chats").openCursor( - IDBKeyRange.bound([account, chatId, new Date(time)], [account, chatId, []]), + IDBKeyRange.bound([account, chatId, new Date(around.timestamp)], [account, chatId, []]), "next" ); const aroundAndAfter = this.getMessagesFromCursor(cursor, null, null); diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx index 5ecba83..a77c5e5 100644 --- a/borogove/persistence/Sqlite.hx +++ b/borogove/persistence/Sqlite.hx @@ -500,36 +500,24 @@ class Sqlite implements Persistence implements KeyValueStore { } @HaxeCBridge.noemit - public function getMessagesBefore(accountId: String, chatId: String, beforeId: Null<String>, beforeTime: Null<String>): Promise<Array<ChatMessage>> { - return getMessages(accountId, chatId, beforeTime, "<"); + public function getMessagesBefore(accountId: String, chatId: String, before: Null<ChatMessage>): Promise<Array<ChatMessage>> { + return getMessages(accountId, chatId, before?.timestamp, "<"); } @HaxeCBridge.noemit - public function getMessagesAfter(accountId: String, chatId: String, afterId: Null<String>, afterTime: Null<String>): Promise<Array<ChatMessage>> { - return getMessages(accountId, chatId, afterTime, ">"); + public function getMessagesAfter(accountId: String, chatId: String, after: Null<ChatMessage>): Promise<Array<ChatMessage>> { + return getMessages(accountId, chatId, after?.timestamp, ">"); } @HaxeCBridge.noemit - public function getMessagesAround(accountId: String, chatId: String, aroundId: Null<String>, aroundTime: Null<String>): Promise<Array<ChatMessage>> { - return (if (aroundTime == null) { - getMessage(accountId, chatId, aroundId, null).then(m -> - if (m != null) { - Promise.resolve(m.timestamp); - } else { - getMessage(accountId, chatId, null, aroundId).then(m -> m?.timestamp); - } - ); - } else { - Promise.resolve(aroundTime); - }).then(aroundTime -> - thenshim.PromiseTools.all([ - getMessages(accountId, chatId, aroundTime, "<"), - getMessages(accountId, chatId, aroundTime, ">=") - ]) - ).then(results -> results.flatten()); + public function getMessagesAround(accountId: String, around: ChatMessage): Promise<Array<ChatMessage>> { + final chatId = around.chatId(); + return thenshim.PromiseTools.all([ + getMessages(accountId, chatId, around.timestamp, "<"), + getMessages(accountId, chatId, around.timestamp, ">=") + ]).then(results -> results.flatten()); } - private function getChatUnreadDetails(accountId: String, chat: Chat): Promise<{ chatId: String, message: ChatMessage, unreadCount: Int }> { return db.exec( "WITH subq as (SELECT ROWID as row, COALESCE(MAX(created_at), 0) as created_at FROM messages where account_id=? AND chat_id=? AND (mam_id=? OR direction=?)) SELECT chat_id AS chatId, stanza, direction, type, status, status_text, sender_id, mam_id, mam_by, sync_point, CASE WHEN (SELECT row FROM subq) IS NULL THEN COUNT(*) ELSE COUNT(*) - 1 END AS unreadCount, strftime('%FT%H:%M:%fZ', MAX(messages.created_at) / 1000.0, 'unixepoch') AS timestamp FROM messages WHERE account_id=? AND chat_id=? AND (stanza_id IS NULL OR stanza_id='' OR stanza_id=correction_id) AND (messages.created_at >= (SELECT created_at FROM subq) AND (messages.created_at <> (SELECT created_at FROM subq) OR messages.ROWID = (SELECT row FROM subq)))",