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
This commit is contained in:
Florian Müllner 2019-11-30 15:20:04 +01:00
parent f1bd94a367
commit 075f4a5efc
4 changed files with 242 additions and 110 deletions

View File

@ -7,3 +7,5 @@
.details-button.expanded:dir(rtl) image {
-gtk-icon-transform: rotate(-0.25turn);
}
image.warning { color: @warning_color; }

View File

@ -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 !== '';

View File

@ -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>

View File

@ -99,10 +99,15 @@
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkStack" id="mainStack">
<property name="visible">True</property>
<property name="transition_type">crossfade</property>
<property name="vexpand">True</property>
<child>
<object class="GtkScrolledWindow" id="scrolledWindow">
<property name="visible">True</property>
@ -241,5 +246,60 @@
</child>
</object>
</child>
<child>
<object class="GtkActionBar" id="updatesBar">
<property name="no_show_all">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="pixel-size">24</property>
<property name="margin">6</property>
<property name="icon_name">software-update-available-symbolic</property>
<style>
<class name="warning"/>>
</style>
</object>
</child>
<child>
<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="valign">center</property>
<property name="action-name">win.logout</property>
<property name="receives_default">True</property>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>