fixes for very large conversation lists
This commit is contained in:
@@ -21,6 +21,13 @@ public class KordophoneApp : Adw.Application
|
|||||||
Gtk.STYLE_PROVIDER_PRIORITY_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
|
// Warm up dbus connections
|
||||||
Settings.get_instance();
|
Settings.get_instance();
|
||||||
Repository.get_instance();
|
Repository.get_instance();
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class MainWindow : Adw.ApplicationWindow
|
|||||||
transcript_view.message_list.model = null;
|
transcript_view.message_list.model = null;
|
||||||
} else {
|
} else {
|
||||||
transcript_view.message_list.model = new MessageListModel (conversation_guid);
|
transcript_view.message_list.model = new MessageListModel (conversation_guid);
|
||||||
|
Repository.get_instance().sync_conversation(conversation_guid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,10 +39,7 @@ public class PreferencesWindow : Adw.PreferencesDialog {
|
|||||||
|
|
||||||
settings = Settings.get_instance();
|
settings = Settings.get_instance();
|
||||||
settings.settings_ready.connect(load_settings);
|
settings.settings_ready.connect(load_settings);
|
||||||
if (settings.is_connected) {
|
load_settings();
|
||||||
message("settings is connected");
|
|
||||||
load_settings();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void load_settings() {
|
private void load_settings() {
|
||||||
|
|||||||
@@ -38,21 +38,22 @@ public class ConversationListView : Adw.Bin
|
|||||||
header_bar.set_title_widget (new Label ("Kordophone"));
|
header_bar.set_title_widget (new Label ("Kordophone"));
|
||||||
container.add_top_bar (header_bar);
|
container.add_top_bar (header_bar);
|
||||||
|
|
||||||
// Set up refresh button
|
// Setup application menu
|
||||||
var refresh_button = new Button.from_icon_name ("view-refresh-symbolic");
|
var app_menu = new Menu ();
|
||||||
refresh_button.tooltip_text = "Refresh Conversations";
|
app_menu.append ("Refresh", "list.refresh");
|
||||||
refresh_button.clicked.connect (() => {
|
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) {
|
if (conversation_model != null) {
|
||||||
conversation_model.load_conversations ();
|
conversation_model.load_conversations ();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
header_bar.pack_end (refresh_button);
|
|
||||||
|
|
||||||
// Setup application menu
|
var action_group = new SimpleActionGroup ();
|
||||||
var app_menu = new Menu ();
|
action_group.add_action(refresh_action);
|
||||||
app_menu.append ("Refresh", "refresh");
|
insert_action_group ("list", action_group);
|
||||||
app_menu.append ("Settings...", "win.settings");
|
|
||||||
app_menu.append ("Quit", "app.quit");
|
|
||||||
|
|
||||||
var menu_button = new Gtk.MenuButton ();
|
var menu_button = new Gtk.MenuButton ();
|
||||||
menu_button.menu_model = app_menu;
|
menu_button.menu_model = app_menu;
|
||||||
|
|||||||
@@ -11,7 +11,14 @@ public class ConversationRow : Adw.ActionRow {
|
|||||||
title = conversation.display_name.strip();
|
title = conversation.display_name.strip();
|
||||||
title_lines = 1;
|
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;
|
subtitle_lines = 1;
|
||||||
|
|
||||||
add_css_class("conversation-row");
|
add_css_class("conversation-row");
|
||||||
|
|||||||
@@ -1,60 +1,12 @@
|
|||||||
public abstract class DBusServiceBase : Object {
|
public abstract class DBusServiceProxy : Object {
|
||||||
protected uint dbus_watch_id;
|
|
||||||
public bool is_connected { get; private set; default = false; }
|
|
||||||
|
|
||||||
protected const string DBUS_PATH = "/net/buzzert/kordophonecd/daemon";
|
protected const string DBUS_PATH = "/net/buzzert/kordophonecd/daemon";
|
||||||
protected const string DBUS_NAME = "net.buzzert.kordophonecd";
|
protected const string DBUS_NAME = "net.buzzert.kordophonecd";
|
||||||
|
|
||||||
protected DBusServiceBase() {
|
protected DBusServiceProxy() {
|
||||||
connect_to_dbus.begin((obj, res) => {
|
|
||||||
connect_to_dbus.end(res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
~DBusServiceBase() {
|
|
||||||
if (dbus_watch_id > 0) {
|
protected errordomain DBusServiceProxyError {
|
||||||
Bus.unwatch_name(dbus_watch_id);
|
NOT_CONNECTED,
|
||||||
}
|
PASSWORD_STORAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,7 +27,7 @@ namespace DBusService {
|
|||||||
public abstract string get_version() throws DBusError, IOError;
|
public abstract string get_version() throws DBusError, IOError;
|
||||||
|
|
||||||
[DBus (name = "GetConversations")]
|
[DBus (name = "GetConversations")]
|
||||||
public abstract GLib.HashTable<string, GLib.Variant>[] get_conversations() throws DBusError, IOError;
|
public abstract GLib.HashTable<string, GLib.Variant>[] get_conversations(int limit, int offset) throws DBusError, IOError;
|
||||||
|
|
||||||
[DBus (name = "SyncConversationList")]
|
[DBus (name = "SyncConversationList")]
|
||||||
public abstract void sync_conversation_list() throws DBusError, IOError;
|
public abstract void sync_conversation_list() throws DBusError, IOError;
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
<!-- Conversations -->
|
<!-- Conversations -->
|
||||||
|
|
||||||
<method name="GetConversations">
|
<method name="GetConversations">
|
||||||
|
<arg type="i" name="limit" direction="in"/>
|
||||||
|
<arg type="i" name="offset" direction="in"/>
|
||||||
|
|
||||||
<arg type="aa{sv}" direction="out" name="conversations">
|
<arg type="aa{sv}" direction="out" name="conversations">
|
||||||
<annotation name="org.freedesktop.DBus.DocString"
|
<annotation name="org.freedesktop.DBus.DocString"
|
||||||
value="Array of dictionaries. Each dictionary has keys:
|
value="Array of dictionaries. Each dictionary has keys:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using GLib;
|
using GLib;
|
||||||
using Gee;
|
using Gee;
|
||||||
|
|
||||||
public class Repository : DBusServiceBase {
|
public class Repository : DBusServiceProxy {
|
||||||
public signal void conversations_updated();
|
public signal void conversations_updated();
|
||||||
public signal void messages_updated(string conversation_guid);
|
public signal void messages_updated(string conversation_guid);
|
||||||
|
|
||||||
@@ -15,40 +15,37 @@ public class Repository : DBusServiceBase {
|
|||||||
|
|
||||||
private static Repository instance = null;
|
private static Repository instance = null;
|
||||||
private DBusService.Repository? dbus_repository;
|
private DBusService.Repository? dbus_repository;
|
||||||
|
private uint dbus_watch_id;
|
||||||
|
|
||||||
private Repository() {
|
private Repository() {
|
||||||
base();
|
this.dbus_watch_id = Bus.watch_name(BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE, (name, name_owner) => {
|
||||||
|
connect_to_repository();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string get_service_name() {
|
private void connect_to_repository() {
|
||||||
return "Repository";
|
try {
|
||||||
}
|
this.dbus_repository = Bus.get_proxy_sync<DBusService.Repository>(BusType.SESSION, DBUS_NAME, DBUS_PATH);
|
||||||
|
this.dbus_repository.conversations_updated.connect(() => {
|
||||||
protected override async Object? get_proxy() throws Error {
|
conversations_updated();
|
||||||
dbus_repository = yield Bus.get_proxy(BusType.SESSION, DBUS_NAME, DBUS_PATH);
|
});
|
||||||
dbus_repository.get_version(); // Test the connection
|
|
||||||
return dbus_repository;
|
this.dbus_repository.messages_updated.connect((conversation_guid) => {
|
||||||
}
|
messages_updated(conversation_guid);
|
||||||
|
});
|
||||||
protected override void setup_signals() {
|
|
||||||
dbus_repository.conversations_updated.connect(() => {
|
|
||||||
conversations_updated();
|
conversations_updated();
|
||||||
});
|
} catch (GLib.Error e) {
|
||||||
|
warning("Failed to connect to repository: %s", e.message);
|
||||||
dbus_repository.messages_updated.connect((conversation_guid) => {
|
}
|
||||||
messages_updated(conversation_guid);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initial load
|
|
||||||
conversations_updated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Conversation[] get_conversations() throws Error {
|
public Conversation[] get_conversations(int limit = 200) throws DBusServiceProxyError, GLib.Error {
|
||||||
if (!is_connected || dbus_repository == null) {
|
if (dbus_repository == null) {
|
||||||
throw create_not_connected_error();
|
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];
|
Conversation[] returned_conversations = new Conversation[conversations.length];
|
||||||
|
|
||||||
for (int i = 0; i < conversations.length; i++) {
|
for (int i = 0; i < conversations.length; i++) {
|
||||||
@@ -58,9 +55,9 @@ public class Repository : DBusServiceBase {
|
|||||||
return returned_conversations;
|
return returned_conversations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message[] get_messages(string conversation_guid, string last_message_id = "") throws Error {
|
public Message[] get_messages(string conversation_guid, string last_message_id = "") throws DBusServiceProxyError, GLib.Error {
|
||||||
if (!is_connected || dbus_repository == null) {
|
if (dbus_repository == null) {
|
||||||
throw create_not_connected_error();
|
throw new DBusServiceProxyError.NOT_CONNECTED("Repository not connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
var messages = dbus_repository.get_messages(conversation_guid, last_message_id);
|
var messages = dbus_repository.get_messages(conversation_guid, last_message_id);
|
||||||
@@ -73,11 +70,19 @@ public class Repository : DBusServiceBase {
|
|||||||
return returned_messages;
|
return returned_messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string send_message(string conversation_guid, string message) throws Error {
|
public string send_message(string conversation_guid, string message) throws DBusServiceProxyError, GLib.Error {
|
||||||
if (!is_connected || dbus_repository == null) {
|
if (dbus_repository == null) {
|
||||||
throw create_not_connected_error();
|
throw new DBusServiceProxyError.NOT_CONNECTED("Repository not connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
return dbus_repository.send_message(conversation_guid, message);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using GLib;
|
using GLib;
|
||||||
|
|
||||||
public class Settings : DBusServiceBase {
|
public class Settings : DBusServiceProxy
|
||||||
|
{
|
||||||
public signal void config_changed();
|
public signal void config_changed();
|
||||||
public signal void settings_ready();
|
public signal void settings_ready();
|
||||||
|
|
||||||
@@ -20,59 +21,45 @@ public class Settings : DBusServiceBase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
secret_service = Secret.Service.get_sync(Secret.ServiceFlags.OPEN_SESSION);
|
secret_service = Secret.Service.get_sync(Secret.ServiceFlags.OPEN_SESSION);
|
||||||
} catch (Error e) {
|
|
||||||
|
this.dbus_settings = Bus.get_proxy_sync<DBusService.Settings>(BusType.SESSION, DBUS_NAME, DBUS_PATH);
|
||||||
|
settings_ready();
|
||||||
|
} catch (GLib.Error e) {
|
||||||
warning("Failed to get secret service: %s", e.message);
|
warning("Failed to get secret service: %s", e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string get_service_name() {
|
public string get_server_url() throws DBusServiceProxyError, GLib.Error {
|
||||||
return "Settings";
|
if (dbus_settings == null) {
|
||||||
}
|
throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected");
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
return dbus_settings.server_u_r_l;
|
return dbus_settings.server_u_r_l;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set_server_url(string url) throws Error {
|
public void set_server_url(string url) throws Error, GLib.Error {
|
||||||
if (!is_connected || dbus_settings == null) {
|
if (dbus_settings == null) {
|
||||||
throw create_not_connected_error();
|
throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected");
|
||||||
}
|
}
|
||||||
dbus_settings.server_u_r_l = url;
|
dbus_settings.server_u_r_l = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string get_username() throws Error {
|
public string get_username() throws Error, GLib.Error {
|
||||||
if (!is_connected || dbus_settings == null) {
|
if (dbus_settings == null) {
|
||||||
throw create_not_connected_error();
|
throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected");
|
||||||
}
|
}
|
||||||
return dbus_settings.username;
|
return dbus_settings.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set_username(string username) throws Error {
|
public void set_username(string username) throws Error, GLib.Error {
|
||||||
if (!is_connected || dbus_settings == null) {
|
if (dbus_settings == null) {
|
||||||
throw create_not_connected_error();
|
throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected");
|
||||||
}
|
}
|
||||||
dbus_settings.username = username;
|
dbus_settings.username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set_server(string url, string username) throws Error {
|
public void set_server(string url, string username) throws Error, GLib.Error {
|
||||||
if (!is_connected || dbus_settings == null) {
|
if (dbus_settings == null) {
|
||||||
throw create_not_connected_error();
|
throw new DBusServiceProxyError.NOT_CONNECTED("Settings not connected");
|
||||||
}
|
}
|
||||||
dbus_settings.set_server(url, username);
|
dbus_settings.set_server(url, username);
|
||||||
}
|
}
|
||||||
@@ -96,7 +83,7 @@ public class Settings : DBusServiceBase {
|
|||||||
return password.get_text();
|
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();
|
var attributes = password_attributes();
|
||||||
bool result = secret_service.store_sync(
|
bool result = secret_service.store_sync(
|
||||||
null,
|
null,
|
||||||
@@ -109,7 +96,7 @@ public class Settings : DBusServiceBase {
|
|||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
warning("Failed to store password for user %s", get_username());
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user