| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-02-23 14:37:45 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-02-23 14:37:45 UTC |
| parent | f0a0c43f46285f328a83c4f49405b8d616ae35c0 |
| borogove/Chat.hx | +11 | -8 |
| borogove/Client.hx | +2 | -2 |
| borogove/persistence/IDB.js | +2 | -0 |
| borogove/persistence/Sqlite.hx | +11 | -4 |
diff --git a/borogove/Chat.hx b/borogove/Chat.hx index c62d420..cd5be03 100644 --- a/borogove/Chat.hx +++ b/borogove/Chat.hx @@ -114,7 +114,7 @@ abstract class Chat { private var omemoContactDeviceIDs: Null<Array<Int>> = null; @:allow(borogove) - private function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, isBlocked = false, extensions: Null<Stanza> = null, readUpToId: Null<String> = null, readUpToBy: Null<String> = null, omemoContactDeviceIDs: Array<Int> = null) { + private function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, isBookmarked = false, isBlocked = false, extensions: Null<Stanza> = null, readUpToId: Null<String> = null, readUpToBy: Null<String> = null, omemoContactDeviceIDs: Array<Int> = null) { if (chatId == null || chatId == "") { throw "chatId may not be empty"; } @@ -123,6 +123,7 @@ abstract class Chat { this.persistence = persistence; this.chatId = chatId; this.uiState = uiState; + this.isBookmarked = isBookmarked; this.isBlocked = isBlocked; this.extensions = extensions ?? new Stanza("extensions", { xmlns: "urn:xmpp:bookmarks:1" }); this.readUpToId = readUpToId; @@ -936,8 +937,8 @@ abstract class Chat { #end class DirectChat extends Chat { @:allow(borogove) - private function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, isBlocked = false, extensions: Null<Stanza> = null, readUpToId: Null<String> = null, readUpToBy: Null<String> = null, omemoContactDeviceIDs: Array<Int> = null) { - super(client, stream, persistence, chatId, uiState, isBlocked, extensions, readUpToId, readUpToBy, omemoContactDeviceIDs); + private function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, isBookmarked = false, isBlocked = false, extensions: Null<Stanza> = null, readUpToId: Null<String> = null, readUpToBy: Null<String> = null, omemoContactDeviceIDs: Array<Int> = null) { + super(client, stream, persistence, chatId, uiState, isBookmarked, isBlocked, extensions, readUpToId, readUpToBy, omemoContactDeviceIDs); outbox.start(); } @@ -1243,8 +1244,8 @@ class Channel extends Chat { private var _nickInUse = null; @:allow(borogove) - private function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, isBlocked = false, extensions = null, readUpToId = null, readUpToBy = null, ?disco: Caps) { - super(client, stream, persistence, chatId, uiState, isBlocked, extensions, readUpToId, readUpToBy); + private function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, isBookmarked = false, isBlocked = false, extensions = null, readUpToId = null, readUpToBy = null, ?disco: Caps) { + super(client, stream, persistence, chatId, uiState, isBookmarked, isBlocked, extensions, readUpToId, readUpToBy); if (disco != null) { this.disco = disco; if (!disco.features.contains("http://jabber.org/protocol/muc")) { @@ -1949,6 +1950,7 @@ class AvailableChat { class SerializedChat { public final chatId:String; public final trusted:Bool; + public final isBookmarked:Bool; public final avatarSha1:Null<BytesData>; public final presence:Map<String, Presence>; public final displayName:Null<String>; @@ -1964,9 +1966,10 @@ class SerializedChat { public final notifyMention: Bool; public final notifyReply: Bool; - public function new(chatId: String, trusted: Bool, avatarSha1: Null<BytesData>, presence: Map<String, Presence>, displayName: Null<String>, uiState: Null<UiState>, isBlocked: Null<Bool>, extensions: Null<String>, readUpToId: Null<String>, readUpToBy: Null<String>, notificationsFiltered: Null<Bool>, notifyMention: Bool, notifyReply: Bool, disco: Null<Caps>, omemoContactDeviceIDs: Array<Int>, klass: String) { + public function new(chatId: String, trusted: Bool, isBookmarked: Bool, avatarSha1: Null<BytesData>, presence: Map<String, Presence>, displayName: Null<String>, uiState: Null<UiState>, isBlocked: Null<Bool>, extensions: Null<String>, readUpToId: Null<String>, readUpToBy: Null<String>, notificationsFiltered: Null<Bool>, notifyMention: Bool, notifyReply: Bool, disco: Null<Caps>, omemoContactDeviceIDs: Array<Int>, klass: String) { this.chatId = chatId; this.trusted = trusted; + this.isBookmarked = isBookmarked; this.avatarSha1 = avatarSha1; this.presence = presence; this.displayName = displayName; @@ -1989,9 +1992,9 @@ class SerializedChat { var mention = notifyMention; final chat = if (klass == "DirectChat") { - new DirectChat(client, stream, persistence, chatId, uiState, isBlocked, extensionsStanza, readUpToId, readUpToBy, omemoContactDeviceIDs); + new DirectChat(client, stream, persistence, chatId, uiState, isBookmarked, isBlocked, extensionsStanza, readUpToId, readUpToBy, omemoContactDeviceIDs); } else if (klass == "Channel") { - final channel = new Channel(client, stream, persistence, chatId, uiState, isBlocked, extensionsStanza, readUpToId, readUpToBy); + final channel = new Channel(client, stream, persistence, chatId, uiState, isBookmarked, isBlocked, extensionsStanza, readUpToId, readUpToBy); channel.disco = disco ?? new Caps("", [], ["http://jabber.org/protocol/muc"], []); if (notificationsFiltered == null && !channel.isPrivate()) { mention = filterN = true; diff --git a/borogove/Client.hx b/borogove/Client.hx index 474506b..a19a29c 100644 --- a/borogove/Client.hx +++ b/borogove/Client.hx @@ -1083,7 +1083,7 @@ class Client extends EventEmitter { } final chat = if (availableChat.isChannel()) { - final channel = new Channel(this, this.stream, this.persistence, availableChat.chatId, Open, false, null, availableChat.caps); + final channel = new Channel(this, this.stream, this.persistence, availableChat.chatId, Open, false, false, null, availableChat.caps); channel.setupNotifications(); chats.unshift(channel); channel.selfPing(false); @@ -1680,7 +1680,7 @@ class Client extends EventEmitter { } else { persistence.storeCaps(resultCaps); if (resultCaps.isChannel(jid)) { - final chat = new Channel(this, this.stream, this.persistence, jid, uiState, false, null, resultCaps); + final chat = new Channel(this, this.stream, this.persistence, jid, uiState, false, false, null, resultCaps); chat.setupNotifications(); chats.unshift(chat); if (inSync && sendAvailable) chat.selfPing(false); diff --git a/borogove/persistence/IDB.js b/borogove/persistence/IDB.js index e25ab58..8abd974 100644 --- a/borogove/persistence/IDB.js +++ b/borogove/persistence/IDB.js @@ -288,6 +288,7 @@ export default async (dbname, media, tokenize, stemmer) => { account: account, chatId: chat.chatId, trusted: chat.trusted, + isBookmarked: chat.isBookmarked, avatarSha1: chat.avatarSha1, presence: new Map([...chat.presence.entries()].map(([k, p]) => [k, { caps: p.caps?.ver(), mucUser: p.mucUser?.toString(), avatarHash: p.avatarHash?.serializeUri() }])), displayName: chat.displayName, @@ -312,6 +313,7 @@ export default async (dbname, media, tokenize, stemmer) => { return await Promise.all(result.map(async (r) => new borogove_SerializedChat( r.chatId, r.trusted, + r.isBookmarked, r.avatarSha1, new Map(await Promise.all((r.presence instanceof Map ? [...r.presence.entries()] : Object.entries(r.presence)).map( async ([k, p]) => [k, new borogove_Presence(p.caps && await this.getCaps(p.caps), p.mucUser && borogove_Stanza.parse(p.mucUser), p.avatarHash && borogove_Hash.fromUri(p.avatarHash))] diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx index babd06b..a9bfe4e 100644 --- a/borogove/persistence/Sqlite.hx +++ b/borogove/persistence/Sqlite.hx @@ -184,6 +184,12 @@ class Sqlite implements Persistence implements KeyValueStore { "PRAGMA user_version = 5"]); } return Promise.resolve(null); + }).then(_ -> { + if (version < 6) { + return exec(["ALTER TABLE chats ADD COLUMN bookmarked INTEGER NOT NULL DEFAULT 0", + "PRAGMA user_version = 6"]); + } + return Promise.resolve(null); }); }); }); @@ -271,7 +277,7 @@ class Sqlite implements Persistence implements KeyValueStore { for (_ in storeChatBuffer) { if (!first) q.add(","); first = false; - q.add("(?,?,?,?,?,?,?,?,?,?,?,jsonb(?),?,?,?,?)"); + q.add("(?,?,?,?,?,?,?,?,?,?,?,jsonb(?),?,?,?,?,?)"); } db.exec( q.toString(), @@ -284,7 +290,8 @@ class Sqlite implements Persistence implements KeyValueStore { chat.extensions.toString(), chat.readUpTo(), chat.readUpToBy, channel?.disco?.verRaw().hash, Json.stringify(mapPresence(chat)), Type.getClassName(Type.getClass(chat)).split(".").pop(), - chat.notificationsFiltered(), chat.notifyMention(), chat.notifyReply() + chat.notificationsFiltered(), chat.notifyMention(), chat.notifyReply(), + chat.isBookmarked ]; return row; }) @@ -297,7 +304,7 @@ class Sqlite implements Persistence implements KeyValueStore { @HaxeCBridge.noemit public function getChats(accountId: String): Promise<Array<SerializedChat>> { return db.exec( - "SELECT chat_id, trusted, avatar_sha1, fn, ui_state, blocked, extensions, read_up_to_id, read_up_to_by, notifications_filtered, notify_mention, notify_reply, json(caps) AS caps, caps_ver, json(presence) AS presence, class FROM chats LEFT JOIN caps ON chats.caps_ver=caps.sha1 WHERE account_id=?", + "SELECT chat_id, trusted, bookmarked, avatar_sha1, fn, ui_state, blocked, extensions, read_up_to_id, read_up_to_by, notifications_filtered, notify_mention, notify_reply, json(caps) AS caps, caps_ver, json(presence) AS presence, class FROM chats LEFT JOIN caps ON chats.caps_ver=caps.sha1 WHERE account_id=?", [accountId] ).then(result -> { final fetchCaps: Map<BytesData, Bool> = []; @@ -339,7 +346,7 @@ class Sqlite implements Persistence implements KeyValueStore { ); } // FIXME: Empty OMEMO contact device ids hardcoded in next line - chats.push(new SerializedChat(row.chat_id, row.trusted != 0, row.avatar_sha1, presenceMap, row.fn, row.ui_state, row.blocked != 0, row.extensions, row.read_up_to_id, row.read_up_to_by, row.notifications_filtered == null ? null : row.notifications_filtered != 0, row.notify_mention != 0, row.notify_reply != 0, row.capsObj, [], Reflect.field(row, "class"))); + chats.push(new SerializedChat(row.chat_id, row.trusted != 0, row.bookmarked != 0, row.avatar_sha1, presenceMap, row.fn, row.ui_state, row.blocked != 0, row.extensions, row.read_up_to_id, row.read_up_to_by, row.notifications_filtered == null ? null : row.notifications_filtered != 0, row.notify_mention != 0, row.notify_reply != 0, row.capsObj, [], Reflect.field(row, "class"))); } return chats; });