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 { .details-button.expanded:dir(rtl) image {
-gtk-icon-transform: rotate(-0.25turn); -gtk-icon-transform: rotate(-0.25turn);
} }
image.warning { color: @warning_color; }

View File

@ -86,6 +86,8 @@ var ExtensionsWindow = GObject.registerClass({
'mainBox', 'mainBox',
'mainStack', 'mainStack',
'scrolledWindow', 'scrolledWindow',
'updatesBar',
'updatesLabel',
], ],
}, class ExtensionsWindow extends Gtk.ApplicationWindow { }, class ExtensionsWindow extends Gtk.ApplicationWindow {
_init(params) { _init(params) {
@ -94,6 +96,7 @@ var ExtensionsWindow = GObject.registerClass({
this._startupUuid = null; this._startupUuid = null;
this._loaded = false; this._loaded = false;
this._prefsDialog = null; this._prefsDialog = null;
this._updatesCheckId = 0;
this._mainBox.set_focus_vadjustment(this._scrolledWindow.vadjustment); this._mainBox.set_focus_vadjustment(this._scrolledWindow.vadjustment);
@ -102,6 +105,10 @@ var ExtensionsWindow = GObject.registerClass({
action.connect('activate', this._showAbout.bind(this)); action.connect('activate', this._showAbout.bind(this));
this.add_action(action); 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 = new Gio.Settings({ schema_id: 'org.gnome.shell' });
this._settings.bind('disable-user-extensions', this._settings.bind('disable-user-extensions',
this._killSwitch, 'active', this._killSwitch, 'active',
@ -219,6 +226,22 @@ var ExtensionsWindow = GObject.registerClass({
aboutDialog.present(); 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) { _buildErrorUI(row, exc) {
let scroll = new Gtk.ScrolledWindow({ let scroll = new Gtk.ScrolledWindow({
hscrollbar_policy: Gtk.PolicyType.NEVER, hscrollbar_policy: Gtk.PolicyType.NEVER,
@ -357,6 +380,8 @@ var ExtensionsWindow = GObject.registerClass({
let extension = ExtensionUtils.deserializeExtension(newState); let extension = ExtensionUtils.deserializeExtension(newState);
let row = this._findExtensionRow(uuid); let row = this._findExtensionRow(uuid);
this._queueUpdatesCheck();
// the extension's type changed; remove the corresponding row // the extension's type changed; remove the corresponding row
// and reset the variable to null so that we create a new row // and reset the variable to null so that we create a new row
// below and add it to the appropriate list // below and add it to the appropriate list
@ -403,6 +428,29 @@ var ExtensionsWindow = GObject.registerClass({
this._systemList.add(row); 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() { _extensionsLoaded() {
this._userList.visible = this._userList.get_children().length > 0; this._userList.visible = this._userList.get_children().length > 0;
this._systemList.visible = this._systemList.get_children().length > 0; this._systemList.visible = this._systemList.get_children().length > 0;
@ -412,6 +460,8 @@ var ExtensionsWindow = GObject.registerClass({
else else
this._mainStack.visible_child_name = 'placeholder'; this._mainStack.visible_child_name = 'placeholder';
this._checkUpdates();
if (this._startupUuid) if (this._startupUuid)
this._showPrefs(this._startupUuid); this._showPrefs(this._startupUuid);
this._startupUuid = null; this._startupUuid = null;
@ -526,6 +576,7 @@ var ExtensionRow = GObject.registerClass({
'descriptionLabel', 'descriptionLabel',
'versionLabel', 'versionLabel',
'authorLabel', 'authorLabel',
'updatesIcon',
'revealButton', 'revealButton',
'revealer', 'revealer',
], ],
@ -622,6 +673,10 @@ var ExtensionRow = GObject.registerClass({
return this._extension.hasPrefs; return this._extension.hasPrefs;
} }
get hasUpdate() {
return this._extension.hasUpdate || false;
}
get type() { get type() {
return this._extension.type; return this._extension.type;
} }
@ -645,6 +700,8 @@ var ExtensionRow = GObject.registerClass({
action.set_state(new GLib.Variant('b', state)); action.set_state(new GLib.Variant('b', state));
action.enabled = this._canToggle(); action.enabled = this._canToggle();
this._updatesIcon.visible = this.hasUpdate;
this._versionLabel.label = `${this.version}`; this._versionLabel.label = `${this.version}`;
this._versionLabel.visible = this.version !== ''; this._versionLabel.visible = this.version !== '';

View File

@ -13,8 +13,21 @@
<child> <child>
<object class="GtkLabel" id="nameLabel"> <object class="GtkLabel" id="nameLabel">
<property name="visible">True</property> <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="hexpand">True</property>
<property name="halign">start</property>
</object> </object>
</child> </child>
<child> <child>
@ -196,7 +209,7 @@
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="width">5</property> <property name="width">7</property>
</packing> </packing>
</child> </child>
</object> </object>

View File

@ -100,144 +100,204 @@
</object> </object>
</child> </child>
<child> <child>
<object class="GtkStack" id="mainStack"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="transition_type">crossfade</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkScrolledWindow" id="scrolledWindow"> <object class="GtkStack" id="mainStack">
<property name="visible">True</property> <property name="visible">True</property>
<property name="hscrollbar_policy">never</property> <property name="transition_type">crossfade</property>
<property name="vexpand">True</property>
<child> <child>
<object class="GtkViewport"> <object class="GtkScrolledWindow" id="scrolledWindow">
<property name="visible">True</property> <property name="visible">True</property>
<property name="hscrollbar_policy">never</property>
<child> <child>
<object class="GtkBox" id="mainBox"> <object class="GtkViewport">
<property name="visible">True</property> <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> <child>
<object class="GtkLabel"> <object class="GtkBox" id="mainBox">
<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="visible">True</property>
<property name="selection_mode">none</property> <property name="orientation">vertical</property>
<property name="margin_bottom">24</property> <property name="halign">center</property>
<style> <property name="margin">36</property>
<class name="frame"/> <property name="spacing">12</property>
</style> <child>
</object> <object class="GtkLabel">
</child> <property name="visible"
<child> bind-source="userList"
<object class="GtkLabel"> bind-property="visible"
<property name="visible" bind-flags="sync-create"/>
bind-source="systemList" <property name="halign">start</property>
bind-property="visible" <property name="hexpand">True</property>
bind-flags="sync-create"/> <property name="label" translatable="yes">Manually Installed</property>
<property name="halign">start</property> <attributes>
<property name="hexpand">True</property> <attribute name="weight" value="bold"/>
<property name="label" translatable="yes">Built-In</property> </attributes>
<attributes> </object>
<attribute name="weight" value="bold"/> </child>
</attributes> <child>
</object> <object class="GtkListBox" id="userList">
</child> <property name="visible">True</property>
<child> <property name="selection_mode">none</property>
<object class="GtkListBox" id="systemList"> <property name="margin_bottom">24</property>
<property name="visible">True</property> <style>
<property name="selection_mode">none</property> <class name="frame"/>
<style> </style>
<class name="frame"/> </object>
</style> </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> </object>
</child> </child>
</object> </object>
</child> </child>
</object> </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">
<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="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>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Somethings 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">Were 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> </child>
</object> </object>
<packing>
<property name="name">main</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkActionBar" id="updatesBar">
<property name="visible">True</property> <property name="no_show_all">True</property>
<property name="orientation">vertical</property>
<property name="margin">32</property>
<property name="spacing">6</property>
<property name="valign">center</property>
<child> <child>
<object class="GtkImage"> <object class="GtkImage">
<property name="visible">True</property> <property name="visible">True</property>
<property name="pixel_size">96</property> <property name="pixel-size">24</property>
<property name="icon_name">org.gnome.Extensions-symbolic</property> <property name="margin">6</property>
</object> <property name="icon_name">software-update-available-symbolic</property>
</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="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>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Somethings gone wrong</property>
<attributes>
<attribute name="scale" value="1.44"/>
</attributes>
<style> <style>
<class name="dim-label"/> <class name="warning"/>>
</style> </style>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="label" translatable="yes">Were 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="orientation">vertical</property>
<property name="justify">center</property> <child>
<property name="wrap">True</property> <object class="GtkLabel">
<style> <property name="visible">True</property>
<class name="dim-label"/> <property name="halign">start</property>
</style> <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> </object>
</child> </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> </object>
<packing>
<property name="name">noshell</property>
</packing>
</child> </child>
</object> </object>
</child> </child>