| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2025-03-05 19:47:30 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2025-03-05 19:47:30 UTC |
| parent | 9e9f75c1a8247edbad5661aef41e1d76d8e260a2 |
| snikket/persistence/Sqlite.hx | +69 | -16 |
diff --git a/snikket/persistence/Sqlite.hx b/snikket/persistence/Sqlite.hx index 93e9a47..0e2eabf 100644 --- a/snikket/persistence/Sqlite.hx +++ b/snikket/persistence/Sqlite.hx @@ -247,6 +247,7 @@ class Sqlite implements Persistence implements KeyValueStore { final chatIds = []; final localIds = []; + final replyTos = []; for (message in messages) { if (message.serverId == null && message.localId == null) throw "Cannot store a message with no id"; if (message.serverId == null && message.isIncoming()) throw "Cannot store an incoming message with no server id"; @@ -258,6 +259,9 @@ class Sqlite implements Persistence implements KeyValueStore { chatIds.push(message.chatId()); localIds.push(message.localId); } + if (message.replyToMessage != null && message.replyToMessage.serverIdBy == null) { + replyTos.push({ chatId: message.chatId(), serverId: message.replyToMessage.serverId, localId: message.replyToMessage.localId }); + } } (if (chatIds.length > 0 && localIds.length > 0) { @@ -279,10 +283,11 @@ class Sqlite implements Persistence implements KeyValueStore { message.timestamp, message.status, message.direction, message.asStanza().toString() ] : Array<Dynamic>)) - ).then(_ -> callback(messages)) - ); + ) + ).then(_ -> { + hydrateReplyTo(accountId, messages, replyTos).then(callback); + }); - // TODO: hydrate reply to stubs? // TODO: corrections // TODO: fetch reactions? } @@ -305,14 +310,19 @@ class Sqlite implements Persistence implements KeyValueStore { q += "LIMIT 1"; db.exec(q, params).then(result -> { for (row in result) { - callback(hydrateMessage(accountId, row)); + final message = hydrateMessage(accountId, row); + (if (message.replyToMessage != null) { + hydrateReplyTo(accountId, [message], [{ chatId: chatId, serverId: message.replyToMessage.serverId, localId: message.replyToMessage.localId }]); + } else { + Promise.resolve([message]); + }).then(hydrated -> callback(hydrated[0])); return; } callback(null); }); } - private function getMessages(accountId: String, chatId: String, time: Null<String>, op: String): Promise<Iterator<ChatMessage>> { + private function getMessages(accountId: String, chatId: String, time: Null<String>, op: String): Promise<Array<ChatMessage>> { var q = "SELECT stanza, direction, strftime('%FT%H:%M:%fZ', created_at / 1000.0, 'unixepoch') AS timestamp, mam_id, mam_by FROM messages WHERE account_id=? AND chat_id=?"; final params = [accountId, chatId]; if (time != null) { @@ -327,26 +337,30 @@ class Sqlite implements Persistence implements KeyValueStore { return db.exec(q, params).then(result -> ({ hasNext: result.hasNext, next: () -> hydrateMessage(accountId, result.next()) - })).then(iter -> + })).then(iter -> { + final arr = []; + final replyTos = []; + for (message in iter) { + arr.push(message); + if (message.replyToMessage != null && message.replyToMessage.serverIdBy == null) { + replyTos.push({ chatId: message.chatId(), serverId: message.replyToMessage.serverId, localId: message.replyToMessage.localId }); + } + } if (op == "<" || op == "<=") { - final arr = { iterator: () -> iter }.array(); arr.reverse(); - final reviter = arr.iterator(); - { hasNext: reviter.hasNext, next: reviter.next }; - } else { - iter; } - ); + return hydrateReplyTo(accountId, arr, replyTos); + }); } @HaxeCBridge.noemit public function getMessagesBefore(accountId: String, chatId: String, beforeId: Null<String>, beforeTime: Null<String>, callback: (Array<ChatMessage>)->Void) { - getMessages(accountId, chatId, beforeTime, "<").then(iter -> callback({ iterator: () -> iter }.array())); + getMessages(accountId, chatId, beforeTime, "<").then(callback); } @HaxeCBridge.noemit public function getMessagesAfter(accountId: String, chatId: String, afterId: Null<String>, afterTime: Null<String>, callback: (Array<ChatMessage>)->Void) { - getMessages(accountId, chatId, afterTime, ">").then(iter -> callback({ iterator: () -> iter }.array())); + getMessages(accountId, chatId, afterTime, ">").then(callback); } @HaxeCBridge.noemit @@ -367,7 +381,7 @@ class Sqlite implements Persistence implements KeyValueStore { getMessages(accountId, chatId, aroundTime, ">=") ]) ).then(results -> - callback(results.flatMap(iter -> { iterator: () -> iter }.array())) + callback(results.flatMap(arr -> arr)) ); } @@ -442,7 +456,12 @@ class Sqlite implements Persistence implements KeyValueStore { ) ).then(result -> { for (row in result) { - callback(hydrateMessage(accountId, row)); + final message = hydrateMessage(accountId, row); + (if (message.replyToMessage != null) { + hydrateReplyTo(accountId, [message], [{ chatId: message.chatId(), serverId: message.replyToMessage.serverId, localId: message.replyToMessage.localId }]); + } else { + Promise.resolve([message]); + }).then(hydrated -> callback(hydrated[0])); return; } }); @@ -606,6 +625,40 @@ class Sqlite implements Persistence implements KeyValueStore { }); } + private function hydrateReplyTo(accountId: String, messages: Array<ChatMessage>, replyTos: Array<{ chatId: String, serverId: Null<String>, localId: Null<String> }>) { + return (if (replyTos.length < 1) { + Promise.resolve(null); + } else { + final params = [accountId]; + final q = new StringBuf(); + q.add("SELECT chat_id, stanza_id, stanza, direction, strftime('%FT%H:%M:%fZ', created_at / 1000.0, 'unixepoch') AS timestamp, mam_id, mam_by FROM messages WHERE account_id=? AND ("); + q.add(replyTos.map(parent -> + if (parent.serverId != null) { + params.push(parent.chatId); + params.push(parent.serverId); + " (chat_id=? AND mam_id=?)"; + } else { + params.push(parent.chatId); + params.push(parent.localId); + " (chat_id=? AND stanza_id=?)"; + } + ).join(" OR ")); + q.add(")"); + db.exec(q.toString(), params); + }).then(iter -> { + if (iter != null) { + final parents = { iterator: () -> iter }.array(); + for (message in messages) { + if (message.replyToMessage != null) { + final found: Dynamic = parents.find(p -> p.chat_id == message.chatId() && (message.replyToMessage.serverId == null || p.mam_id == message.replyToMessage.serverId) && (message.replyToMessage.localId == null || p.stanza_id == message.replyToMessage.localId)); + if (found != null) message.replyToMessage = hydrateMessage(accountId, found); + } + } + } + return messages; + }); + } + private function hydrateMessage(accountId: String, row: { stanza: String, timestamp: String, direction: MessageDirection, mam_id: String, mam_by: String }) { // TODO final accountJid = JID.parse(accountId);