[polari/wip/dmoelgaard/initial-setup: 227/228] initial Setup



commit 9cd38f95a2ae7659c3e96d03981c7491d55d5755
Author: Danny Mølgaard <moelgaard dmp gmail com>
Date:   Mon Jan 16 20:02:25 2017 +0100

    initial Setup

 ...s-Change-message-button-label-in-userDeta.patch |   32 ++
 data/resources/init-setup.ui                       |  170 ++++--
 src/.goutputstream-AQH6QY                          |  563 ++++++++++++++++++++
 src/application.js                                 |   23 +-
 src/initSetup.js                                   |  121 ++++-
 src/networksManager.js                             |    9 +
 src/serverRoomManager.js                           |    1 +
 7 files changed, 828 insertions(+), 91 deletions(-)
---
diff --git a/0001-user-details-Change-message-button-label-in-userDeta.patch 
b/0001-user-details-Change-message-button-label-in-userDeta.patch
new file mode 100644
index 0000000..0126cb3
--- /dev/null
+++ b/0001-user-details-Change-message-button-label-in-userDeta.patch
@@ -0,0 +1,32 @@
+From 64664581bd4fb5fdedd800d52b717f1727117635 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Danny=20M=C3=B8lgaard?= <moelgaard dmp gmail com>
+Date: Thu, 10 Nov 2016 20:30:04 +0100
+Subject: [PATCH] user-details: Change message button label in userDetails
+
+'Message' can be both a noun and verb, thus resulting in confusion
+in terms of its translation to other languages. It also makes more
+sense to have the label correspond with the 'End Conversation'
+available in the sidebar's right-click menu.
+
+This commit changes the message button label in userDetails used by
+userPopover and userList from 'Message' to 'Start Conversation'.
+---
+ data/resources/user-details.ui | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/data/resources/user-details.ui b/data/resources/user-details.ui
+index 003768b..5be9597 100644
+--- a/data/resources/user-details.ui
++++ b/data/resources/user-details.ui
+@@ -178,7 +178,7 @@
+         </child>
+         <child>
+           <object class="GtkModelButton" id="messageButton">
+-            <property name="text" translatable="yes">Message</property>
++            <property name="text" translatable="yes">Start Conversation</property>
+             <property name="halign">fill</property>
+             <property name="hexpand">True</property>
+             <property name="visible">True</property>
+-- 
+2.7.4
+
diff --git a/data/resources/init-setup.ui b/data/resources/init-setup.ui
index e51afcd..d43b3d0 100644
--- a/data/resources/init-setup.ui
+++ b/data/resources/init-setup.ui
@@ -4,8 +4,6 @@
     <property name="icon-name">org.gnome.Polari</property>
     <property name="default-width">500</property>
     <property name="default-height">500</property>
-
-    <!-- HEADERBAR START -->
     <child type="titlebar">
       <object class="GtkBox">
         <property name="visible">True</property>
@@ -18,6 +16,7 @@
             <property name="title">Polari Setup</property>
             <child>
               <object class="GtkStack" id="leftHeaderStack">
+                <property name="hhomogeneous">False</property>
                 <property name="visible">True</property>
                 <property name="transition-type">crossfade</property>
                 <child>
@@ -54,6 +53,7 @@
             </child>
             <child>
               <object class="GtkStack" id="rightHeaderStack">
+                <property name="hhomogeneous">False</property>
                 <property name="visible">True</property>
                 <property name="transition-type">crossfade</property>
                 <child>
@@ -73,6 +73,9 @@
                     <property name="visible">True</property>
                     <property name="halign">end</property>
                     <property name="valign">center</property>
+                    <style>
+                      <class name="suggested-action"/>
+                    </style>
                   </object>
                   <packing>
                     <property name="name">doneButton</property>
@@ -87,50 +90,55 @@
         </child>
       </object>
     </child>
-    <!-- HEADERBAR END -->
-
-    <!-- CONTENT WINDOW START -->
     <child>
       <object class="GtkStack" id="contentStack">
         <property name="visible">True</property>
         <property name="transition-type">slide-left-right</property>
-        <!-- MAIN STACK PAGE 1 START -->
+        <property name="vexpand">True</property>
         <child>
           <object class="GtkBox">
             <property name="orientation">vertical</property>
             <property name="visible">True</property>
             <property name="valign">center</property>
+            <property name="margin">30</property>
+            <property name="margin-bottom">24</property>
+            <property name="spacing">25</property>
             <child>
-              <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="label">Welcome to Polari</property>
-                <style>
-                  <class name="polari-background-title"/>
-                </style>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLabel">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="label">Polari is an easy way to chat using IRC.</property>
-                <style>
-                  <class name="polari-background-description"/>
-                </style>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="label">Select a connection to get started.</property>
-                <style>
-                  <class name="polari-background-description"/>
-                </style>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label">Welcome to Polari</property>
+                    <style>
+                      <class name="polari-background-title"/>
+                    </style>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label">Polari is an easy way to chat using IRC.</property>
+                    <style>
+                      <class name="polari-background-description"/>
+                    </style>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label">Select a connection to get started.</property>
+                    <style>
+                      <class name="polari-background-description"/>
+                    </style>
+                  </object>
+                </child>
               </object>
             </child>
             <child>
               <object class="Gjs_InitSetupConnectionsList" id="connectionPageList">
                 <property name="visible">True</property>
-                <property name="vexpand">True</property>
               </object>
             </child>
           </object>
@@ -138,38 +146,95 @@
             <property name="name">connectionPage</property>
           </packing>
         </child>
-        <!-- MAIN STACK PAGE 1 END -->
-        <!-- MAIN STACK PAGE 2 START -->
         <child>
           <object class="GtkBox">
             <property name="orientation">vertical</property>
             <property name="visible">True</property>
             <property name="valign">center</property>
+            <property name="margin">30</property>
+            <property name="margin-bottom">24</property>
+            <property name="spacing">25</property>
             <child>
-              <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="label">Welcome to Polari</property>
-                <style>
-                  <class name="polari-background-title"/>
-                </style>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLabel">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="label">Select rooms you want to connect to. You can add more</property>
-                <style>
-                  <class name="polari-background-description"/>
-                </style>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label">Welcome to Polari</property>
+                    <style>
+                      <class name="polari-background-title"/>
+                    </style>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label">Select rooms you want to connect to. You can add more</property>
+                    <style>
+                      <class name="polari-background-description"/>
+                    </style>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label">connections and rooms later, by clicking the + button.</property>
+                    <style>
+                      <class name="polari-background-description"/>
+                    </style>
+                  </object>
+                </child>
               </object>
             </child>
             <child>
-              <object class="GtkLabel">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="label">connections and rooms later, by clicking the + button.</property>
-                <style>
-                  <class name="polari-background-description"/>
-                </style>
+                <property name="orientation">vertical</property>
+                <property name="spacing">18</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <style>
+                      <class name="linked"/>
+                      <class name="frame"/>
+                    </style>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">True</property>
+                        <style>
+                          <class name="polari-listbox-filterbar"/>
+                        </style>
+                        <child>
+                          <object class="GtkSearchEntry" id="nameEntry">
+                            <property name="visible">True</property>
+                            <property name="hexpand">True</property>
+                            <property name="margin">60</property>
+                            <property name="margin-top">6</property>
+                            <property name="margin-bottom">6</property>
+                            <property name="margin-end">22</property>
+                            <property name="placeholder-text" translatable="yes">Enter room name to 
add</property>
+                         </object>
+                       </child>
+                        <child>
+                          <object class="GtkSpinner" id="spinner">
+                            <property name="visible">True</property>
+                            <property name="margin-end">22</property>
+                          </object>
+                        </child>
+                      </object>
+                     </child>
+                    <child>
+                      <object class="Gjs_ServerRoomList" id="serverRoomList">
+                        <property name="min-content-height">274</property>
+                        <property name="visible">True</property>
+                        <property name="vexpand">True</property>
+                        <property name="hscrollbar-policy">never</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
               </object>
             </child>
           </object>
@@ -177,10 +242,7 @@
             <property name="name">roomPage</property>
           </packing>
         </child>
-        <!-- MAIN STACK PAGE 2 END -->
       </object>
     </child>
-    <!-- CONTENT WINDOW END -->
-
   </template>
 </interface>
diff --git a/src/.goutputstream-AQH6QY b/src/.goutputstream-AQH6QY
new file mode 100644
index 0000000..7aa1f16
--- /dev/null
+++ b/src/.goutputstream-AQH6QY
@@ -0,0 +1,563 @@
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Polari = imports.gi.Polari;
+const Tp = imports.gi.TelepathyGLib;
+
+const AccountsMonitor = imports.accountsMonitor;
+const AppNotifications = imports.appNotifications;
+const Connections = imports.connections;
+const Lang = imports.lang;
+const MainWindow = imports.mainWindow;
+const PasteManager = imports.pasteManager;
+const RoomManager = imports.roomManager;
+const TelepathyClient = imports.telepathyClient;
+const UserTracker = imports.userTracker;
+const Utils = imports.utils;
+const NetworksManager = imports.networksManager;
+const InitSetup = imports.initSetup;
+
+const MAX_RETRIES = 3;
+
+const IRC_SCHEMA_REGEX = /^(irc?:\/\/)([\da-z\.-]+):?(\d+)?\/(?:%23)?([\w\.\+-]+)/i;
+
+const Application = new Lang.Class({
+    Name: 'Application',
+    Extends: Gtk.Application,
+    Signals: { 'prepare-shutdown': {},
+               'room-focus-changed': {} },
+
+    _init: function() {
+        this.parent({ application_id: 'org.gnome.Polari',
+                      flags: Gio.ApplicationFlags.HANDLES_OPEN });
+
+        GLib.set_application_name('Polari');
+        GLib.set_prgname('org.gnome.Polari');
+        this._retryData = new Map();
+
+        this.add_main_option('start-client', 0,
+                             GLib.OptionFlags.NONE, GLib.OptionArg.NONE,
+                             _("Start Telephathy client"), null);
+        this.connect('handle-local-options', (o, dict) => {
+            try {
+                this.register(null);
+            } catch(e) {
+                return 1;
+            }
+
+            let v = dict.lookup_value('start-client', null);
+            if (v && v.get_boolean()) {
+                this.activate_action('start-client', null);
+                return 0;
+            }
+
+            return -1;
+        });
+    },
+
+    isRoomFocused: function(room) {
+        return this.active_window &&
+               this.active_window.is_active &&
+               this.active_window.active_room == room;
+    },
+
+    vfunc_startup: function() {
+        this.parent();
+
+        let actionEntries = [
+          { name: 'show-join-dialog',
+            activate: Lang.bind(this, this._onShowJoinDialog),
+            accels: ['<Primary>n'] },
+          { name: 'join-room',
+            activate: Lang.bind(this, this._onJoinRoom),
+            parameter_type: GLib.VariantType.new('(ssu)') },
+          { name: 'message-user',
+            activate: Lang.bind(this, this._onMessageUser),
+            parameter_type: GLib.VariantType.new('(sssu)') },
+          { name: 'leave-room',
+            parameter_type: GLib.VariantType.new('(ss)') },
+          { name: 'leave-current-room',
+            activate: Lang.bind(this, this._onLeaveCurrentRoom),
+            create_hook: (a) => { a.enabled = false; },
+            accels: ['<Primary>w'] },
+          { name: 'authenticate-account',
+            parameter_type: GLib.VariantType.new('(os)') },
+          { name: 'connect-account',
+            activate: Lang.bind(this, this._onConnectAccount),
+            parameter_type: GLib.VariantType.new('o') },
+          { name: 'reconnect-account',
+            activate: Lang.bind(this, this._onConnectAccount),
+            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: 'remove-connection',
+            activate: Lang.bind(this, this._onRemoveConnection),
+            parameter_type: GLib.VariantType.new('o') },
+          { name: 'edit-connection',
+            activate: Lang.bind(this, this._onEditConnection),
+            parameter_type: GLib.VariantType.new('o') },
+          { name: 'save-identify-password',
+            parameter_type: GLib.VariantType.new('o') },
+          { name: 'discard-identify-password',
+            parameter_type: GLib.VariantType.new('o') },
+          { name: 'start-client',
+            activate: Lang.bind(this, this._onStartClient) },
+          { name: 'help',
+            activate: Lang.bind(this, this._onShowHelp),
+            accels: ['F1'] },
+          { name: 'about',
+            activate: Lang.bind(this, this._onShowAbout) },
+          { name: 'quit',
+            activate: Lang.bind(this, this._onQuit),
+            accels: ['<Primary>q'] },
+          { name: 'next-room',
+            accels: ['<Primary>Page_Down', '<Alt>Down'] },
+          { name: 'previous-room',
+            accels: ['<Primary>Page_Up', '<Alt>Up'] },
+          { name: 'first-room',
+            accels: ['<Primary>Home'] },
+          { name: 'last-room',
+            accels: ['<Primary>End'] },
+          { name: 'nth-room',
+            parameter_type: GLib.VariantType.new('i') },
+          { name: 'next-pending-room',
+            accels: ['<Alt><Shift>Down', '<Primary><Shift>Page_Down']},
+          { name: 'previous-pending-room',
+            accels: ['<Alt><Shift>Up', '<Primary><Shift>Page_Up']}
+        ];
+        actionEntries.forEach(Lang.bind(this,
+            function(actionEntry) {
+                let props = {};
+                ['name', 'state', 'parameter_type'].forEach(
+                    function(prop) {
+                        if (actionEntry[prop])
+                            props[prop] = actionEntry[prop];
+                    });
+                let action = new Gio.SimpleAction(props);
+                if (actionEntry.create_hook)
+                    actionEntry.create_hook(action);
+                if (actionEntry.activate)
+                    action.connect('activate', actionEntry.activate);
+                if (actionEntry.change_state)
+                    action.connect('change-state', actionEntry.change_state);
+                if (actionEntry.accels)
+                    this.set_accels_for_action('app.' + actionEntry.name,
+                                               actionEntry.accels);
+                this.add_action(action);
+        }));
+
+        for (let i = 1; i < 10; i++)
+            this.set_accels_for_action('app.nth-room(%d)'.format(i), ['<Alt>' + i]);
+
+        this._telepathyClient = null;
+
+        this._roomManager = RoomManager.getDefault();
+        this._accountsMonitor = AccountsMonitor.getDefault();
+        this._userStatusMonitor = UserTracker.getUserStatusMonitor();
+        this._networksManager = NetworksManager.getDefault();
+
+        this._accountsMonitor.connect('account-status-changed',
+                                      Lang.bind(this, this._onAccountStatusChanged));
+
+        this.pasteManager = new PasteManager.PasteManager();
+        this.notificationQueue = new AppNotifications.NotificationQueue();
+        this.commandOutputQueue = new AppNotifications.CommandOutputQueue();
+    },
+
+    vfunc_activate: function() {
+        this.activate_action('start-client', null);
+
+        if (!this.active_window) {
+            if (true /*first time startup*/) {
+                while (true /*until setup is complete or cancelled*/) {
+                    let setupDialog = new InitSetup.InitSetup({ application: this });
+                    setupDialog.show_all();
+                }
+            }
+
+            let window = new MainWindow.MainWindow({ application: this });
+            window.connect('destroy',
+                           () => { this.emit('prepare-shutdown'); });
+            window.connect('notify::active-room',
+                           () => { this.emit('room-focus-changed'); });
+            window.connect('notify::is-active',
+                           () => { this.emit('room-focus-changed'); });
+            window.show_all();
+        }
+        this.active_window.present();
+    },
+
+    vfunc_window_added: function(window) {
+        this.parent(window);
+
+        let action = this.lookup_action('leave-current-room');
+        window.connect('notify::active-room', () => {
+            action.enabled = window.active_room != null;
+        });
+        action.enabled = window.active_room != null;
+
+        window.connect('active-room-state-changed',
+                       Lang.bind(this, this._updateUserListAction));
+        this._updateUserListAction();
+    },
+
+    vfunc_open: function(files) {
+        this.activate();
+
+        let time = Utils.getTpEventTime();
+        let uris = files.map(function(f) { return f.get_uri(); });
+
+        this._accountsMonitor.prepare(() => {
+            this._openURIs(uris, time);
+        });
+    },
+
+    _openURIs: function(uris, time) {
+        let map = {};
+
+        this._accountsMonitor.enabledAccounts.forEach(a => {
+            let params = a.dup_parameters_vardict().deep_unpack();
+            map[a.get_object_path()] = {
+                server: params.server.deep_unpack(),
+                service: a.service
+            };
+        });
+
+        let joinAction = this.lookup_action('join-room');
+        uris.forEach(Lang.bind(this, function(uri) {
+            let [success, server, port, room] = this._parseURI(uri);
+            if (!success)
+                return;
+
+            let matchedId = this._networksManager.findByServer(server);
+            let matches = Object.keys(map).filter(function(a) {
+                return GLib.ascii_strcasecmp(map[a].server, server) == 0 ||
+                       map[a].service == matchedId;
+            });
+
+            if (matches.length)
+                joinAction.activate(new GLib.Variant('(ssu)',
+                                [matches[0], '#' + room, time]));
+            else
+                this._createAccount(matchedId, server, port,
+                    function(a) {
+                        if (a)
+                            joinAction.activate(new GLib.Variant('(ssu)',
+                                            [a.get_object_path(),
+                                             '#' + room, time]));
+                    });
+        }));
+    },
+
+    _parseURI: function(uri) {
+        let server, port, room;
+        let success = false;
+        try {
+            [,, server, port, room] = uri.match(IRC_SCHEMA_REGEX);
+            success = true;
+        } catch(e) {
+            let label = _("Failed to open link");
+            let n = new AppNotifications.MessageNotification(label,
+                                                             'dialog-error-symbolic');
+            this.notificationQueue.addNotification(n);
+        }
+
+        return [success, server, port, room];
+    },
+
+    _createAccount: function(id, server, port, callback) {
+        let params, name;
+
+        if (id) {
+            params = this._networksManager.getNetworkDetails(id);
+            name = this._networksManager.getNetworkName(id);
+        } else {
+            params = {
+                'account': new GLib.Variant('s', GLib.get_user_name()),
+                'server': new GLib.Variant('s', server),
+                'port': new GLib.Variant('u', port ? port : 6667),
+                'use-ssl': new GLib.Variant('b', (port == 6697)),
+            };
+            name = server;
+        }
+
+        let req = new Tp.AccountRequest({ account_manager: Tp.AccountManager.dup(),
+                                          connection_manager: 'idle',
+                                          protocol: 'irc',
+                                          display_name: name });
+        req.set_enabled(true);
+
+        if (id)
+            req.set_service(id);
+
+        for (let prop in params)
+            req.set_parameter(prop, params[prop]);
+
+        req.create_account_async(Lang.bind(this,
+            function(r, res) {
+                let account = req.create_account_finish(res);
+                callback(account);
+            }));
+    },
+
+    _updateUserListAction: function() {
+        let room = this.active_window.active_room;
+        let action = this.lookup_action('user-list');
+        action.enabled = room && room.type == Tp.HandleType.ROOM && room.channel;
+    },
+
+    _userListCreateHook: function(action) {
+        action.connect('notify::enabled', function() {
+            if (!action.enabled)
+                action.change_state(GLib.Variant.new('b', false));
+        });
+        action.enabled = false;
+    },
+
+    _onShowJoinDialog: function() {
+        this.active_window.showJoinRoomDialog();
+    },
+
+    _maybePresent: function(time) {
+        let [present, ] = Tp.user_action_time_should_present(time);
+
+        if (!this.active_window || present)
+            this.activate();
+    },
+
+    _onJoinRoom: function(action, parameter) {
+        let [accountPath, channelName, time] = parameter.deep_unpack();
+        this._maybePresent(time);
+    },
+
+    _onMessageUser: function(action, parameter) {
+        let [accountPath, contactName, message, time] = parameter.deep_unpack();
+        this._maybePresent(time);
+    },
+
+    _ensureRetryData: function(account) {
+        let data = this._retryData.get(account.object_path);
+        if (data)
+            return data;
+
+        let params = Connections.getAccountParams(account);
+        let server = params['server'];
+        let accountName = params['account'];
+        let port = params['port'];
+        debug('Failed to connect to %s with username %s'.format(server, accountName));
+
+        let accountServers = [];
+        if (this._networksManager.getAccountIsPredefined(account))
+            accountServers = this._networksManager.getNetworkServers(account.service);
+
+        data = {
+            retry: 0,
+            originalAccountName: accountName,
+            alternateServers: accountServers.filter(s => s.address != server ||
+                                                         s.port != port)
+        };
+        this._retryData.set(account.object_path, data);
+        return data;
+    },
+
+    _restoreAccountName: function(account) {
+        let data = this._retryData.get(account.object_path);
+        if (!data || !data.retry || !data.originalAccountName)
+            return;
+
+        let params = { account: new GLib.Variant('s', data.originalAccountName) };
+        let asv = new GLib.Variant('a{sv}', params);
+        account.update_parameters_vardict_async(asv, [], null);
+        delete data.originalAccountName;
+    },
+
+    _retryWithParams: function(account, params) {
+        account.update_parameters_vardict_async(params, [], () => {
+            let presence = Tp.ConnectionPresenceType.AVAILABLE;
+            let msg = account.requested_status_message;
+            account.request_presence_async(presence, 'available', msg, null);
+        });
+    },
+
+    _retryNickRequest: function(account) {
+        let retryData = this._ensureRetryData(account);
+
+        if (retryData.retry++ >= MAX_RETRIES)
+            return false;
+
+        let oldParams = account.dup_parameters_vardict().deep_unpack();
+        let nick = oldParams['account'].deep_unpack();
+
+        debug('Retrying with nickname %s'.format(nick + '_'));
+        let params = { account: new GLib.Variant('s', nick + '_') };
+        this._retryWithParams(account, new GLib.Variant('a{sv}', params));
+        return true;
+    },
+
+    _retryServerRequest: function(account) {
+        let retryData = this._ensureRetryData(account);
+
+        let server = retryData.alternateServers.shift();
+        if (!server)
+            return false;
+
+        debug('Retrying with %s:%d'.format(server.address, server.port));
+        let params = { server: new GLib.Variant('s', server.address),
+                       port: new GLib.Variant('u', server.port),
+                       'use-ssl': new GLib.Variant('b', server.ssl) };
+        this._retryWithParams(account, new GLib.Variant('a{sv}', params));
+        return true;
+    },
+
+    _onAccountStatusChanged: function(mon, account) {
+        let status = account.connection_status;
+
+        if (status == Tp.ConnectionStatus.CONNECTING)
+            return;
+
+        if (status == Tp.ConnectionStatus.DISCONNECTED) {
+            let reason = account.connection_status_reason;
+
+            if (reason == Tp.ConnectionStatusReason.NAME_IN_USE)
+                if (this._retryNickRequest(account))
+                    return;
+
+            if (reason == Tp.ConnectionStatusReason.NETWORK_ERROR ||
+                reason == Tp.ConnectionStatusReason.NONE_SPECIFIED)
+                if (this._retryServerRequest(account))
+                    return;
+
+            if (reason != Tp.ConnectionStatusReason.REQUESTED) {
+                let strReasons = Object.keys(Tp.ConnectionStatusReason);
+                debug('Account %s disconnected with reason %s'.format(
+                      account.display_name, strReasons[reason]));
+            }
+        }
+
+        this._restoreAccountName(account);
+    },
+
+    _onLeaveCurrentRoom: function() {
+        let room = this.active_window.active_room;
+        if (!room)
+            return;
+        let action = this.lookup_action('leave-room');
+        action.activate(GLib.Variant.new('(ss)', [room.id, '']));
+    },
+
+    _onConnectAccount: function(action, parameter) {
+        let accountPath = parameter.deep_unpack();
+        let account = this._accountsMonitor.lookupAccount(accountPath);
+        if (account)
+            this._restoreAccountName(account);
+        this._retryData.delete(accountPath);
+    },
+
+    _onToggleAction: function(action) {
+        let state = action.get_state();
+        action.change_state(GLib.Variant.new('b', !state.get_boolean()));
+    },
+
+    _onRemoveConnection: function(action, parameter){
+        let accountPath = parameter.deep_unpack();
+        let account = this._accountsMonitor.lookupAccount(accountPath);
+        account.set_enabled_async(false, Lang.bind(this,
+            function() {
+                let label = _("%s removed.").format(account.display_name);
+                let n = new AppNotifications.UndoNotification(label);
+                this.notificationQueue.addNotification(n);
+
+                n.connect('closed', function() {
+                    account.remove_async(function(a, res) {
+                        a.remove_finish(res); // TODO: Check for errors
+                    });
+                });
+                n.connect('undo', function() {
+                    account.set_enabled_async(true, function(a, res) {
+                        a.set_enabled_finish(res); // TODO: Check for errors
+                    });
+                });
+            }));
+    },
+
+    _onEditConnection: function(action, parameter) {
+        let accountPath = parameter.deep_unpack();
+        let account = this._accountsMonitor.lookupAccount(accountPath);
+        let dialog = new Connections.ConnectionProperties(account);
+        dialog.transient_for = this.active_window;
+        dialog.connect('response', Lang.bind(this,
+            function(w, response) {
+                w.destroy();
+            }));
+        dialog.show();
+    },
+
+    _onStartClient: function() {
+        if (this._telepathyClient)
+            return;
+
+        let params = {
+            name: 'Polari',
+            account_manager: this._accountsMonitor.accountManager,
+            uniquify_name: false
+        };
+        this._telepathyClient = new TelepathyClient.TelepathyClient(params);
+    },
+
+    _onShowHelp: function() {
+        Utils.openURL('help:org.gnome.Polari', Gtk.get_current_event_time());
+    },
+
+    _onShowAbout: function() {
+        if (this._aboutDialog) {
+            this._aboutDialog.present();
+            return;
+        }
+        let aboutParams = {
+            authors: [
+                'Florian Müllner <fmuellner gnome org>',
+                'William Jon McCann <william jon mccann gmail com>',
+                'Carlos Soriano <carlos soriano89 gmail com>',
+                'Giovanni Campagna <gcampagna src gnome org>',
+                'Carlos Garnacho <carlosg gnome org>',
+                'Jonas Danielsson <jonas danielsson threetimestwo org>',
+                'Bastian Ilsø <bastianilso gnome org>',
+                'Kunaal Jain <kunaalus gmail com>',
+                'Cody Welsh <codyw protonmail com>',
+                'Isabella Ribeiro <belinhacbr gmail com>',
+                'Jonas Danielsson <jonas threetimestwo org>',
+                'Rares Visalom <rares visalom gmail com>',
+                'Danny Mølgaard <moelgaard dmp gmail com>'
+            ],
+            artists: [
+                'Sam Hewitt',
+                'Jakub Steiner <jimmac gmail com>'
+            ],
+            translator_credits: _("translator-credits"),
+            comments: _("An Internet Relay Chat Client for GNOME"),
+            copyright: 'Copyright © 2013-2015 The Polari authors',
+            license_type: Gtk.License.GPL_2_0,
+            logo_icon_name: 'org.gnome.Polari',
+            version: pkg.version,
+            website_label: _("Learn more about Polari"),
+            website: 'https://wiki.gnome.org/Apps/Polari',
+
+            transient_for: this.active_window,
+            modal: true
+        };
+
+        this._aboutDialog = new Gtk.AboutDialog(aboutParams);
+        this._aboutDialog.show();
+        this._aboutDialog.connect('response', Lang.bind(this, function() {
+            this._aboutDialog.destroy();
+            this._aboutDialog = null;
+        }));
+    },
+
+    _onQuit: function() {
+        this.get_windows().reverse().forEach(w => { w.destroy(); });
+    }
+});
diff --git a/src/application.js b/src/application.js
index 934fd0f..c509a5f 100644
--- a/src/application.js
+++ b/src/application.js
@@ -288,23 +288,18 @@ var Application = new Lang.Class({
                 this.emit('prepare-shutdown');
             });
 
-            if (true /*first time startup*/) {
+            if (/*firstTimeStartup*/true) {
                 let setupDialog = new InitSetup.InitSetup({ application: this });
                 setupDialog.show_all();
-            }
 
-/*            let window = new MainWindow.MainWindow({ application: this });
-            window.connect('destroy', () => {
-                if (this._settings.get_boolean('run-in-background'))
-                    return;
-                this.emit('prepare-shutdown');
-            });
-
-            window.connect('notify::active-room',
-                           () => { this.emit('room-focus-changed'); });
-            window.connect('notify::is-active',
-                           () => { this.emit('room-focus-changed'); });
-            window.show_all();*/
+                setupDialog.connect('destroy', () => {
+                window.connect('notify::active-room',
+                               () => { this.emit('room-focus-changed'); });
+                window.connect('notify::is-active',
+                               () => { this.emit('room-focus-changed'); });
+                window.show_all();
+                });
+            }
         }
         this.active_window.present();
     },
diff --git a/src/initSetup.js b/src/initSetup.js
index 20d8f59..51a510e 100644
--- a/src/initSetup.js
+++ b/src/initSetup.js
@@ -8,6 +8,7 @@ const Tp = imports.gi.TelepathyGLib;
 
 const Lang = imports.lang;
 const NetworksManager = imports.networksManager;
+const Utils = imports.utils;
 
 const CONFIGURE_TIMEOUT = 100; /* ms */
 
@@ -16,9 +17,6 @@ const SetupPage = {
     ROOM: 1
 };
 
-
-// SETUP WINDOW CONFIG
-
 const InitSetup = new Lang.Class({
     Name: 'InitSetup',
     Extends: Gtk.Window,
@@ -30,13 +28,17 @@ const InitSetup = new Lang.Class({
                         'nextButton',
                         'doneButton',
                         'contentStack',
-                        'connectionPageList' ],
+                        'connectionPageList',
+                        'serverRoomList',
+                        'nameEntry',
+                        'spinner' ],
 
     _init: function(params) {
 
         this.parent(params);
 
-        // Allow css styling
+        this._currentAccount = null;
+
         this._addApplicationStyle();
 
         this._setupConnectionPage();
@@ -70,21 +72,26 @@ const InitSetup = new Lang.Class({
                 this.destroy();
             }));
 
-        this._connectionPageList.connect('row-selected', Lang.bind(this,
-            function() {
+        this._connectionPageList.connect('account-created', Lang.bind(this,
+            function(w, account) {
                 this._setPage(SetupPage.ROOM);
+                this._currentAccount = account;
+                this._serverRoomList.setAccount(account);
             }));
     },
 
     _setPage: function(page) {
         let isConnection = page == SetupPage.CONNECTION;
 
+        if (!isConnection)
+            this._nameEntry.grab_focus();
+
         this._contentStack.visible_child_name = isConnection ? 'connectionPage'
-                                                       : 'roomPage';
+                                                             : 'roomPage';
         this._leftHeaderStack.visible_child_name = isConnection ? 'cancelButton'
-                                                          : 'backButton';
+                                                                : 'backButton';
         this._rightHeaderStack.visible_child_name = isConnection ? 'nextButton'
-                                                           : 'doneButton';
+                                                                 : 'doneButton';
     },
 
     _setupRoomPage: function() {
@@ -92,15 +99,66 @@ const InitSetup = new Lang.Class({
             function() {
                 this._setPage(SetupPage.CONNECTION);
             }));
+
+        this._doneButton.connect('clicked', Lang.bind(this,
+            function() {
+                this._joinRoom();
+                this.destroy();
+            }));
+
+        this._doneButton.sensitive = false;
+
+        this._nameEntry.connect('changed',
+                                Lang.bind(this, this._updateCanJoin));
+        this._serverRoomList.connect('notify::can-join',
+                                     Lang.bind(this, this._updateCanJoin));
+        this._serverRoomList.bind_property('loading', this._spinner, 'active',
+                                            GObject.BindingFlags.SYNC_CREATE);
+    },
+
+    get _page() {
+        if (this._contentStack.visible_child_name == 'roomPage')
+            return SetupPage.ROOM;
+        else
+            return SetupPage.MAIN;
+    },
+
+    _updateCanJoin: function() {
+        let sensitive = false;
+
+        if (this._page == SetupPage.ROOM)
+            sensitive = this._serverRoomList.can_join;
+
+        this._doneButton.sensitive = sensitive;
+    },
+
+    _joinRoom: function() {
+        this.hide();
+
+        let toJoinRooms = this._serverRoomList.selectedRooms;
+        if (this._nameEntry.get_text_length() > 0)
+            toJoinRooms.push(this._nameEntry.get_text());
+
+        let account = this._currentAccount;
+        toJoinRooms.forEach(function(room) {
+            if (room[0] != '#')
+                room = '#' + room;
+
+            let app = Gio.Application.get_default();
+            let action = app.lookup_action('join-room');
+            action.activate(GLib.Variant.new('(ssu)',
+                                             [ account.get_object_path(),
+                                             room,
+                                             Utils.getTpEventTime() ]));
+        });
     }
 
 });
 
-// CONNECTION LIST SETUP
 const InitSetupConnectionsList = new Lang.Class({
     Name: 'InitSetupConnectionsList',
     Extends: Gtk.Frame,
-    Signals: { 'row-selected' : {} },
+    Signals: { 'account-created': { param_types: [Tp.Account.$gtype] } },
 
     _init: function(params) {
         this.parent(params);
@@ -126,20 +184,33 @@ const InitSetupConnectionsList = new Lang.Class({
     },
 
     _networksChanged: function() {
-
-        let networkList = this._networksManager.networks;
-        for (let n of networkList) {
-            if (this._networksManager.getNetworkIsFavorite(n.id)) {
-                this._rows.set(n.id,
-                               new InitSetupConnectionRow({ id: n.id }));
-                this._list.add(this._rows.get(n.id));
-            }
+        for (let network of this._networksManager.favoriteNetworks) {
+            this._rows.set(network.id,
+                           new InitSetupConnectionRow({ id: network.id }));
+            this._list.add(this._rows.get(network.id));
         }
     },
 
-    _onRowActivated: function() {
-        this.emit('row-selected');
-    },
+    _onRowActivated: function(list, row) {
+        let name = this._networksManager.getNetworkName(row.id);
+        let req = new Tp.AccountRequest({ account_manager: Tp.AccountManager.dup(),
+                                          connection_manager: 'idle',
+                                          protocol: 'irc',
+                                          display_name: name });
+        req.set_service(row.id);
+        req.set_enabled(true);
+
+        let details = this._networksManager.getNetworkDetails(row.id);
+
+        for (let prop in details)
+            req.set_parameter(prop, details[prop]);
+
+        req.create_account_async(Lang.bind(this,
+            function(r, res) {
+                let account = req.create_account_finish(res);
+                this.emit('account-created', account);
+            }));
+    }
 });
 
 const InitSetupConnectionRow = new Lang.Class({
@@ -164,4 +235,8 @@ const InitSetupConnectionRow = new Lang.Class({
         this.show_all();
     },
 
+    get id() {
+        return this._id;
+    }
+
 });
diff --git a/src/networksManager.js b/src/networksManager.js
index f69c8cc..5a0d3c5 100644
--- a/src/networksManager.js
+++ b/src/networksManager.js
@@ -70,6 +70,15 @@ var NetworksManager = new Lang.Class({
         return this._networks;
     },
 
+    get favoriteNetworks() {
+        let favNetworks = [ ];
+        for (let network of this._networks) {
+            if (this.getNetworkIsFavorite(network.id))
+                favNetworks.push(network);
+        }
+        return favNetworks;
+    },
+
     getAccountIsPredefined: function(account) {
         return account && this._networksById.get(account.service) != null;
     },
diff --git a/src/serverRoomManager.js b/src/serverRoomManager.js
index e15c95d..1a89510 100644
--- a/src/serverRoomManager.js
+++ b/src/serverRoomManager.js
@@ -77,6 +77,7 @@ var _ServerRoomManager = new Lang.Class({
         roomList.connect('got-room', Lang.bind(this, this._onGotRoom));
         roomList.connect('notify::listing',
                          Lang.bind(this, this._onListingChanged));
+
         this._roomLists.set(account, { list: roomList, rooms: [] });
     },
 



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