From e44120712fb6ec364404350a34223e1c77ce0832 Mon Sep 17 00:00:00 2001 From: James Magahern Date: Sat, 3 May 2025 18:19:17 -0700 Subject: [PATCH] fixes for very large conversation lists --- src/application/kordophone-application.vala | 7 ++ src/application/main-window.vala | 1 + src/application/preferences-window.vala | 5 +- .../conversation-list-view.vala | 21 +++--- src/conversation-list/conversation-row.vala | 9 ++- src/service/dbus-service-base.vala | 66 +++-------------- src/service/interface/dbusservice.vala | 2 +- .../xml/net.buzzert.kordophonecd.Server.xml | 3 + src/service/repository.vala | 73 ++++++++++--------- src/service/settings.vala | 59 ++++++--------- 10 files changed, 103 insertions(+), 143 deletions(-) diff --git a/src/application/kordophone-application.vala b/src/application/kordophone-application.vala index 758e4dd..3260af4 100644 --- a/src/application/kordophone-application.vala +++ b/src/application/kordophone-application.vala @@ -21,6 +21,13 @@ public class KordophoneApp : Adw.Application Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ); + // Setup application actions + var quit_action = new SimpleAction("quit", null); + quit_action.activate.connect(() => { + this.quit(); + }); + add_action(quit_action); + // Warm up dbus connections Settings.get_instance(); Repository.get_instance(); diff --git a/src/application/main-window.vala b/src/application/main-window.vala index ccb44fe..6ce22cc 100644 --- a/src/application/main-window.vala +++ b/src/application/main-window.vala @@ -40,6 +40,7 @@ public class MainWindow : Adw.ApplicationWindow transcript_view.message_list.model = null; } else { transcript_view.message_list.model = new MessageListModel (conversation_guid); + Repository.get_instance().sync_conversation(conversation_guid); } } diff --git a/src/application/preferences-window.vala b/src/application/preferences-window.vala index d5f2681..76969fa 100644 --- a/src/application/preferences-window.vala +++ b/src/application/preferences-window.vala @@ -39,10 +39,7 @@ public class PreferencesWindow : Adw.PreferencesDialog { settings = Settings.get_instance(); settings.settings_ready.connect(load_settings); - if (settings.is_connected) { - message("settings is connected"); - load_settings(); - } + load_settings(); } private void load_settings() { diff --git a/src/conversation-list/conversation-list-view.vala b/src/conversation-list/conversation-list-view.vala index cf2fd52..11bffe7 100644 --- a/src/conversation-list/conversation-list-view.vala +++ b/src/conversation-list/conversation-list-view.vala @@ -38,21 +38,22 @@ public class ConversationListView : Adw.Bin header_bar.set_title_widget (new Label ("Kordophone")); container.add_top_bar (header_bar); - // Set up refresh button - var refresh_button = new Button.from_icon_name ("view-refresh-symbolic"); - refresh_button.tooltip_text = "Refresh Conversations"; - refresh_button.clicked.connect (() => { + // Setup application menu + var app_menu = new Menu (); + app_menu.append ("Refresh", "list.refresh"); + app_menu.append ("Settings...", "win.settings"); + app_menu.append ("Quit", "app.quit"); + + var refresh_action = new SimpleAction("refresh", null); + refresh_action.activate.connect (() => { if (conversation_model != null) { conversation_model.load_conversations (); } }); - header_bar.pack_end (refresh_button); - // Setup application menu - var app_menu = new Menu (); - app_menu.append ("Refresh", "refresh"); - app_menu.append ("Settings...", "win.settings"); - app_menu.append ("Quit", "app.quit"); + var action_group = new SimpleActionGroup (); + action_group.add_action(refresh_action); + insert_action_group ("list", action_group); var menu_button = new Gtk.MenuButton (); menu_button.menu_model = app_menu; diff --git a/src/conversation-list/conversation-row.vala b/src/conversation-list/conversation-row.vala index 5f75b80..d809040 100644 --- a/src/conversation-list/conversation-row.vala +++ b/src/conversation-list/conversation-row.vala @@ -11,7 +11,14 @@ public class ConversationRow : Adw.ActionRow { title = conversation.display_name.strip(); title_lines = 1; - subtitle = conversation.last_message_preview.strip(); + var preview = conversation.last_message_preview + .strip() + .replace("\n", " ") + .replace("<", "\\<") + .replace(">", "\\>") + .replace("&", "&"); + + subtitle = preview.length > 100 ? preview.substring(0, 100) : preview; subtitle_lines = 1; add_css_class("conversation-row"); diff --git a/src/service/dbus-service-base.vala b/src/service/dbus-service-base.vala index f0a2ffa..cf8cd8a 100644 --- a/src/service/dbus-service-base.vala +++ b/src/service/dbus-service-base.vala @@ -1,60 +1,12 @@ -public abstract class DBusServiceBase : Object { - protected uint dbus_watch_id; - public bool is_connected { get; private set; default = false; } - +public abstract class DBusServiceProxy : Object { protected const string DBUS_PATH = "/net/buzzert/kordophonecd/daemon"; protected const string DBUS_NAME = "net.buzzert.kordophonecd"; - - protected DBusServiceBase() { - connect_to_dbus.begin((obj, res) => { - connect_to_dbus.end(res); - }); + + protected DBusServiceProxy() { } - - ~DBusServiceBase() { - if (dbus_watch_id > 0) { - Bus.unwatch_name(dbus_watch_id); - } - } - - protected abstract void setup_signals(); - protected abstract async Object? get_proxy() throws Error; - protected abstract string get_service_name(); - - private async void connect_to_dbus() { - try { - debug("Trying to connect to %s service at path: %s", get_service_name(), DBUS_PATH); - - var proxy = yield get_proxy(); - if (proxy == null) { - throw new Error(1337, 1, "Failed to get proxy"); - } - - // If we get here, connection succeeded - debug("Connected to %s service at path: %s", get_service_name(), DBUS_PATH); - is_connected = true; - - setup_signals(); - - } catch (Error e) { - debug("Failed to connect to %s at %s: %s", get_service_name(), DBUS_PATH, e.message); - } - - if (!is_connected) { - warning("Failed to connect to %s on any known path", get_service_name()); - - // Watch for the service to appear - dbus_watch_id = Bus.watch_name(BusType.SESSION, - DBUS_NAME, - BusNameWatcherFlags.AUTO_START, - () => { - connect_to_dbus.begin(); - }, - null); - } - } - - protected Error create_not_connected_error() { - return new Error(1337, 1, @"$(get_service_name()) not connected"); - } -} \ No newline at end of file +} + +protected errordomain DBusServiceProxyError { + NOT_CONNECTED, + PASSWORD_STORAGE; +} \ No newline at end of file diff --git a/src/service/interface/dbusservice.vala b/src/service/interface/dbusservice.vala index df38113..16aceb1 100644 --- a/src/service/interface/dbusservice.vala +++ b/src/service/interface/dbusservice.vala @@ -27,7 +27,7 @@ namespace DBusService { public abstract string get_version() throws DBusError, IOError; [DBus (name = "GetConversations")] - public abstract GLib.HashTable[] get_conversations() throws DBusError, IOError; + public abstract GLib.HashTable[] get_conversations(int limit, int offset) throws DBusError, IOError; [DBus (name = "SyncConversationList")] public abstract void sync_conversation_list() throws DBusError, IOError; diff --git a/src/service/interface/xml/net.buzzert.kordophonecd.Server.xml b/src/service/interface/xml/net.buzzert.kordophonecd.Server.xml index 7623714..8198155 100644 --- a/src/service/interface/xml/net.buzzert.kordophonecd.Server.xml +++ b/src/service/interface/xml/net.buzzert.kordophonecd.Server.xml @@ -11,6 +11,9 @@ + + + { + + private void connect_to_repository() { + try { + this.dbus_repository = Bus.get_proxy_sync(BusType.SESSION, DBUS_NAME, DBUS_PATH); + this.dbus_repository.conversations_updated.connect(() => { + conversations_updated(); + }); + + this.dbus_repository.messages_updated.connect((conversation_guid) => { + messages_updated(conversation_guid); + }); + conversations_updated(); - }); - - dbus_repository.messages_updated.connect((conversation_guid) => { - messages_updated(conversation_guid); - }); - - // Initial load - conversations_updated(); + } catch (GLib.Error e) { + warning("Failed to connect to repository: %s", e.message); + } } - public Conversation[] get_conversations() throws Error { - if (!is_connected || dbus_repository == null) { - throw create_not_connected_error(); + public Conversation[] get_conversations(int limit = 200) throws DBusServiceProxyError, GLib.Error { + if (dbus_repository == null) { + throw new DBusServiceProxyError.NOT_CONNECTED("Repository not connected"); } - - var conversations = dbus_repository.get_conversations(); + + var conversations = dbus_repository.get_conversations(limit, 0); Conversation[] returned_conversations = new Conversation[conversations.length]; for (int i = 0; i < conversations.length; i++) { @@ -58,9 +55,9 @@ public class Repository : DBusServiceBase { return returned_conversations; } - public Message[] get_messages(string conversation_guid, string last_message_id = "") throws Error { - if (!is_connected || dbus_repository == null) { - throw create_not_connected_error(); + public Message[] get_messages(string conversation_guid, string last_message_id = "") throws DBusServiceProxyError, GLib.Error { + if (dbus_repository == null) { + throw new DBusServiceProxyError.NOT_CONNECTED("Repository not connected"); } var messages = dbus_repository.get_messages(conversation_guid, last_message_id); @@ -73,11 +70,19 @@ public class Repository : DBusServiceBase { return returned_messages; } - public string send_message(string conversation_guid, string message) throws Error { - if (!is_connected || dbus_repository == null) { - throw create_not_connected_error(); + public string send_message(string conversation_guid, string message) throws DBusServiceProxyError, GLib.Error { + if (dbus_repository == null) { + throw new DBusServiceProxyError.NOT_CONNECTED("Repository not connected"); } return dbus_repository.send_message(conversation_guid, message); } + + public void sync_conversation(string conversation_guid) throws DBusServiceProxyError, GLib.Error { + if (dbus_repository == null) { + throw new DBusServiceProxyError.NOT_CONNECTED("Repository not connected"); + } + + dbus_repository.sync_conversation(conversation_guid); + } } diff --git a/src/service/settings.vala b/src/service/settings.vala index 07adfd0..a8d5674 100644 --- a/src/service/settings.vala +++ b/src/service/settings.vala @@ -1,6 +1,7 @@ using GLib; -public class Settings : DBusServiceBase { +public class Settings : DBusServiceProxy +{ public signal void config_changed(); public signal void settings_ready(); @@ -20,59 +21,45 @@ public class Settings : DBusServiceBase { try { secret_service = Secret.Service.get_sync(Secret.ServiceFlags.OPEN_SESSION); - } catch (Error e) { + + this.dbus_settings = Bus.get_proxy_sync(BusType.SESSION, DBUS_NAME, DBUS_PATH); + settings_ready(); + } catch (GLib.Error e) { warning("Failed to get secret service: %s", e.message); } } - protected override string get_service_name() { - return "Settings"; - } - - protected override async Object? get_proxy() throws Error { - dbus_settings = yield Bus.get_proxy(BusType.SESSION, DBUS_NAME, DBUS_PATH); - return dbus_settings; - } - - protected override void setup_signals() { - dbus_settings.config_changed.connect(() => { - config_changed(); - }); - - settings_ready(); - } - - public string get_server_url() throws Error { - if (!is_connected || dbus_settings == null) { - throw create_not_connected_error(); + public string get_server_url() throws DBusServiceProxyError, GLib.Error { + if (dbus_settings == null) { + throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected"); } return dbus_settings.server_u_r_l; } - public void set_server_url(string url) throws Error { - if (!is_connected || dbus_settings == null) { - throw create_not_connected_error(); + public void set_server_url(string url) throws Error, GLib.Error { + if (dbus_settings == null) { + throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected"); } dbus_settings.server_u_r_l = url; } - public string get_username() throws Error { - if (!is_connected || dbus_settings == null) { - throw create_not_connected_error(); + public string get_username() throws Error, GLib.Error { + if (dbus_settings == null) { + throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected"); } return dbus_settings.username; } - public void set_username(string username) throws Error { - if (!is_connected || dbus_settings == null) { - throw create_not_connected_error(); + public void set_username(string username) throws Error, GLib.Error { + if (dbus_settings == null) { + throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected"); } dbus_settings.username = username; } - public void set_server(string url, string username) throws Error { - if (!is_connected || dbus_settings == null) { - throw create_not_connected_error(); + public void set_server(string url, string username) throws Error, GLib.Error { + if (dbus_settings == null) { + throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected"); } dbus_settings.set_server(url, username); } @@ -96,7 +83,7 @@ public class Settings : DBusServiceBase { return password.get_text(); } - public void set_password(string password) throws Error { + public void set_password(string password) throws Error, GLib.Error { var attributes = password_attributes(); bool result = secret_service.store_sync( null, @@ -109,7 +96,7 @@ public class Settings : DBusServiceBase { if (!result) { warning("Failed to store password for user %s", get_username()); - throw new Error(1337, 1, "Failed to store password"); + throw new DBusServiceProxyError.PASSWORD_STORAGE("Failed to store password"); } } } \ No newline at end of file