From e7c6da94966bf4cf93c9e05eb1026eaebc945200 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Sun, 30 Dec 2018 22:40:04 +1100 Subject: [PATCH] Add initial support for showing/hiding account status as it changes Add infobars for offline, service problems, auth & cert problems. Show offline and service problem infobars as needed. --- src/client/application/geary-controller.vala | 68 +++- src/client/components/main-window.vala | 54 ++- ui/main-window.ui | 349 ++++++++++++++++++- 3 files changed, 453 insertions(+), 18 deletions(-) diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala index dda59ec8..3eb6a3d9 100644 --- a/src/client/application/geary-controller.vala +++ b/src/client/application/geary-controller.vala @@ -88,6 +88,29 @@ public class GearyController : Geary.BaseObject { this.store = new Geary.App.EmailStore(account); } + public Geary.Account.Status get_effective_status() { + Geary.Account.Status current = this.account.current_status; + Geary.Account.Status effective = 0; + if (current.is_online()) { + effective |= ONLINE; + } + if (current.has_service_problem()) { + // Only retain this flag if the problem isn't auth or + // cert related, that is handled elsewhere. + Geary.ClientService.Status incoming = + account.incoming.current_status; + Geary.ClientService.Status outgoing = + account.outgoing.current_status; + if (incoming != AUTHENTICATION_FAILED && + incoming != TLS_VALIDATION_FAILED && + outgoing != AUTHENTICATION_FAILED && + outgoing != TLS_VALIDATION_FAILED) { + effective |= SERVICE_PROBLEM; + } + } + return effective; + } + } @@ -601,6 +624,9 @@ public class GearyController : Geary.BaseObject { } private void open_account(Geary.Account account) { + account.notify["current-status"].connect( + on_account_status_notify + ); account.report_problem.connect(on_report_problem); connect_account_async.begin(account, cancellable_open_account); @@ -608,16 +634,18 @@ public class GearyController : Geary.BaseObject { account.contacts_loaded.connect(list_store.set_sort_function); } - private async void close_account(Geary.AccountInformation info) { - AccountContext? context = this.accounts.get(info); + private async void close_account(Geary.AccountInformation config) { + AccountContext? context = this.accounts.get(config); if (context != null) { - Geary.ContactStore contact_store = context.account.get_contact_store(); - ContactListStore list_store = this.contact_list_store_cache.get(contact_store); + Geary.Account account = context.account; + Geary.ContactStore contact_store = account.get_contact_store(); + ContactListStore list_store = + this.contact_list_store_cache.get(contact_store); - context.account.contacts_loaded.disconnect(list_store.set_sort_function); + account.contacts_loaded.disconnect(list_store.set_sort_function); this.contact_list_store_cache.unset(contact_store); - if (this.current_account == context.account) { + if (this.current_account == account) { this.current_account = null; previous_non_search_folder = null; @@ -626,9 +654,12 @@ public class GearyController : Geary.BaseObject { cancel_folder(); } - // Stop showing errors when closing the account - the user - // doesn't care - context.account.report_problem.disconnect(on_report_problem); + // Stop updating status and showing errors when closing + // the account - the user doesn't care any more + account.report_problem.disconnect(on_report_problem); + account.notify["current-status"].disconnect( + on_account_status_notify + ); yield disconnect_account_async(context); } @@ -858,6 +889,21 @@ public class GearyController : Geary.BaseObject { } } + private void update_account_status() { + Geary.Account.Status effective_status = + this.accounts.values.fold( + (ctx, status) => ctx.get_effective_status() | status, + 0 + ); + + foreach (Gtk.Window window in this.application.get_windows()) { + MainWindow? main = window as MainWindow; + if (main != null) { + main.update_account_status(effective_status); + } + } + } + private void on_retry_problem(MainWindowInfoBar info_bar) { Geary.ServiceProblemReport? service_report = info_bar.report as Geary.ServiceProblemReport; @@ -952,6 +998,10 @@ public class GearyController : Geary.BaseObject { report_problem(problem); } + private void on_account_status_notify() { + update_account_status(); + } + private void on_account_email_removed(Geary.Folder folder, Gee.Collection ids) { if (folder.special_folder_type == Geary.SpecialFolderType.OUTBOX) { main_window.status_bar.deactivate_message(StatusBar.Message.OUTBOX_SEND_FAILURE); diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala index dceb3f2a..25e83078 100644 --- a/src/client/components/main-window.vala +++ b/src/client/components/main-window.vala @@ -59,6 +59,19 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { [GtkChild] private Gtk.Grid info_bar_container; + [GtkChild] + private Gtk.InfoBar offline_infobar; + + [GtkChild] + private Gtk.InfoBar service_problem_infobar; + + [GtkChild] + private Gtk.InfoBar auth_problem_infobar; + + [GtkChild] + private Gtk.InfoBar cert_problem_infobar; + + /** Fired when the shift key is pressed or released. */ public signal void on_shift_key(bool pressed); @@ -87,6 +100,23 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { base_unref(); } + /** Updates the window's account status info bars. */ + public void update_account_status(Geary.Account.Status status) { + // Only ever show one at a time. Offline is primary since + // nothing else can happen when offline. Service problems are + // secondary since auth and cert problems can't be resolved + // when the service isn't talking to the server. Auth and cert + // problems are enabled elsewhere, since the controller might + // be already prompting the user about it. + this.offline_infobar.set_visible(!status.is_online()); + this.service_problem_infobar.set_visible( + status.is_online() && status.has_service_problem() + ); + this.auth_problem_infobar.hide(); + this.cert_problem_infobar.hide(); + update_infobar_frame(); + } + public void show_infobar(MainWindowInfoBar info_bar) { this.info_bar_container.add(info_bar); this.info_bar_frame.show(); @@ -443,6 +473,18 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { this.main_toolbar.folder = this.current_folder.get_display_name(); } + private void update_infobar_frame() { + // Ensure the info bar frame is shown only when it has visible + // children + bool show_frame = false; + info_bar_container.foreach((child) => { + if (child.visible) { + show_frame = true; + } + }); + this.info_bar_frame.set_visible(show_frame); + } + private inline void check_shift_event(Gdk.EventKey event) { // FIXME: it's possible the user will press two shift keys. We want // the shift key to report as released when they release ALL of them. @@ -474,13 +516,15 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { return Gdk.EVENT_STOP; } + [GtkCallback] + private void on_offline_infobar_response() { + this.offline_infobar.hide(); + update_infobar_frame(); + } + [GtkCallback] private void on_info_bar_container_remove() { - // Ensure the info bar frame is hidden when the last info bar - // is removed from the container. - if (this.info_bar_container.get_children().length() == 0) { - this.info_bar_frame.hide(); - } + update_infobar_frame(); } } diff --git a/ui/main-window.ui b/ui/main-window.ui index 367e7566..811b886f 100644 --- a/ui/main-window.ui +++ b/ui/main-window.ui @@ -1,5 +1,5 @@ - +