From bf07ecddb35d899b158b97c53b671054acd8c571 Mon Sep 17 00:00:00 2001 From: Konstantin Kuznetsov Date: Tue, 12 Mar 2024 11:30:39 +0300 Subject: [PATCH 1/2] Add button to fetch MAM history for conversation --- libdino/src/service/history_sync.vala | 25 +++++++++ main/CMakeLists.txt | 1 + main/meson.build | 1 + .../ui/contact_details/history_provider.vala | 52 +++++++++++++++++++ main/src/ui/conversation_details.vala | 1 + 5 files changed, 80 insertions(+) create mode 100644 main/src/ui/contact_details/history_provider.vala diff --git a/libdino/src/service/history_sync.vala b/libdino/src/service/history_sync.vala index 8ab6d7bb..eb24c7e2 100644 --- a/libdino/src/service/history_sync.vala +++ b/libdino/src/service/history_sync.vala @@ -120,6 +120,31 @@ public class Dino.HistorySync { } } + public async void fetch_history(Account account, Jid target, Cancellable? cancellable = null) { + debug("Fetch history for %s", target.to_string()); + + RowOption latest_row_opt = db.mam_catchup.select() + .with(db.mam_catchup.account_id, "=", account.id) + .with(db.mam_catchup.server_jid, "=", target.to_string()) + .with(db.mam_catchup.to_time, ">=", (long) new DateTime.from_unix_utc(0).to_unix()) + .order_by(db.mam_catchup.to_time, "DESC") + .single().row(); + Row? latest_row = latest_row_opt.is_present() ? latest_row_opt.inner : null; + + if (latest_row == null) { + warning("Failed to fetch history for %s, no mam catchup data", target.to_string()); + return; + } + + DateTime latest_time = new DateTime.now(); + string latest_id = latest_row[db.mam_catchup.from_id]; + + Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params; + query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_before(target, latest_time, latest_id); + + yield fetch_query(account, query_params, latest_row[db.mam_catchup.id], cancellable); + } + public async void fetch_everything(Account account, Jid mam_server, Cancellable? cancellable = null, DateTime until_earliest_time = new DateTime.from_unix_utc(0)) { debug("Fetch everything for %s %s", mam_server.to_string(), until_earliest_time != null ? @"(until $until_earliest_time)" : ""); RowOption latest_row_opt = db.mam_catchup.select() diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index ea4de99b..43e593fe 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -203,6 +203,7 @@ SOURCES src/ui/contact_details/settings_provider.vala src/ui/contact_details/permissions_provider.vala + src/ui/contact_details/history_provider.vala src/ui/conversation_details.vala diff --git a/main/meson.build b/main/meson.build index 1b5abcfc..e9b37878 100644 --- a/main/meson.build +++ b/main/meson.build @@ -39,6 +39,7 @@ sources = files( 'src/ui/chat_input/view.vala', 'src/ui/contact_details/permissions_provider.vala', 'src/ui/contact_details/settings_provider.vala', + 'src/ui/contact_details/history_provider.vala', 'src/ui/conversation_content_view/call_widget.vala', 'src/ui/conversation_content_view/chat_state_populator.vala', 'src/ui/conversation_content_view/content_populator.vala', diff --git a/main/src/ui/contact_details/history_provider.vala b/main/src/ui/contact_details/history_provider.vala new file mode 100644 index 00000000..558ce3fd --- /dev/null +++ b/main/src/ui/contact_details/history_provider.vala @@ -0,0 +1,52 @@ +using Gee; +using Gtk; + +using Dino.Entities; + +using Xmpp; + +namespace Dino.Ui.ContactDetails { + +public class HistoryProvider : Plugins.ContactDetailsProvider, Object { + public string id { get { return "history_settings"; } } + + private StreamInteractor stream_interactor; + + private HashMap> sync_cancellables = new HashMap>(Account.hash_func, Account.equals_func); + + public HistoryProvider(StreamInteractor stream_interactor) { + this.stream_interactor = stream_interactor; + } + + public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { + if (type != Plugins.WidgetType.GTK4) return; + + EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); + + string RESYNC_LABEL = _("Resync"); + string RESYNC_DESC_LABEL = _("Fetch a complete MAM history for this chat"); + entity_info.has_feature.begin(conversation.account, conversation.counterpart, Xmpp.MessageArchiveManagement.NS_URI, (_, res) => { + bool can_do_mam = entity_info.has_feature.end(res); + if (can_do_mam) { + Button resync_button = new Button.with_label(RESYNC_LABEL); + contact_details.add("Permissions", RESYNC_DESC_LABEL, "", resync_button); + resync_button.clicked.connect(() => { + if (!sync_cancellables.has_key(conversation.account)) { + sync_cancellables[conversation.account] = new HashMap(); + } + + if (!sync_cancellables[conversation.account].has_key(conversation.counterpart.bare_jid)) { + sync_cancellables[conversation.account][conversation.counterpart.bare_jid] = new Cancellable(); + var history_sync = stream_interactor.get_module(MessageProcessor.IDENTITY).history_sync; + history_sync.fetch_history.begin(conversation.account, conversation.counterpart.bare_jid, sync_cancellables[conversation.account][conversation.counterpart.bare_jid], (_, res) => { + history_sync.fetch_everything.end(res); + sync_cancellables[conversation.account].unset(conversation.counterpart.bare_jid); + }); + } + }); + } + }); + } +} + +} \ No newline at end of file diff --git a/main/src/ui/conversation_details.vala b/main/src/ui/conversation_details.vala index 82d6ae54..a7942d4f 100644 --- a/main/src/ui/conversation_details.vala +++ b/main/src/ui/conversation_details.vala @@ -152,6 +152,7 @@ namespace Dino.Ui.ConversationDetails { Application app = GLib.Application.get_default() as Application; app.plugin_registry.register_contact_details_entry(new ContactDetails.SettingsProvider(stream_interactor)); app.plugin_registry.register_contact_details_entry(new ContactDetails.PermissionsProvider(stream_interactor)); + app.plugin_registry.register_contact_details_entry(new ContactDetails.HistoryProvider(stream_interactor)); foreach (Plugins.ContactDetailsProvider provider in app.plugin_registry.contact_details_entries) { provider.populate(conversation, contact_details, Plugins.WidgetType.GTK4); From ecb3fb0056ad7a34cd83d13f499c33bed8772d66 Mon Sep 17 00:00:00 2001 From: Konstantin Kuznetsov Date: Tue, 12 Mar 2024 16:17:39 +0300 Subject: [PATCH 2/2] Fetch MAM history for conversations without local mam catchup data --- libdino/src/service/history_sync.vala | 42 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/libdino/src/service/history_sync.vala b/libdino/src/service/history_sync.vala index eb24c7e2..60d78476 100644 --- a/libdino/src/service/history_sync.vala +++ b/libdino/src/service/history_sync.vala @@ -131,18 +131,24 @@ public class Dino.HistorySync { .single().row(); Row? latest_row = latest_row_opt.is_present() ? latest_row_opt.inner : null; - if (latest_row == null) { - warning("Failed to fetch history for %s, no mam catchup data", target.to_string()); - return; + int? db_id = null; + string? latest_id = null; + if (latest_row != null) { + // Local mam catchup data exists so we can filter messages based on the latest id + db_id = latest_row[db.mam_catchup.id]; + latest_id = latest_row[db.mam_catchup.from_id]; } DateTime latest_time = new DateTime.now(); - string latest_id = latest_row[db.mam_catchup.from_id]; - Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params; query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_before(target, latest_time, latest_id); - yield fetch_query(account, query_params, latest_row[db.mam_catchup.id], cancellable); + if (db_id == null) { + query_params.mam_server = account.bare_jid; + query_params.with = target; + } + + yield fetch_query(account, query_params, db_id, cancellable); } public async void fetch_everything(Account account, Jid mam_server, Cancellable? cancellable = null, DateTime until_earliest_time = new DateTime.from_unix_utc(0)) { @@ -346,7 +352,7 @@ public class Dino.HistorySync { * Iteratively fetches all pages returned for a query (until a PageResult other than MorePagesAvailable is returned) * @return The last PageRequestResult result **/ - private async PageRequestResult fetch_query(Account account, Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params, int db_id, Cancellable? cancellable = null) { + private async PageRequestResult fetch_query(Account account, Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params, int? db_id, Cancellable? cancellable = null) { debug("[%s | %s] Fetch query %s - %s", account.bare_jid.to_string(), query_params.mam_server.to_string(), query_params.start != null ? query_params.start.to_string() : "", query_params.end != null ? query_params.end.to_string() : ""); PageRequestResult? page_result = null; do { @@ -358,17 +364,21 @@ public class Dino.HistorySync { string earliest_mam_id = page_result.query_result.first; long earliest_mam_time = (long)mam_times[account][earliest_mam_id].to_unix(); - debug("Updating %s to %s, %s", query_params.mam_server.to_string(), earliest_mam_time.to_string(), earliest_mam_id); - var query = db.mam_catchup.update() - .with(db.mam_catchup.id, "=", db_id) - .set(db.mam_catchup.from_time, earliest_mam_time) - .set(db.mam_catchup.from_id, earliest_mam_id); + if (db_id != null) { + // Update local mam catchup data if it exists + debug("Updating %s to %s, %s", query_params.mam_server.to_string(), earliest_mam_time.to_string(), earliest_mam_id); + var query = db.mam_catchup.update() + .with(db.mam_catchup.id, "=", db_id) + .set(db.mam_catchup.from_time, earliest_mam_time) + .set(db.mam_catchup.from_id, earliest_mam_id); - if (page_result.page_result == PageResult.NoMoreMessages) { - // If the server doesn't have more messages, store that this range is at its end. - query.set(db.mam_catchup.from_end, true); + if (page_result.page_result == PageResult.NoMoreMessages) { + // If the server doesn't have more messages, store that this range is at its end. + query.set(db.mam_catchup.from_end, true); + } + query.perform(); } - query.perform(); + } while (page_result.page_result == PageResult.MorePagesAvailable); return page_result;