[gnome-software/wip/rancell/channels] Support snap channels
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/rancell/channels] Support snap channels
- Date: Thu, 23 Nov 2017 01:38:58 +0000 (UTC)
commit ec322a821d18c5f3bc359d09326b5d9078b26afd
Author: Robert Ancell <robert ancell canonical com>
Date: Thu Nov 23 10:54:20 2017 +1300
Support snap channels
lib/gs-app.c | 47 ++++++++++++++++
lib/gs-app.h | 4 +
lib/gs-channel.c | 116 +++++++++++++++++++++++++++++++++++++++
lib/gs-channel.h | 44 +++++++++++++++
lib/gs-plugin-job-private.h | 1 +
lib/gs-plugin-job.c | 31 ++++++++++
lib/gs-plugin-job.h | 2 +
lib/gs-plugin-loader.c | 13 ++++
lib/gs-plugin-types.h | 2 +
lib/gs-plugin-vfuncs.h | 18 ++++++
lib/gs-plugin.c | 2 +
lib/meson.build | 2 +
meson.build | 2 +-
plugins/dummy/gs-plugin-dummy.c | 11 ++++
plugins/snap/gs-plugin-snap.c | 62 +++++++++++++++++----
src/gs-details-page.c | 41 ++++++++++++++
src/gs-details-page.ui | 94 +++++++++++++++++++++++++------
src/gtk-style.css | 31 ++++++++++
18 files changed, 492 insertions(+), 31 deletions(-)
---
diff --git a/lib/gs-app.c b/lib/gs-app.c
index 9c69349..ba797d5 100644
--- a/lib/gs-app.c
+++ b/lib/gs-app.c
@@ -124,6 +124,7 @@ typedef struct
AsContentRating *content_rating;
GdkPixbuf *pixbuf;
GsPrice *price;
+ GPtrArray *channels;
GCancellable *cancellable;
} GsAppPrivate;
@@ -579,6 +580,14 @@ gs_app_to_string_append (GsApp *app, GString *str)
gs_app_kv_lpad (str, "keyword", tmp);
}
}
+ for (i = 0; i < priv->channels->len; i++) {
+ GsChannel *channel = g_ptr_array_index (priv->channels, i);
+ g_autofree gchar *key = NULL;
+ key = g_strdup_printf ("channel-%02u", i);
+ gs_app_kv_printf (str, key, "%s [%s]",
+ gs_channel_get_name (channel),
+ gs_channel_get_version (channel));
+ }
keys = g_hash_table_get_keys (priv->metadata);
for (l = keys; l != NULL; l = l->next) {
GVariant *val;
@@ -3811,6 +3820,42 @@ gs_app_get_priority (GsApp *app)
}
/**
+ * gs_app_add_channel:
+ * @app: a #GsApp
+ * @channel: a #GsChannel
+ *
+ * Adds a channel to the application.
+ *
+ * Since: 3.28
+ **/
+void
+gs_app_add_channel (GsApp *app, GsChannel *channel)
+{
+ GsAppPrivate *priv = gs_app_get_instance_private (app);
+ g_return_if_fail (GS_IS_APP (app));
+ g_return_if_fail (GS_IS_CHANNEL (channel));
+ g_ptr_array_add (priv->channels, g_object_ref (channel));
+}
+
+/**
+ * gs_app_get_channels:
+ * @app: a #GsApp
+ *
+ * Gets the list of channels.
+ *
+ * Returns: (element-type GsChannel) (transfer none): a list
+ *
+ * Since: 3.28
+ **/
+GPtrArray *
+gs_app_get_channels (GsApp *app)
+{
+ GsAppPrivate *priv = gs_app_get_instance_private (app);
+ g_return_val_if_fail (GS_IS_APP (app), NULL);
+ return priv->channels;
+}
+
+/**
* gs_app_get_cancellable:
* @app: a #GsApp
*
@@ -3958,6 +4003,7 @@ gs_app_dispose (GObject *object)
g_clear_pointer (&priv->reviews, g_ptr_array_unref);
g_clear_pointer (&priv->provides, g_ptr_array_unref);
g_clear_pointer (&priv->icons, g_ptr_array_unref);
+ g_clear_pointer (&priv->channels, g_ptr_array_unref);
G_OBJECT_CLASS (gs_app_parent_class)->dispose (object);
}
@@ -4136,6 +4182,7 @@ gs_app_init (GsApp *app)
priv->reviews = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
priv->provides = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
priv->icons = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ priv->channels = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
priv->metadata = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
diff --git a/lib/gs-app.h b/lib/gs-app.h
index 6876bf8..af5f2c5 100644
--- a/lib/gs-app.h
+++ b/lib/gs-app.h
@@ -27,6 +27,7 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <appstream-glib.h>
+#include "gs-channel.h"
#include "gs-price.h"
G_BEGIN_DECLS
@@ -317,6 +318,9 @@ void gs_app_remove_quirk (GsApp *app,
AsAppQuirk quirk);
gboolean gs_app_is_installed (GsApp *app);
gboolean gs_app_is_updatable (GsApp *app);
+GPtrArray *gs_app_get_channels (GsApp *app);
+void gs_app_add_channel (GsApp *app,
+ GsChannel *channel);
G_END_DECLS
#endif /* __GS_APP_H */
diff --git a/lib/gs-channel.c b/lib/gs-channel.c
new file mode 100644
index 0000000..658a33b
--- /dev/null
+++ b/lib/gs-channel.c
@@ -0,0 +1,116 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "gs-channel.h"
+
+struct _GsChannel
+{
+ GObject parent_instance;
+
+ gchar *name;
+ gchar *version;
+};
+
+G_DEFINE_TYPE (GsChannel, gs_channel, G_TYPE_OBJECT)
+
+/**
+ * gs_channel_get_name:
+ * @channel: a #GsChannel
+ *
+ * Get the channel name.
+ *
+ * Returns: a channel name.
+ *
+ * Since: 3.28
+ */
+const gchar *
+gs_channel_get_name (GsChannel *channel)
+{
+ g_return_val_if_fail (GS_IS_CHANNEL (channel), NULL);
+ return channel->name;
+}
+
+/**
+ * gs_channel_get_version:
+ * @channel: a #GsChannel
+ *
+ * Get the channel version.
+ *
+ * Returns: a channel version.
+ *
+ * Since: 3.28
+ */
+const gchar *
+gs_channel_get_version (GsChannel *channel)
+{
+ g_return_val_if_fail (GS_IS_CHANNEL (channel), NULL);
+ return channel->version;
+}
+
+static void
+gs_channel_finalize (GObject *object)
+{
+ GsChannel *channel = GS_CHANNEL (object);
+
+ g_free (channel->name);
+ g_free (channel->version);
+
+ G_OBJECT_CLASS (gs_channel_parent_class)->finalize (object);
+}
+
+static void
+gs_channel_class_init (GsChannelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gs_channel_finalize;
+}
+
+static void
+gs_channel_init (GsChannel *channel)
+{
+}
+
+/**
+ * gs_channel_new:
+ * @name: the name of the channel.
+ * @version: the version this channel is providing.
+ *
+ * Creates a new channel object.
+ *
+ * Return value: a new #GsChannel object.
+ *
+ * Since: 3.28
+ **/
+GsChannel *
+gs_channel_new (const gchar *name, const gchar *version)
+{
+ GsChannel *channel;
+ channel = g_object_new (GS_TYPE_CHANNEL, NULL);
+ channel->name = g_strdup (name);
+ channel->version = g_strdup (version);
+ return GS_CHANNEL (channel);
+}
+
+/* vim: set noexpandtab: */
diff --git a/lib/gs-channel.h b/lib/gs-channel.h
new file mode 100644
index 0000000..64610ab
--- /dev/null
+++ b/lib/gs-channel.h
@@ -0,0 +1,44 @@
+ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GS_CHANNEL_H
+#define __GS_CHANNEL_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_CHANNEL (gs_channel_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsChannel, gs_channel, GS, CHANNEL, GObject)
+
+GsChannel *gs_channel_new (const gchar *name,
+ const gchar *version);
+
+const gchar *gs_channel_get_name (GsChannel *channel);
+
+const gchar *gs_channel_get_version (GsChannel *channel);
+
+G_END_DECLS
+
+#endif /* __GS_CHANNEL_H */
+
+/* vim: set noexpandtab: */
diff --git a/lib/gs-plugin-job-private.h b/lib/gs-plugin-job-private.h
index f238213..414414d 100644
--- a/lib/gs-plugin-job-private.h
+++ b/lib/gs-plugin-job-private.h
@@ -53,6 +53,7 @@ GsPlugin *gs_plugin_job_get_plugin (GsPluginJob *self);
GsCategory *gs_plugin_job_get_category (GsPluginJob *self);
AsReview *gs_plugin_job_get_review (GsPluginJob *self);
GsPrice *gs_plugin_job_get_price (GsPluginJob *self);
+GsChannel *gs_plugin_job_get_channel (GsPluginJob *self);
gchar *gs_plugin_job_to_string (GsPluginJob *self);
void gs_plugin_job_set_action (GsPluginJob *self,
GsPluginAction action);
diff --git a/lib/gs-plugin-job.c b/lib/gs-plugin-job.c
index 2c98a6b..406268d 100644
--- a/lib/gs-plugin-job.c
+++ b/lib/gs-plugin-job.c
@@ -48,6 +48,7 @@ struct _GsPluginJob
GsCategory *category;
AsReview *review;
GsPrice *price;
+ GsChannel *channel;
gint64 time_created;
};
@@ -68,6 +69,7 @@ enum {
PROP_REVIEW,
PROP_MAX_RESULTS,
PROP_PRICE,
+ PROP_CHANNEL,
PROP_TIMEOUT,
PROP_LAST
};
@@ -128,6 +130,9 @@ gs_plugin_job_to_string (GsPluginJob *self)
g_autofree gchar *price_string = gs_price_to_string (self->price);
g_string_append_printf (str, " with price=%s", price_string);
}
+ if (self->channel != NULL) {
+ g_string_append_printf (str, " with channel=%s", gs_channel_get_name (self->channel));
+ }
if (self->auth != NULL) {
g_string_append_printf (str, " with auth=%s",
gs_auth_get_provider_id (self->auth));
@@ -452,6 +457,20 @@ gs_plugin_job_get_price (GsPluginJob *self)
return self->price;
}
+void
+gs_plugin_job_set_channel (GsPluginJob *self, GsChannel *channel)
+{
+ g_return_if_fail (GS_IS_PLUGIN_JOB (self));
+ g_set_object (&self->channel, channel);
+}
+
+GsChannel *
+gs_plugin_job_get_channel (GsPluginJob *self)
+{
+ g_return_val_if_fail (GS_IS_PLUGIN_JOB (self), NULL);
+ return self->channel;
+}
+
static void
gs_plugin_job_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec)
{
@@ -500,6 +519,9 @@ gs_plugin_job_get_property (GObject *obj, guint prop_id, GValue *value, GParamSp
case PROP_PRICE:
g_value_set_object (value, self->price);
break;
+ case PROP_CHANNEL:
+ g_value_set_object (value, self->channel);
+ break;
case PROP_MAX_RESULTS:
g_value_set_uint (value, self->max_results);
break;
@@ -566,6 +588,9 @@ gs_plugin_job_set_property (GObject *obj, guint prop_id, const GValue *value, GP
case PROP_PRICE:
gs_plugin_job_set_price (self, g_value_get_object (value));
break;
+ case PROP_CHANNEL:
+ gs_plugin_job_set_channel (self, g_value_get_object (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
@@ -585,6 +610,7 @@ gs_plugin_job_finalize (GObject *obj)
g_clear_object (&self->category);
g_clear_object (&self->review);
g_clear_object (&self->price);
+ g_clear_object (&self->channel);
G_OBJECT_CLASS (gs_plugin_job_parent_class)->finalize (obj);
}
@@ -678,6 +704,11 @@ gs_plugin_job_class_init (GsPluginJobClass *klass)
GS_TYPE_PRICE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_PRICE, pspec);
+
+ pspec = g_param_spec_object ("channel", NULL, NULL,
+ GS_TYPE_CHANNEL,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_CHANNEL, pspec);
}
static void
diff --git a/lib/gs-plugin-job.h b/lib/gs-plugin-job.h
index 2edd285..48c3fa3 100644
--- a/lib/gs-plugin-job.h
+++ b/lib/gs-plugin-job.h
@@ -72,6 +72,8 @@ void gs_plugin_job_set_review (GsPluginJob *self,
AsReview *review);
void gs_plugin_job_set_price (GsPluginJob *self,
GsPrice *price);
+void gs_plugin_job_set_channel (GsPluginJob *self,
+ GsChannel *channel);
#define gs_plugin_job_newv(a,...)
GS_PLUGIN_JOB(g_object_new(GS_TYPE_PLUGIN_JOB, "action", a, __VA_ARGS__))
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index daddf2c..845c984 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -128,6 +128,11 @@ typedef gboolean (*GsPluginPurchaseFunc) (GsPlugin *plugin,
GsPrice *price,
GCancellable *cancellable,
GError **error);
+typedef gboolean (*GsPluginSwitchChannelFunc) (GsPlugin *plugin,
+ GsApp *app,
+ GsChannel *channel,
+ GCancellable *cancellable,
+ GError **error);
typedef gboolean (*GsPluginReviewFunc) (GsPlugin *plugin,
GsApp *app,
AsReview *review,
@@ -616,6 +621,14 @@ gs_plugin_loader_call_vfunc (GsPluginLoaderHelper *helper,
cancellable, &error_local);
}
break;
+ case GS_PLUGIN_ACTION_SWITCH_CHANNEL:
+ {
+ GsPluginSwitchChannelFunc plugin_func = func;
+ ret = plugin_func (plugin, app,
+ gs_plugin_job_get_channel (helper->plugin_job),
+ cancellable, &error_local);
+ }
+ break;
case GS_PLUGIN_ACTION_REVIEW_SUBMIT:
case GS_PLUGIN_ACTION_REVIEW_UPVOTE:
case GS_PLUGIN_ACTION_REVIEW_DOWNVOTE:
diff --git a/lib/gs-plugin-types.h b/lib/gs-plugin-types.h
index 2804216..47911f6 100644
--- a/lib/gs-plugin-types.h
+++ b/lib/gs-plugin-types.h
@@ -267,6 +267,7 @@ typedef enum {
* @GS_PLUGIN_ACTION_INITIALIZE: Initialize the plugin
* @GS_PLUGIN_ACTION_DESTROY: Destroy the plugin
* @GS_PLUGIN_ACTION_PURCHASE: Purchase an app
+ * @GS_PLUGIN_ACTION_SWITCH_CHANNEL: Switch app channel
*
* The plugin action.
**/
@@ -314,6 +315,7 @@ typedef enum {
GS_PLUGIN_ACTION_INITIALIZE,
GS_PLUGIN_ACTION_DESTROY,
GS_PLUGIN_ACTION_PURCHASE,
+ GS_PLUGIN_ACTION_SWITCH_CHANNEL,
/*< private >*/
GS_PLUGIN_ACTION_LAST
} GsPluginAction;
diff --git a/lib/gs-plugin-vfuncs.h b/lib/gs-plugin-vfuncs.h
index f8d48e7..6144715 100644
--- a/lib/gs-plugin-vfuncs.h
+++ b/lib/gs-plugin-vfuncs.h
@@ -612,6 +612,24 @@ gboolean gs_plugin_app_install (GsPlugin *plugin,
GError **error);
/**
+ * gs_plugin_app_switch_channel:
+ * @plugin: a #GsPlugin
+ * @app: a #GsApp
+ * @channel: a #GsChannel
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Set the app chanel.
+ *
+ * Returns: %TRUE for success or if not relevant
+ **/
+gboolean gs_plugin_app_switch_channel (GsPlugin *plugin,
+ GsApp *app,
+ GsChannel *channel,
+ GCancellable *cancellable,
+ GError **error);
+
+/**
* gs_plugin_app_remove:
* @plugin: a #GsPlugin
* @app: a #GsApp
diff --git a/lib/gs-plugin.c b/lib/gs-plugin.c
index 9b1fe7a..61a06e3 100644
--- a/lib/gs-plugin.c
+++ b/lib/gs-plugin.c
@@ -1766,6 +1766,8 @@ gs_plugin_action_to_function_name (GsPluginAction action)
return "gs_plugin_destroy";
if (action == GS_PLUGIN_ACTION_PURCHASE)
return "gs_plugin_app_purchase";
+ if (action == GS_PLUGIN_ACTION_SWITCH_CHANNEL)
+ return "gs_plugin_app_switch_channel";
return NULL;
}
diff --git a/lib/meson.build b/lib/meson.build
index 47b71a9..c745554 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ install_headers([
'gs-app-list.h',
'gs-auth.h',
'gs-category.h',
+ 'gs-channel.h',
'gs-os-release.h',
'gs-plugin.h',
'gs-plugin-event.h',
@@ -76,6 +77,7 @@ libgnomesoftware = static_library(
'gs-app-list.c',
'gs-auth.c',
'gs-category.c',
+ 'gs-channel.c',
'gs-debug.c',
'gs-os-release.c',
'gs-plugin.c',
diff --git a/meson.build b/meson.build
index e305dfb..c4349a8 100644
--- a/meson.build
+++ b/meson.build
@@ -168,7 +168,7 @@ if get_option('enable-gudev')
endif
if get_option('enable-snap')
- snap = dependency('snapd-glib', version : '>= 1.19')
+ snap = dependency('snapd-glib', version : '>= 1.26')
endif
gnome = import('gnome')
diff --git a/plugins/dummy/gs-plugin-dummy.c b/plugins/dummy/gs-plugin-dummy.c
index 6962852..fe119d0 100644
--- a/plugins/dummy/gs-plugin-dummy.c
+++ b/plugins/dummy/gs-plugin-dummy.c
@@ -839,6 +839,17 @@ gs_plugin_refresh (GsPlugin *plugin,
}
gboolean
+gs_plugin_app_switch_channel (GsPlugin *plugin,
+ GsApp *app,
+ GsChannel *channel,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_debug ("Switching channel to %s", gs_channel_get_name (channel));
+ return TRUE;
+}
+
+gboolean
gs_plugin_app_upgrade_download (GsPlugin *plugin, GsApp *app,
GCancellable *cancellable, GError **error)
{
diff --git a/plugins/snap/gs-plugin-snap.c b/plugins/snap/gs-plugin-snap.c
index 5db7243..dd28983 100644
--- a/plugins/snap/gs-plugin-snap.c
+++ b/plugins/snap/gs-plugin-snap.c
@@ -47,10 +47,6 @@ get_client (GsPlugin *plugin, GError **error)
client = snapd_client_new ();
snapd_client_set_allow_interaction (client, TRUE);
-#ifndef SNAPD_GLIB_VERSION_1_24
- if (!snapd_client_connect_sync (client, NULL, error))
- return NULL;
-#endif
old_user_agent = snapd_client_get_user_agent (client);
user_agent = g_strdup_printf ("%s %s", gs_user_agent (), old_user_agent);
snapd_client_set_user_agent (client, user_agent);
@@ -306,6 +302,12 @@ find_snaps (GsPlugin *plugin, SnapdFindFlags flags, const gchar *section, const
return g_steal_pointer (&snaps);
}
+static gchar *
+make_version (SnapdSnap *snap)
+{
+ return g_strdup_printf ("%s (%s)", snapd_snap_get_version (snap), snapd_snap_get_revision (snap));
+}
+
static GsApp *
snap_to_app (GsPlugin *plugin, SnapdSnap *snap)
{
@@ -313,6 +315,12 @@ snap_to_app (GsPlugin *plugin, SnapdSnap *snap)
g_autofree gchar *unique_id = NULL;
GsApp *cached_app;
g_autoptr(GsApp) app = NULL;
+ g_autofree gchar *stable_version = NULL;
+ SnapdChannel *channel;
+ g_autoptr(GsChannel) stable_channel = NULL;
+ g_autoptr(GsChannel) candidate_channel = NULL;
+ g_autoptr(GsChannel) beta_channel = NULL;
+ g_autoptr(GsChannel) edge_channel = NULL;
switch (snapd_snap_get_snap_type (snap)) {
case SNAPD_SNAP_TYPE_APP:
@@ -347,6 +355,17 @@ snap_to_app (GsPlugin *plugin, SnapdSnap *snap)
if (priv->system_confinement == SNAPD_SYSTEM_CONFINEMENT_STRICT && snapd_snap_get_confinement (snap)
== SNAPD_CONFINEMENT_STRICT)
gs_app_add_kudo (app, GS_APP_KUDO_SANDBOXED);
+ stable_version = make_version (snap);
+ channel = snapd_snap_match_channel (snap, "stable");
+ stable_channel = gs_channel_new ("stable", stable_version);
+ gs_app_add_channel (app, stable_channel);
+ candidate_channel = gs_channel_new ("candidate", stable_version);
+ gs_app_add_channel (app, candidate_channel);
+ beta_channel = gs_channel_new ("beta", stable_version);
+ gs_app_add_channel (app, beta_channel);
+ edge_channel = gs_channel_new ("edge", stable_version);
+ gs_app_add_channel (app, edge_channel);
+
return g_steal_pointer (&app);
}
@@ -711,7 +730,7 @@ gs_plugin_refine_app (GsPlugin *plugin,
if (description != NULL)
gs_app_set_description (app, GS_APP_QUALITY_NORMAL, description);
gs_app_set_license (app, GS_APP_QUALITY_NORMAL, snapd_snap_get_license (local_snap));
- version = g_strdup_printf ("%s (%s)", snapd_snap_get_version (local_snap),
snapd_snap_get_revision (local_snap));
+ version = make_version (local_snap);
gs_app_set_version (app, version);
gs_app_set_size_installed (app, snapd_snap_get_installed_size (local_snap));
gs_app_set_install_date (app, g_date_time_to_unix (snapd_snap_get_install_date (local_snap)));
@@ -743,7 +762,7 @@ gs_plugin_refine_app (GsPlugin *plugin,
if (description != NULL)
gs_app_set_description (app, GS_APP_QUALITY_NORMAL, description);
gs_app_set_license (app, GS_APP_QUALITY_NORMAL, snapd_snap_get_license (store_snap));
- version = g_strdup_printf ("%s (%s)", snapd_snap_get_version (store_snap),
snapd_snap_get_revision (store_snap));
+ version = make_version (store_snap);
gs_app_set_version (app, version);
gs_app_set_size_download (app, snapd_snap_get_download_size (store_snap));
gs_app_set_developer_name (app, snapd_snap_get_developer (store_snap));
@@ -921,6 +940,31 @@ gs_plugin_launch (GsPlugin *plugin,
}
gboolean
+gs_plugin_app_switch_channel (GsPlugin *plugin,
+ GsApp *app,
+ GsChannel *channel,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(SnapdClient) client = NULL;
+
+ /* We can only modify apps we know of */
+ if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
+ return TRUE;
+
+ client = get_client (plugin, error);
+ if (client == NULL)
+ return FALSE;
+
+ if (!snapd_client_switch_sync (client, gs_app_get_id (app), gs_channel_get_name (channel),
progress_cb, app, cancellable, error)) {
+ snapd_error_convert (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
gs_plugin_app_remove (GsPlugin *plugin,
GsApp *app,
GCancellable *cancellable,
@@ -962,21 +1006,15 @@ gs_plugin_auth_login (GsPlugin *plugin, GsAuth *auth,
g_clear_object (&priv->auth_data);
if (priv->snapd_supports_polkit) {
g_autoptr(SnapdClient) client = NULL;
-#ifdef SNAPD_GLIB_VERSION_1_26
g_autoptr(SnapdUserInformation) user_information = NULL;
-#endif
client = get_client (plugin, error);
if (client == NULL)
return FALSE;
-#ifdef SNAPD_GLIB_VERSION_1_26
user_information = snapd_client_login2_sync (client, gs_auth_get_username (auth),
gs_auth_get_password (auth), gs_auth_get_pin (auth), NULL, error);
if (user_information != NULL)
priv->auth_data = g_object_ref (snapd_user_information_get_auth_data
(user_information));
-#else
- priv->auth_data = snapd_client_login_sync (client, gs_auth_get_username (auth),
gs_auth_get_password (auth), gs_auth_get_pin (auth), NULL, error);
-#endif
}
else
priv->auth_data = snapd_login_sync (gs_auth_get_username (auth), gs_auth_get_password (auth),
gs_auth_get_pin (auth), NULL, error);
diff --git a/src/gs-details-page.c b/src/gs-details-page.c
index d13932b..65e7814 100644
--- a/src/gs-details-page.c
+++ b/src/gs-details-page.c
@@ -107,6 +107,8 @@ struct _GsDetailsPage
GtkWidget *label_details_size_download_title;
GtkWidget *label_details_size_download_value;
GtkWidget *label_details_updated_value;
+ GtkWidget *label_details_channel_title;
+ GtkWidget *button_details_channel;
GtkWidget *label_details_version_value;
GtkWidget *label_failed;
GtkWidget *label_pending;
@@ -123,6 +125,7 @@ struct _GsDetailsPage
GtkWidget *spinner_remove;
GtkWidget *stack_details;
GtkWidget *grid_details_kudo;
+ GtkWidget *grid_popover_channel;
GtkWidget *image_details_kudo_docs;
GtkWidget *image_details_kudo_sandboxed;
GtkWidget *image_details_kudo_integration;
@@ -134,6 +137,7 @@ struct _GsDetailsPage
GtkWidget *label_details_kudo_translated;
GtkWidget *label_details_kudo_updated;
GtkWidget *progressbar_top;
+ GtkWidget *popover_channel;
GtkWidget *popover_license_free;
GtkWidget *popover_license_nonfree;
GtkWidget *popover_license_unknown;
@@ -240,6 +244,7 @@ gs_details_page_switch_to (GsPage *page, gboolean scroll_up)
GsPrice *price;
g_autofree gchar *text = NULL;
GtkStyleContext *sc;
+ GPtrArray *channels;
GtkAdjustment *adj;
if (gs_shell_get_mode (self->shell) != GS_SHELL_MODE_DETAILS) {
@@ -396,6 +401,10 @@ gs_details_page_switch_to (GsPage *page, gboolean scroll_up)
}
}
+ channels = gs_app_get_channels (self->app);
+ gtk_widget_set_visible (self->label_details_channel_title, channels->len > 0);
+ gtk_widget_set_visible (self->button_details_channel, channels->len > 0);
+
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (self->scrolledwindow_details));
gtk_adjustment_set_value (adj, gtk_adjustment_get_lower (adj));
@@ -1802,6 +1811,31 @@ gs_details_page_app_cancel_button_cb (GtkWidget *widget, GsDetailsPage *self)
}
static void
+gs_details_page_channel_cb (GtkWidget *widget, GsDetailsPage *self)
+{
+ GPtrArray *channels;
+ guint i;
+
+ gs_container_remove_all (GTK_CONTAINER (self->grid_popover_channel));
+ channels = gs_app_get_channels (self->app);
+ for (i = 0; i < channels->len; i++) {
+ GsChannel *channel = g_ptr_array_index (channels, i);
+ GtkWidget *label;
+
+ label = gtk_label_new (gs_channel_get_name (channel));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_widget_show (label);
+ gtk_grid_attach (GTK_GRID (self->grid_popover_channel), label, 0, i, 1, 1);
+ label = gtk_label_new (gs_channel_get_version (channel));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_widget_show (label);
+ gtk_grid_attach (GTK_GRID (self->grid_popover_channel), label, 1, i, 1, 1);
+ }
+
+ gtk_widget_show (self->popover_channel);
+}
+
+static void
gs_details_page_app_install_button_cb (GtkWidget *widget, GsDetailsPage *self)
{
GList *l;
@@ -2265,6 +2299,9 @@ gs_details_page_setup (GsPage *page,
g_signal_connect (self->button_donate, "clicked",
G_CALLBACK (gs_details_page_donate_cb),
self);
+ g_signal_connect (self->button_details_channel, "clicked",
+ G_CALLBACK (gs_details_page_channel_cb),
+ self);
g_signal_connect (self->button_details_license_free, "clicked",
G_CALLBACK (gs_details_page_license_free_cb),
self);
@@ -2361,6 +2398,10 @@ gs_details_page_class_init (GsDetailsPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage,
label_details_size_installed_title);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage,
label_details_size_installed_value);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, label_details_updated_value);
+ gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, label_details_channel_title);
+ gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, button_details_channel);
+ gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, popover_channel);
+ gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, grid_popover_channel);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, label_details_version_value);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, label_failed);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, label_pending);
diff --git a/src/gs-details-page.ui b/src/gs-details-page.ui
index 68a9fb9..df40ebb 100644
--- a/src/gs-details-page.ui
+++ b/src/gs-details-page.ui
@@ -780,6 +780,51 @@
<property name="row_spacing">9</property>
<property name="column_spacing">24</property>
<child>
+ <object class="GtkLabel" id="label_details_channel_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Channel</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="vexpand">True</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_details_channel_value">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkButton" id="button_details_channel">
+ <property name="label" translatable="no">stable</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">start</property>
+ <style>
+ <class name="details-channel"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkLabel" id="label_details_version_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -793,7 +838,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">0</property>
+ <property name="top_attach">1</property>
</packing>
</child>
<child>
@@ -812,7 +857,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">0</property>
+ <property name="top_attach">1</property>
</packing>
</child>
@@ -830,7 +875,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">8</property>
+ <property name="top_attach">9</property>
</packing>
</child>
<child>
@@ -850,7 +895,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">8</property>
+ <property name="top_attach">9</property>
</packing>
</child>
@@ -868,7 +913,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">1</property>
+ <property name="top_attach">2</property>
</packing>
</child>
<child>
@@ -886,7 +931,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">1</property>
+ <property name="top_attach">2</property>
</packing>
</child>
<child>
@@ -903,7 +948,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">2</property>
+ <property name="top_attach">3</property>
</packing>
</child>
<child>
@@ -923,7 +968,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">2</property>
+ <property name="top_attach">3</property>
</packing>
</child>
@@ -941,7 +986,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">6</property>
+ <property name="top_attach">7</property>
</packing>
</child>
<child>
@@ -956,7 +1001,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">6</property>
+ <property name="top_attach">7</property>
</packing>
</child>
@@ -974,7 +1019,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">7</property>
+ <property name="top_attach">8</property>
</packing>
</child>
<child>
@@ -989,7 +1034,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">7</property>
+ <property name="top_attach">8</property>
</packing>
</child>
@@ -1007,7 +1052,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">5</property>
+ <property name="top_attach">6</property>
</packing>
</child>
<child>
@@ -1023,7 +1068,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">5</property>
+ <property name="top_attach">6</property>
</packing>
</child>
<child>
@@ -1040,7 +1085,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">4</property>
+ <property name="top_attach">5</property>
</packing>
</child>
<child>
@@ -1057,7 +1102,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">4</property>
+ <property name="top_attach">5</property>
</packing>
</child>
<child>
@@ -1074,7 +1119,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">3</property>
+ <property name="top_attach">4</property>
</packing>
</child>
<child>
@@ -1136,7 +1181,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">3</property>
+ <property name="top_attach">4</property>
</packing>
</child>
</object>
@@ -1377,6 +1422,19 @@
<widget name="button_details_license_unknown"/>
</widgets>
</object>
+ <object class="GtkPopover" id="popover_channel">
+ <property name="can_focus">False</property>
+ <property name="border_width">21</property>
+ <property name="relative_to">button_details_channel</property>
+ <child>
+ <object class="GtkGrid" id="grid_popover_channel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">9</property>
+ <property name="column_spacing">12</property>
+ </object>
+ </child>
+ </object>
<object class="GtkPopover" id="popover_license_free">
<property name="can_focus">False</property>
<property name="border_width">21</property>
diff --git a/src/gtk-style.css b/src/gtk-style.css
index f6ddfaa..2c7facd 100644
--- a/src/gtk-style.css
+++ b/src/gtk-style.css
@@ -53,6 +53,37 @@
border-radius: 16px;
}
+.details-channel,
+.details-channel:backdrop {
+ outline-offset: 0;
+ background-image: none;
+ border-image: none;
+ border-radius: 4px;
+ border-width: 0 0 2px 0;
+ padding: 1px 9px;
+ box-shadow: none;
+ text-shadow: none;
+ color: #ffffff;
+}
+
+.details-channel label,
+.details-channel:backdrop label,
+.details-channel:hover label {
+ color: #fff;
+}
+
+.details-channel {
+ background-color: #4e9a06;
+ border-color: #3e7905;
+}
+.details-channel:hover {
+ background-color: #5db807;
+ border-color: #4d9606;
+}
+.details-channel:backdrop {
+ border-color: #4e9a06;
+}
+
.details-license-free,
.details-license-nonfree,
.details-license-unknown,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]