diff --git a/po/POTFILES.in b/po/POTFILES.in index 8e1e600a..7a7364d1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -62,6 +62,7 @@ src/client/conversation-viewer/conversation-web-view.vala src/client/dialogs/alert-dialog.vala src/client/dialogs/attachment-dialog.vala src/client/dialogs/certificate-warning-dialog.vala +src/client/dialogs/dialogs-problem-details-dialog.vala src/client/dialogs/password-dialog.vala src/client/dialogs/preferences-dialog.vala src/client/dialogs/upgrade-dialog.vala @@ -428,4 +429,5 @@ ui/main-window.ui ui/main-window-info-bar.ui ui/password-dialog.glade ui/preferences-dialog.ui +ui/problem-details-dialog.vala ui/upgrade_dialog.glade diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala index 516854ed..986fd008 100644 --- a/src/client/application/geary-controller.vala +++ b/src/client/application/geary-controller.vala @@ -891,7 +891,8 @@ public class GearyController : Geary.BaseObject { private void report_problem(Geary.ProblemReport report) { debug("Problem reported: %s", report.to_string()); - if (!(report.error is IOError.CANCELLED)) { + if (report.error == null || + !(report.error.thrown is IOError.CANCELLED)) { MainWindowInfoBar info_bar = new MainWindowInfoBar.for_problem(report); info_bar.retry.connect(on_retry_problem); this.main_window.show_infobar(info_bar); @@ -900,16 +901,23 @@ public class GearyController : Geary.BaseObject { private void update_account_status() { Geary.Account.Status effective_status = 0; - bool auth_error = false; + bool has_auth_error = false; + Geary.Account? service_problem_source = null; foreach (AccountContext context in this.accounts.values) { effective_status |= context.get_effective_status(); - auth_error |= context.authentication_failed; + if (effective_status.has_service_problem() && + service_problem_source == null) { + service_problem_source = context.account; + } + has_auth_error |= context.authentication_failed; } foreach (Gtk.Window window in this.application.get_windows()) { MainWindow? main = window as MainWindow; if (main != null) { - main.update_account_status(effective_status, auth_error); + main.update_account_status( + effective_status, has_auth_error, service_problem_source + ); } } } diff --git a/src/client/components/main-window-info-bar.vala b/src/client/components/main-window-info-bar.vala index 7c489064..183c0988 100644 --- a/src/client/components/main-window-info-bar.vala +++ b/src/client/components/main-window-info-bar.vala @@ -12,7 +12,7 @@ public class MainWindowInfoBar : Gtk.InfoBar { - private enum ResponseType { COPY, DETAILS, RETRY; } + private enum ResponseType { DETAILS, RETRY; } /** If reporting a problem, returns the problem report else null. */ public Geary.ProblemReport? report { get; private set; default = null; } @@ -27,12 +27,6 @@ public class MainWindowInfoBar : Gtk.InfoBar { [GtkChild] private Gtk.Label description; - [GtkChild] - private Gtk.Grid problem_details; - - [GtkChild] - private Gtk.TextView detail_text; - public MainWindowInfoBar.for_problem(Geary.ProblemReport report) { Gtk.MessageType type = Gtk.MessageType.WARNING; @@ -182,107 +176,13 @@ public class MainWindowInfoBar : Gtk.InfoBar { this.show_close_button = show_close; } - private string format_details() { - Geary.ServiceProblemReport? service_report = this.report as Geary.ServiceProblemReport; - Geary.AccountProblemReport? account_report = this.report as Geary.AccountProblemReport; - - StringBuilder details = new StringBuilder(); - details.append_printf( - "Geary version: %s\n", - GearyApplication.VERSION - ); - details.append_printf( - "GTK version: %u.%u.%u\n", - Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version() - ); - details.append_printf( - "Desktop: %s\n", - Environment.get_variable("XDG_CURRENT_DESKTOP") ?? "Unknown" - ); - details.append_printf( - "Problem type: %s\n", - this.report.problem_type.to_string() - ); - if (account_report != null) { - details.append_printf( - "Account type: %s\n", - account_report.account.service_provider.to_string() - ); - } - if (service_report != null) { - details.append_printf( - "Service type: %s\n", - service_report.service.protocol.to_string() - ); - details.append_printf( - "Service host: %s\n", - service_report.service.host - ); - } - if (this.report.error == null) { - details.append("No error reported"); - } else { - details.append_printf( - "Error type: %s\n", this.report.error.format_error_type() - ); - details.append_printf( - "Message: %s\n", this.report.error.thrown.message - ); - details.append("Back trace:\n"); - foreach (Geary.ErrorContext.StackFrame frame in - this.report.error.backtrace) { - details.append_printf(" - %s\n", frame.to_string()); - } - } - return details.str; - } - private void show_details() { - this.detail_text.buffer.text = format_details(); - - // Would love to construct the dialog in Builder, but we to - // construct the dialog manually since we can't adjust the - // Headerbar setting afterwards. If the user re-clicks on the - // Details button to re-show it, a whole bunch of GTK - // criticals are spewed and the dialog appears b0rked, so just - // do it from scratch ever time anyway. - bool use_header = Gtk.Settings.get_default().gtk_dialogs_use_header; - Gtk.DialogFlags flags = Gtk.DialogFlags.MODAL; - if (use_header) { - flags |= Gtk.DialogFlags.USE_HEADER_BAR; - } - Gtk.Dialog dialog = new Gtk.Dialog.with_buttons( - _("Details"), // same as the button - get_toplevel() as Gtk.Window, - flags, - null + Dialogs.ProblemDetailsDialog dialog = + new Dialogs.ProblemDetailsDialog.for_problem_report( + get_toplevel() as Gtk.Window, this.report ); - dialog.set_default_size(600, -1); - dialog.get_content_area().add(this.problem_details); - - Gtk.HeaderBar? header_bar = dialog.get_header_bar() as Gtk.HeaderBar; - use_header = (header_bar != null); - if (use_header) { - header_bar.show_close_button = true; - } else { - dialog.add_button(_("_Close"), Gtk.ResponseType.CLOSE); - } - - Gtk.Widget copy = dialog.add_button( - _("Copy to Clipboard"), ResponseType.COPY - ); - copy.tooltip_text = - _("Copy technical details to clipboard for pasting into an email or bug report"); - - - dialog.set_default_response(ResponseType.COPY); - dialog.response.connect(on_details_response); - dialog.show(); - copy.grab_focus(); - } - - private void copy_details() { - get_clipboard(Gdk.SELECTION_CLIPBOARD).set_text(format_details(), -1); + dialog.run(); + dialog.destroy(); } [GtkCallback] @@ -308,19 +208,4 @@ public class MainWindowInfoBar : Gtk.InfoBar { this.parent.remove(this); } - private void on_details_response(Gtk.Dialog dialog, int response) { - switch(response) { - case ResponseType.COPY: - copy_details(); - break; - - default: - // fml - dialog.get_content_area().remove(this.problem_details); - dialog.hide(); - break; - } - } - - } diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala index 9d7e3307..cd39e6aa 100644 --- a/src/client/components/main-window.vala +++ b/src/client/components/main-window.vala @@ -33,6 +33,7 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { public ConversationViewer conversation_viewer { get; private set; default = new ConversationViewer(); } public StatusBar status_bar { get; private set; default = new StatusBar(); } private MonitoredSpinner spinner = new MonitoredSpinner(); + [GtkChild] private Gtk.Box main_layout; [GtkChild] @@ -65,12 +66,16 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { [GtkChild] private Gtk.InfoBar service_problem_infobar; + [GtkChild] + private Gtk.Button service_problem_details; + [GtkChild] private Gtk.InfoBar cert_problem_infobar; [GtkChild] private Gtk.InfoBar auth_problem_infobar; + private Geary.Account? service_problem_account = null; /** Fired when the user requests an account status be retried. */ public signal void retry_service_problem(Geary.ClientService.Status problem); @@ -105,7 +110,8 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { /** Updates the window's account status info bars. */ public void update_account_status(Geary.Account.Status status, - bool auth_error) { + bool has_auth_error, + Geary.Account? service_problem) { // 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 @@ -120,12 +126,15 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { show_offline = true; } else if (status.has_service_problem()) { show_service = true; - } else if (auth_error) { + } else if (has_auth_error) { show_auth = true; } + this.service_problem_account = service_problem; + this.offline_infobar.set_visible(show_offline); this.service_problem_infobar.set_visible(show_service); + this.service_problem_details.set_visible(get_problem_service() != null); this.cert_problem_infobar.hide(); this.auth_problem_infobar.set_visible(show_auth); update_infobar_frame(); @@ -512,6 +521,18 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { } } + private Geary.ClientService? get_problem_service() { + Geary.ClientService? service = null; + if (this.service_problem_account != null) { + if (this.service_problem_account.incoming.last_error != null) { + service = this.service_problem_account.incoming; + } else if (this.service_problem_account.outgoing.last_error != null) { + service = this.service_problem_account.outgoing; + } + } + return service; + } + [GtkCallback] private bool on_focus_event() { on_shift_key(false); @@ -543,6 +564,22 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface { retry_service_problem(Geary.ClientService.Status.CONNECTION_FAILED); } + [GtkCallback] + private void on_service_problem_details() { + Geary.ClientService? service = get_problem_service(); + if (service != null) { + Dialogs.ProblemDetailsDialog dialog = + new Dialogs.ProblemDetailsDialog( + this, + service.last_error, + this.service_problem_account.information, + service.configuration + ); + dialog.run(); + dialog.destroy(); + } + } + [GtkCallback] private void on_cert_problem_retry() { this.cert_problem_infobar.hide(); diff --git a/src/client/dialogs/dialogs-problem-details-dialog.vala b/src/client/dialogs/dialogs-problem-details-dialog.vala new file mode 100644 index 00000000..c6608e53 --- /dev/null +++ b/src/client/dialogs/dialogs-problem-details-dialog.vala @@ -0,0 +1,104 @@ +/* + * Copyright 2019 Michael Gratton + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +/** + * Displays technical details when a problem has been reported. + */ +[GtkTemplate (ui = "/org/gnome/Geary/problem-details-dialog.ui")] +public class Dialogs.ProblemDetailsDialog : Gtk.Dialog { + + + private Geary.ErrorContext error; + private Geary.AccountInformation? account; + private Geary.ServiceInformation? service; + + [GtkChild] + private Gtk.TextView detail_text; + + + public ProblemDetailsDialog(Gtk.Window parent, + Geary.ErrorContext error, + Geary.AccountInformation? account, + Geary.ServiceInformation? service) { + Object(use_header_bar: 1); + set_default_size(600, -1); + + this.error = error; + this.account = account; + this.service = service; + + this.detail_text.buffer.text = format_details(); + } + + public ProblemDetailsDialog.for_problem_report(Gtk.Window parent, + Geary.ProblemReport report) { + Geary.ServiceProblemReport? service_report = + report as Geary.ServiceProblemReport; + Geary.AccountProblemReport? account_report = + report as Geary.AccountProblemReport; + this( + parent, + report.error, + account_report != null ? account_report.account : null, + service_report != null ? service_report.service : null + ); + } + + private string format_details() { + StringBuilder details = new StringBuilder(); + details.append_printf( + "Geary version: %s\n", + GearyApplication.VERSION + ); + details.append_printf( + "GTK version: %u.%u.%u\n", + Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version() + ); + details.append_printf( + "Desktop: %s\n", + Environment.get_variable("XDG_CURRENT_DESKTOP") ?? "Unknown" + ); + if (this.account != null) { + details.append_printf( + "Account type: %s\n", + this.account.service_provider.to_string() + ); + } + if (this.service != null) { + details.append_printf( + "Service type: %s\n", + this.service.protocol.to_string() + ); + details.append_printf( + "Service host: %s\n", + this.service.host + ); + } + if (this.error == null) { + details.append("No error reported"); + } else { + details.append_printf( + "Error type: %s\n", this.error.format_error_type() + ); + details.append_printf( + "Message: %s\n", this.error.thrown.message + ); + details.append("Back trace:\n"); + foreach (Geary.ErrorContext.StackFrame frame in + this.error.backtrace) { + details.append_printf(" - %s\n", frame.to_string()); + } + } + return details.str; + } + + [GtkCallback] + private void on_copy_clicked() { + get_clipboard(Gdk.SELECTION_CLIPBOARD).set_text(format_details(), -1); + } + +} diff --git a/src/client/meson.build b/src/client/meson.build index 6cb48042..0afae2c1 100644 --- a/src/client/meson.build +++ b/src/client/meson.build @@ -62,6 +62,7 @@ geary_client_vala_sources = files( 'dialogs/alert-dialog.vala', 'dialogs/attachment-dialog.vala', 'dialogs/certificate-warning-dialog.vala', + 'dialogs/dialogs-problem-details-dialog.vala', 'dialogs/password-dialog.vala', 'dialogs/preferences-dialog.vala', 'dialogs/upgrade-dialog.vala', diff --git a/ui/main-window-info-bar.ui b/ui/main-window-info-bar.ui index 68179278..f649291d 100644 --- a/ui/main-window-info-bar.ui +++ b/ui/main-window-info-bar.ui @@ -1,5 +1,5 @@ - + - - True - False - 18 - 18 - 18 - 18 - 6 - 12 - - - True - True - start - baseline - 12 - True - If the problem is serious or persists, please copy and send these details to the <a href="https://wiki.gnome.org/Apps/Geary/Contact">mailing list</a> or file a <a href="https://wiki.gnome.org/Apps/Geary/ReportingABug">new bug report</a>. - True - True - 0 - - - 0 - 0 - - - - - True - False - start - baseline - Details: - False - - - 0 - 1 - - - - - 600 - 200 - True - True - True - True - in - - - True - True - True - True - False - word - 6 - 6 - 6 - 6 - False - True - - - - - 0 - 2 - - - diff --git a/ui/main-window.ui b/ui/main-window.ui index e3c88a7e..87f6f94b 100644 --- a/ui/main-window.ui +++ b/ui/main-window.ui @@ -243,12 +243,12 @@ You will not be able to send or receive email until it is re-connected.6 end - - Retry - True + + Details True True - + View technical details about the error + True @@ -256,6 +256,21 @@ You will not be able to send or receive email until it is re-connected.0 + + + Retry + True + True + True + Retry connecting now + + + + True + True + 1 + + False @@ -340,10 +355,11 @@ Please check your Internet connection, the server configuration and try again.end - Retry + Check True True True + Check the security details for the connection @@ -436,10 +452,11 @@ Please check the server configuration and try again. end - Retry + Login True True True + Retry login, you will be prompted for your password diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml index 8f43542f..38fce5d8 100644 --- a/ui/org.gnome.Geary.gresource.xml +++ b/ui/org.gnome.Geary.gresource.xml @@ -35,6 +35,7 @@ main-window-info-bar.ui password-dialog.glade preferences-dialog.ui + problem-details-dialog.ui upgrade_dialog.glade geary.css diff --git a/ui/problem-details-dialog.ui b/ui/problem-details-dialog.ui new file mode 100644 index 00000000..0e7d2b88 --- /dev/null +++ b/ui/problem-details-dialog.ui @@ -0,0 +1,138 @@ + + + + + +