[gnome-shell] extensionPrefs: Support extension updates



commit 075f4a5efcb1ed0a11f2e3720d261cb88f3be999
Author: Florian Müllner <fmuellner gnome org>
Date:   Sat Nov 30 15:20:04 2019 +0100

    extensionPrefs: Support extension updates
    
    Now that we have support for extension updates in the shell, we
    need some place to display the updates to the user.
    
    As we are establishing the Extensions app as the primary way for
    managing extensions, it's a natural place for that functionality.
    
    Show which extensions have updates available, and offer a log out
    button (so gnome-shell can apply the updates when logging back in).
    
    https://gitlab.gnome.org/GNOME/gnome-shell/issues/1968

 js/extensionPrefs/css/application.css     |   2 +
 js/extensionPrefs/main.js                 |  57 +++++++
 js/extensionPrefs/ui/extension-row.ui     |  17 +-
 js/extensionPrefs/ui/extensions-window.ui | 258 ++++++++++++++++++------------
 4 files changed, 233 insertions(+), 101 deletions(-)
---
diff --git a/js/extensionPrefs/css/application.css b/js/extensionPrefs/css/application.css
index 1a35205f1d..37105d0819 100644
--- a/js/extensionPrefs/css/application.css
+++ b/js/extensionPrefs/css/application.css
@@ -7,3 +7,5 @@
 .details-button.expanded:dir(rtl) image {
   -gtk-icon-transform: rotate(-0.25turn);
 }
+
+image.warning { color: @warning_color; }
diff --git a/js/extensionPrefs/main.js b/js/extensionPrefs/main.js
index f8ce4fb7ae..67ff6c5c15 100644
--- a/js/extensionPrefs/main.js
+++ b/js/extensionPrefs/main.js
@@ -86,6 +86,8 @@ var ExtensionsWindow = GObject.registerClass({
         'mainBox',
         'mainStack',
         'scrolledWindow',
+        'updatesBar',
+        'updatesLabel',
     ],
 }, class ExtensionsWindow extends Gtk.ApplicationWindow {
     _init(params) {
@@ -94,6 +96,7 @@ var ExtensionsWindow = GObject.registerClass({
         this._startupUuid = null;
         this._loaded = false;
         this._prefsDialog = null;
+        this._updatesCheckId = 0;
 
         this._mainBox.set_focus_vadjustment(this._scrolledWindow.vadjustment);
 
@@ -102,6 +105,10 @@ var ExtensionsWindow = GObject.registerClass({
         action.connect('activate', this._showAbout.bind(this));
         this.add_action(action);
 
+        action = new Gio.SimpleAction({ name: 'logout' });
+        action.connect('activate', this._logout.bind(this));
+        this.add_action(action);
+
         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
         this._settings.bind('disable-user-extensions',
             this._killSwitch, 'active',
@@ -219,6 +226,22 @@ var ExtensionsWindow = GObject.registerClass({
         aboutDialog.present();
     }
 
+    _logout() {
+        this.application.get_dbus_connection().call(
+            'org.gnome.SessionManager',
+            '/org/gnome/SessionManager',
+            'org.gnome.SessionManager',
+            'Logout',
+            new GLib.Variant('(u)', [0]),
+            null,
+            Gio.DBusCallFlags.NONE,
+            -1,
+            null,
+            (o, res) => {
+                o.call_finish(res);
+            });
+    }
+
     _buildErrorUI(row, exc) {
         let scroll = new Gtk.ScrolledWindow({
             hscrollbar_policy: Gtk.PolicyType.NEVER,
@@ -357,6 +380,8 @@ var ExtensionsWindow = GObject.registerClass({
         let extension = ExtensionUtils.deserializeExtension(newState);
         let row = this._findExtensionRow(uuid);
 
+        this._queueUpdatesCheck();
+
         // the extension's type changed; remove the corresponding row
         // and reset the variable to null so that we create a new row
         // below and add it to the appropriate list
@@ -403,6 +428,29 @@ var ExtensionsWindow = GObject.registerClass({
             this._systemList.add(row);
     }
 
+    _queueUpdatesCheck() {
+        if (this._updatesCheckId)
+            return;
+
+        this._updatesCheckId = GLib.timeout_add_seconds(
+            GLib.PRIORITY_DEFAULT, 1, () => {
+                this._checkUpdates();
+
+                this._updatesCheckId = 0;
+                return GLib.SOURCE_REMOVE;
+            });
+    }
+
+    _checkUpdates() {
+        let nUpdates = this._userList.get_children().filter(c => c.hasUpdate).length;
+
+        this._updatesLabel.label = Gettext.ngettext(
+            '%d extension will be updated on next login.',
+            '%d extensions will be updated on next login.e',
+            nUpdates).format(nUpdates);
+        this._updatesBar.visible = nUpdates > 0;
+    }
+
     _extensionsLoaded() {
         this._userList.visible = this._userList.get_children().length > 0;
         this._systemList.visible = this._systemList.get_children().length > 0;
@@ -412,6 +460,8 @@ var ExtensionsWindow = GObject.registerClass({
         else
             this._mainStack.visible_child_name = 'placeholder';
 
+        this._checkUpdates();
+
         if (this._startupUuid)
             this._showPrefs(this._startupUuid);
         this._startupUuid = null;
@@ -526,6 +576,7 @@ var ExtensionRow = GObject.registerClass({
         'descriptionLabel',
         'versionLabel',
         'authorLabel',
+        'updatesIcon',
         'revealButton',
         'revealer',
     ],
@@ -622,6 +673,10 @@ var ExtensionRow = GObject.registerClass({
         return this._extension.hasPrefs;
     }
 
+    get hasUpdate() {
+        return this._extension.hasUpdate || false;
+    }
+
     get type() {
         return this._extension.type;
     }
@@ -645,6 +700,8 @@ var ExtensionRow = GObject.registerClass({
         action.set_state(new GLib.Variant('b', state));
         action.enabled = this._canToggle();
 
+        this._updatesIcon.visible = this.hasUpdate;
+
         this._versionLabel.label = `${this.version}`;
         this._versionLabel.visible = this.version !== '';
 
diff --git a/js/extensionPrefs/ui/extension-row.ui b/js/extensionPrefs/ui/extension-row.ui
index c4f4e60507..f54ab3c760 100644
--- a/js/extensionPrefs/ui/extension-row.ui
+++ b/js/extensionPrefs/ui/extension-row.ui
@@ -13,8 +13,21 @@
         <child>
           <object class="GtkLabel" id="nameLabel">
             <property name="visible">True</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkImage" id="updatesIcon">
+            <property name="no_show_all">True</property>
+            <property name="icon_name">software-update-available-symbolic</property>
+            <style>
+              <class name="warning"/>>
+            </style>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
             <property name="hexpand">True</property>
-            <property name="halign">start</property>
           </object>
         </child>
         <child>
@@ -196,7 +209,7 @@
           <packing>
             <property name="left_attach">0</property>
             <property name="top_attach">1</property>
-            <property name="width">5</property>
+            <property name="width">7</property>
           </packing>
         </child>
       </object>
diff --git a/js/extensionPrefs/ui/extensions-window.ui b/js/extensionPrefs/ui/extensions-window.ui
index 3d211395f0..c288bb3711 100644
--- a/js/extensionPrefs/ui/extensions-window.ui
+++ b/js/extensionPrefs/ui/extensions-window.ui
@@ -100,144 +100,204 @@
       </object>
     </child>
     <child>
-      <object class="GtkStack" id="mainStack">
+      <object class="GtkBox">
         <property name="visible">True</property>
-        <property name="transition_type">crossfade</property>
+        <property name="orientation">vertical</property>
         <child>
-          <object class="GtkScrolledWindow" id="scrolledWindow">
+          <object class="GtkStack" id="mainStack">
             <property name="visible">True</property>
-            <property name="hscrollbar_policy">never</property>
+            <property name="transition_type">crossfade</property>
+            <property name="vexpand">True</property>
             <child>
-              <object class="GtkViewport">
+              <object class="GtkScrolledWindow" id="scrolledWindow">
                 <property name="visible">True</property>
+                <property name="hscrollbar_policy">never</property>
                 <child>
-                  <object class="GtkBox" id="mainBox">
+                  <object class="GtkViewport">
                     <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
-                    <property name="halign">center</property>
-                    <property name="margin">36</property>
-                    <property name="spacing">12</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible"
-                                  bind-source="userList"
-                                  bind-property="visible"
-                                  bind-flags="sync-create"/>
-                        <property name="halign">start</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Manually Installed</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBox" id="userList">
-                        <property name="visible">True</property>
-                        <property name="selection_mode">none</property>
-                        <property name="margin_bottom">24</property>
-                        <style>
-                          <class name="frame"/>
-                        </style>
-                      </object>
-                    </child>
                     <child>
-                      <object class="GtkLabel">
-                        <property name="visible"
-                                  bind-source="systemList"
-                                  bind-property="visible"
-                                  bind-flags="sync-create"/>
-                        <property name="halign">start</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Built-In</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBox" id="systemList">
+                      <object class="GtkBox" id="mainBox">
                         <property name="visible">True</property>
-                        <property name="selection_mode">none</property>
-                        <style>
-                          <class name="frame"/>
-                        </style>
+                        <property name="orientation">vertical</property>
+                        <property name="halign">center</property>
+                        <property name="margin">36</property>
+                        <property name="spacing">12</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible"
+                                      bind-source="userList"
+                                      bind-property="visible"
+                                      bind-flags="sync-create"/>
+                            <property name="halign">start</property>
+                            <property name="hexpand">True</property>
+                            <property name="label" translatable="yes">Manually Installed</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkListBox" id="userList">
+                            <property name="visible">True</property>
+                            <property name="selection_mode">none</property>
+                            <property name="margin_bottom">24</property>
+                            <style>
+                              <class name="frame"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible"
+                                      bind-source="systemList"
+                                      bind-property="visible"
+                                      bind-flags="sync-create"/>
+                            <property name="halign">start</property>
+                            <property name="hexpand">True</property>
+                            <property name="label" translatable="yes">Built-In</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkListBox" id="systemList">
+                            <property name="visible">True</property>
+                            <property name="selection_mode">none</property>
+                            <style>
+                              <class name="frame"/>
+                            </style>
+                          </object>
+                        </child>
                       </object>
                     </child>
                   </object>
                 </child>
               </object>
+              <packing>
+                <property name="name">main</property>
+              </packing>
             </child>
-          </object>
-          <packing>
-            <property name="name">main</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkBox">
-            <property name="visible">True</property>
-            <property name="orientation">vertical</property>
-            <property name="margin">32</property>
-            <property name="spacing">6</property>
-            <property name="valign">center</property>
             <child>
-              <object class="GtkImage">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="pixel_size">96</property>
-                <property name="icon_name">org.gnome.Extensions-symbolic</property>
+                <property name="orientation">vertical</property>
+                <property name="margin">32</property>
+                <property name="spacing">6</property>
+                <property name="valign">center</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="pixel_size">96</property>
+                    <property name="icon_name">org.gnome.Extensions-symbolic</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">No Installed Extensions</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                      <attribute name="scale" value="1.44"/>
+                    </attributes>
+                  </object>
+                </child>
               </object>
+              <packing>
+                <property name="name">placeholder</property>
+              </packing>
             </child>
             <child>
-              <object class="GtkLabel">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">No Installed Extensions</property>
-                <attributes>
-                  <attribute name="weight" value="bold"/>
-                  <attribute name="scale" value="1.44"/>
-                </attributes>
+                <property name="margin_left">100</property>
+                <property name="margin_right">100</property>
+                <property name="margin_top">100</property>
+                <property name="margin_bottom">60</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Something’s gone wrong</property>
+                    <attributes>
+                      <attribute name="scale" value="1.44"/>
+                    </attributes>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">We’re very sorry, but it was not possible to 
get the list of installed extensions. Make sure you are logged into GNOME and try again.</property>
+                    <property name="justify">center</property>
+                    <property name="wrap">True</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </object>
+                </child>
               </object>
+              <packing>
+                <property name="name">noshell</property>
+              </packing>
             </child>
           </object>
-          <packing>
-            <property name="name">placeholder</property>
-          </packing>
         </child>
         <child>
-          <object class="GtkBox">
-            <property name="visible">True</property>
-            <property name="margin_left">100</property>
-            <property name="margin_right">100</property>
-            <property name="margin_top">100</property>
-            <property name="margin_bottom">60</property>
-            <property name="orientation">vertical</property>
-            <property name="spacing">12</property>
+          <object class="GtkActionBar" id="updatesBar">
+            <property name="no_show_all">True</property>
             <child>
-              <object class="GtkLabel">
+              <object class="GtkImage">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">Something’s gone wrong</property>
-                <attributes>
-                  <attribute name="scale" value="1.44"/>
-                </attributes>
+                <property name="pixel-size">24</property>
+                <property name="margin">6</property>
+                <property name="icon_name">software-update-available-symbolic</property>
                 <style>
-                  <class name="dim-label"/>
+                  <class name="warning"/>>
                 </style>
               </object>
             </child>
             <child>
-              <object class="GtkLabel">
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="halign">start</property>
+                    <property name="label">Extension Updates Ready</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="updatesLabel">
+                    <property name="visible">True</property>
+                    <property name="halign">start</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton">
+                <property name="label" translatable="yes">Logout…</property>
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">We’re very sorry, but it was not possible to get 
the list of installed extensions. Make sure you are logged into GNOME and try again.</property>
-                <property name="justify">center</property>
-                <property name="wrap">True</property>
+                <property name="valign">center</property>
+                <property name="action-name">win.logout</property>
+                <property name="receives_default">True</property>
                 <style>
-                  <class name="dim-label"/>
+                  <class name="suggested-action"/>
                 </style>
               </object>
+              <packing>
+                <property name="pack_type">end</property>
+              </packing>
             </child>
           </object>
-          <packing>
-            <property name="name">noshell</property>
-          </packing>
         </child>
       </object>
     </child>


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