[polari/wip/bastianilso/error-handling: 17/19] Indicate server status in the roomList instead



commit ecd2634fffaa91fb2a15165d320e79f84df9665f
Author: Bastian Ilsø <bastianilso src gnome org>
Date:   Sun Aug 2 17:05:24 2015 +0200

    Indicate server status in the roomList instead
    
    Instead of displaying notifications when connecting to
    a server, use less attention grabbing status indicators in
    the roomlist for displaying status of the servers
    individually.

 src/appNotifications.js |   34 ----------
 src/application.js      |   38 ++++++++++-
 src/chatroomManager.js  |   10 +++
 src/mainWindow.js       |   24 -------
 src/roomList.js         |  164 ++++++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 201 insertions(+), 69 deletions(-)
---
diff --git a/src/appNotifications.js b/src/appNotifications.js
index 694c422..24d6009 100644
--- a/src/appNotifications.js
+++ b/src/appNotifications.js
@@ -89,40 +89,6 @@ const GridOutput = new Lang.Class({
     }
 });
 
-const ConnectingNotification = new Lang.Class({
-    Name: 'ConnectingNotification',
-    Extends: AppNotification,
-
-    _init: function(account) {
-        this.parent();
-
-        this._grid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL,
-                                    column_spacing: 12 });
-
-        this._grid.add(new Gtk.Spinner({ active: true }));
-
-        let text = _("Connecting to %s").format(account.display_name);
-        let label = new Gtk.Label({ label: text });
-        this._grid.add(label);
-
-        this.widget.add(this._grid);
-        this.widget.show_all();
-
-        let id = account.connect('notify::connection-status',
-                                 Lang.bind(this, this._onConnectionStatusChanged));
-        this.widget.connect('destroy',
-            function() {
-                account.disconnect(id);
-            });
-    },
-
-    _onConnectionStatusChanged: function(account) {
-        if (account.connection_status == Tp.ConnectionStatus.CONNECTING)
-            return;
-        this.close();
-    }
-});
-
 const NotificationQueue = new Lang.Class({
     Name: 'NotificationQueue',
 
diff --git a/src/application.js b/src/application.js
index b7ab206..318e949 100644
--- a/src/application.js
+++ b/src/application.js
@@ -13,7 +13,6 @@ const MainWindow = imports.mainWindow;
 const PasteManager = imports.pasteManager;
 const Utils = imports.utils;
 
-
 const MAX_RETRIES = 3;
 
 const ConnectionError = {
@@ -77,11 +76,19 @@ const Application = new Lang.Class({
             activate: Lang.bind(this, this._onLeaveCurrentRoom),
             create_hook: Lang.bind(this, this._leaveRoomCreateHook),
             accels: ['<Primary>w'] },
+          { name: 'reconnect-account',
+            parameter_type: GLib.VariantType.new('o') },
           { name: 'user-list',
             activate: Lang.bind(this, this._onToggleAction),
             create_hook: Lang.bind(this, this._userListCreateHook),
             state: GLib.Variant.new('b', false),
             accels: ['F9', '<Primary>u'] },
+          { name: 'reconnect-without-encryption',
+            activate: Lang.bind(this, this._onReconnectWithoutEncryption),
+            parameter_type: GLib.VariantType.new('o') },
+          { name: 'edit-connection',
+            activate: Lang.bind(this, this._onEditConnection),
+            parameter_type: GLib.VariantType.new('o') },
           { name: 'connections',
             activate: Lang.bind(this, this._onListConnections) },
           { name: 'preferences',
@@ -266,7 +273,6 @@ const Application = new Lang.Class({
           time: time,
           retry: 0,
           originalNick: account.nickname };
-
         this._pendingRequests[roomId] = requestData;
 
         this._ensureChannel(requestData);
@@ -299,7 +305,6 @@ const Application = new Lang.Class({
 
     _onEnsureChannel: function(req, res, requestData) {
         let account = req.account;
-
         try {
             req.ensure_channel_finish(res);
         } catch (e if e.matches(Tp.Error, Tp.Error.DISCONNECTED)) {
@@ -398,6 +403,33 @@ const Application = new Lang.Class({
             }));
     },
 
+    _onEditConnection: function(action, parameter) {
+        let accountPath = parameter.deep_unpack();
+        let factory = Tp.AccountManager.dup().get_factory();
+        let account = factory.ensure_account(accountPath, []);
+        let dialog = new Connections.ConnectionDetailsDialog(account);
+        dialog.widget.transient_for = this._window.window;
+        dialog.widget.connect('response', Lang.bind(this,
+            function(w, response) {
+                w.destroy();
+            }));
+        dialog.widget.show();
+    },
+
+    _onReconnectWithoutEncryption: function(action, parameter) {
+        let accountPath = parameter.deep_unpack();
+        let factory = Tp.AccountManager.dup().get_factory();
+        let account = factory.ensure_account(accountPath, []);
+
+        let sv = { port: GLib.Variant.new('u', 6667) };
+        sv['use-ssl'] = GLib.Variant.new('b', false);
+        let asv = GLib.Variant.new('a{sv}', sv);
+        account.update_parameters_vardict_async(asv, [],
+            Lang.bind(this, function(a, res) {
+                a.update_parameters_vardict_finish(res);
+            }));
+    },
+
     _onShowPreferences: function() {
     },
 
diff --git a/src/chatroomManager.js b/src/chatroomManager.js
index 6ec3b95..d43adba 100644
--- a/src/chatroomManager.js
+++ b/src/chatroomManager.js
@@ -65,6 +65,9 @@ const _ChatroomManager = new Lang.Class({
         let leaveAction = this._app.lookup_action('leave-room');
         leaveAction.connect('activate', Lang.bind(this, this._onLeaveActivated));
 
+        let reconnectAction = this._app.lookup_action('reconnect-account');
+        reconnectAction.connect('activate', Lang.bind(this, this._onReconnectAccountActivated));
+
         this._client = new Client(am, this);
 
         let filters = [];
@@ -150,6 +153,13 @@ const _ChatroomManager = new Lang.Class({
         action.activate(parameter);
     },
 
+    _onReconnectAccountActivated: function(action, parameter) {
+        let accountPath = parameter.deep_unpack();
+        let factory = Tp.AccountManager.dup().get_factory();
+        let account = factory.ensure_account(accountPath, []);
+        this._restoreSavedChannels(account);
+    },
+
     _onJoinActivated: function(action, parameter) {
         let [accountPath, channelName, ] = parameter.deep_unpack();
         let factory = Tp.AccountManager.dup().get_factory();
diff --git a/src/mainWindow.js b/src/mainWindow.js
index f53e483..1891e59 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -54,12 +54,6 @@ const MainWindow = new Lang.Class({
             Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
         );
 
-        this._accountsMonitor = AccountsMonitor.getDefault();
-        this._accountsMonitor.connect('account-status-changed',
-                                      Lang.bind(this, this._onAccountChanged));
-        this._accountsMonitor.connect('account-added',
-                                      Lang.bind(this, this._onAccountChanged));
-
         this._roomManager = ChatroomManager.getDefault();
         this._roomManager.connect('active-changed',
                                   Lang.bind(this, this._activeRoomChanged));
@@ -119,24 +113,6 @@ const MainWindow = new Lang.Class({
                                  GLib.Variant.new('ai', this._currentSize));
     },
 
-    _onAccountChanged: function(am, account) {
-        if (account.connection_status != Tp.ConnectionStatus.CONNECTING)
-            return;
-
-        if (account._connectingNotification)
-            return;
-
-        let app = Gio.Application.get_default();
-        let notification = new AppNotifications.ConnectingNotification(account);
-        app.notificationQueue.addNotification(notification);
-
-        account._connectingNotification = notification;
-        notification.widget.connect('destroy',
-            function() {
-                delete account._connectingNotification;
-            });
-    },
-
     _updateDecorations: function() {
         let layoutLeft = null;
         let layoutRight = null;
diff --git a/src/roomList.js b/src/roomList.js
index f7ee09f..05b7716 100644
--- a/src/roomList.js
+++ b/src/roomList.js
@@ -8,6 +8,8 @@ const Tp = imports.gi.TelepathyGLib;
 
 const ChatroomManager = imports.chatroomManager;
 const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Connections = imports.connections;
 
 const RoomRow = new Lang.Class({
     Name: 'RoomRow',
@@ -118,6 +120,156 @@ const RoomRow = new Lang.Class({
     }
 });
 
+const RoomListHeader = new Lang.Class({
+    Name: 'RoomListHeader',
+
+    _init: function(account) {
+        this._account = account;
+
+        this._networkMonitor = Gio.NetworkMonitor.get_default();
+
+        this._app = Gio.Application.get_default();
+        this.widget = new Gtk.Box({ margin_bottom: 4, spacing: 2, hexpand: true,
+                                     orientation: Gtk.Orientation.HORIZONTAL,
+                                     visible: true });
+        this.widget.get_style_context().add_class('room-list-header');
+
+        let label = new Gtk.Label({ xalign: 0, margin: 4,
+                                    hexpand: true,
+                                    max_width_chars: 15,
+                                    ellipsize: Pango.EllipsizeMode.END });
+        account.bind_property('display-name', label, 'label',
+                              GObject.BindingFlags.SYNC_CREATE);
+        this.widget.add(label);
+
+        this._iconStack = new Gtk.Stack({ vhomogeneous: true, valign: Gtk.Align.CENTER,
+                                   margin_end: 4 });
+        this._iconStack.transition_type = Gtk.StackTransitionType.CROSSFADE;
+
+        let errorIcon = new Gtk.Button({ image: new Gtk.Image({icon_name: 'dialog-error-symbolic'}),
+                                         halign: Gtk.Align.END });
+        errorIcon.get_style_context().remove_class('button');
+        errorIcon.connect('clicked', Lang.bind(this, function () {
+            this._popover.show_all();
+            }));
+        this._popover = new Gtk.Popover({ modal: true,
+                                          position: Gtk.PositionType.BOTTOM });
+        let popoverBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL,
+                                         margin: 12, spacing: 3 });
+        this._popoverLabel = new Gtk.Label({ wrap: true, max_width_chars: 30,
+                                             halign: Gtk.Align.START, xalign: 0 });
+        this._popoverTitle = new Gtk.Label({ wrap: true, max_width_chars: 30,
+                                             use_markup: true, xalign: 0,
+                                             halign: Gtk.Align.START });
+        this._popoverTitle.label = '<b>' + _("Connection Error") + '</b>';
+        this._popoverButton = new Gtk.Button({ valign: Gtk.Align.CENTER, hexpand: true,
+                                               margin_top: 15, halign: Gtk.Align.END });
+        this._popoverButton.connect('clicked', Lang.bind(this,
+            function() {
+                this._popover.hide();
+            }));
+        popoverBox.add(this._popoverTitle);
+        popoverBox.add(this._popoverLabel);
+        popoverBox.add(this._popoverButton);
+        this._popover.add(popoverBox);
+        this._popover.relative_to = errorIcon;
+
+        this._iconStack.add_named(errorIcon, 'error');
+
+        let connecting = new Gtk.Spinner({active: true, halign: Gtk.Align.START });
+        this._iconStack.add_named(connecting, 'connecting');
+
+        this._iconStack.add_named(new Gtk.Box({visible: false}), 'none');
+
+        let displayStatus = function(binding) {
+            let status = binding.source.connection_status;
+            let reason = binding.source.connection_status_reason;
+            binding.target.visible_child_name = this._evaluateStatus(status, reason);
+        }
+
+        this._account.connect('notify::connection-status', Lang.bind(this, 
this._updateConnectionStatusIcon));
+        this.widget.add(this._iconStack);
+        this.widget.show_all();
+
+        this._updateConnectionStatusIcon();
+
+    },
+
+    _updateConnectionStatusIcon: function() {
+        let status = this._account.connection_status;
+        let reason = this._account.connection_status_reason;
+        let child = 'none';
+
+        if (status == Tp.ConnectionStatus.CONNECTING) {
+            if (this._networkMonitor.network_available)
+                child = 'connecting';
+        } else if (status == Tp.ConnectionStatus.DISCONNECTED &&
+                   reason != Tp.ConnectionStatusReason.REQUESTED) {
+
+            child = 'error';
+            switch (this._account.connection_error) {
+
+                case Tp.error_get_dbus_name(Tp.Error.CONNECTION_REFUSED):
+                case Tp.error_get_dbus_name(Tp.Error.NETWORK_ERROR): {
+                    this._popoverLabel.label = _("Please check your connection details.")
+
+                    this._popoverButton.label =  _("Edit Connection");
+                    this._popoverButton.action_name = 'app.edit-connection';
+                    this._popoverButton.action_target = new GLib.Variant('o', 
this._account.get_object_path());
+                    break;
+                }
+
+                case Tp.error_get_dbus_name(Tp.Error.CERT_REVOKED):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_INSECURE):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_LIMIT_EXCEEDED):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_INVALID):
+                case Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_ERROR):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_NOT_PROVIDED):
+                case Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_NOT_AVAILABLE):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_UNTRUSTED):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_EXPIRED):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_NOT_ACTIVATED):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_HOSTNAME_MISMATCH):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_FINGERPRINT_MISMATCH):
+                case Tp.error_get_dbus_name(Tp.Error.CERT_SELF_SIGNED): {
+                    this._popoverLabel.label = _("The connection is not safe.");
+                    this._popoverButton.label =  _("Continue Anyway");
+                    this._popoverButton.action_name = 'app.reconnect-without-encryption';
+                    this._popoverButton.action_target = GLib.Variant.new('o', 
this._account.get_object_path());
+                    break;
+                }
+
+                case Tp.error_get_dbus_name(Tp.Error.AUTHENTICATION_FAILED): {
+                    this._popoverLabel.label = _("Authentication failed.");
+                    this._popoverButton.label = _("Try again");
+                    this._popoverButton.action_name = 'app.reconnect-account';
+                    this._popoverButton.action_target = GLib.Variant.new('o', 
this._account.get_object_path());
+                    break;
+                }
+
+                case Tp.error_get_dbus_name(Tp.Error.CONNECTION_FAILED):
+                case Tp.error_get_dbus_name(Tp.Error.CONNECTION_LOST):
+                case Tp.error_get_dbus_name(Tp.Error.CONNECTION_REPLACED):
+                case Tp.error_get_dbus_name(Tp.Error.SERVICE_BUSY): {
+                    this._popoverLabel.label = _("The server is busy.");
+                    this._popoverButton.label = _("Try again");
+                    this._popoverButton.action_name = 'app.reconnect-account';
+                    this._popoverButton.action_target = GLib.Variant.new('o', 
this._account.get_object_path());
+                    break;
+                }
+
+                default:
+                    this._popoverLabel.label = _("Failed to connect for an unknown reason.");
+                    this._popoverButton.label = _("Try again");
+                    this._popoverButton.action_name = 'app.reconnect-account';
+                    this._popoverButton.action_target = GLib.Variant.new('o', 
this._account.get_object_path());
+                    break;
+            }
+        }
+        this._iconStack.visible_child_name = child;
+    },
+});
+
 const RoomList = new Lang.Class({
     Name: 'RoomList',
 
@@ -132,6 +284,7 @@ const RoomList = new Lang.Class({
         this._selectedRows = 0;
         this._selectionMode = false;
 
+
         this.widget.connect('row-selected',
                             Lang.bind(this, this._onRowSelected));
 
@@ -148,6 +301,7 @@ const RoomList = new Lang.Class({
         this._leaveAction.connect('activate',
                                   Lang.bind(this, this._onLeaveActivated));
 
+
         let action;
         action = app.lookup_action('next-room');
         action.connect('activate', Lang.bind(this,
@@ -281,14 +435,8 @@ const RoomList = new Lang.Class({
         if (row.get_header())
             return;
 
-        let label = new Gtk.Label({ margin_bottom: 4, xalign: 0,
-                                    max_width_chars: 15,
-                                    ellipsize: Pango.EllipsizeMode.END });
-        label.get_style_context().add_class('room-list-header');
-
-        account.bind_property('display-name', label, 'label',
-                              GObject.BindingFlags.SYNC_CREATE);
-        row.set_header(label);
+        let roomListHeader = new RoomListHeader(account);
+        row.set_header(roomListHeader.widget);
     },
 
     _sort: function(row1, row2) {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]