Commit 122c8430 authored by Robert Ancell's avatar Robert Ancell

Support snap channels

parent a5ce4781
......@@ -126,6 +126,8 @@ typedef struct
AsContentRating *content_rating;
GdkPixbuf *pixbuf;
GsPrice *price;
GPtrArray *channels;
GsChannel *active_channel;
GCancellable *cancellable;
GsPluginAction pending_action;
} GsAppPrivate;
......@@ -595,6 +597,18 @@ 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));
}
if (priv->active_channel != NULL) {
gs_app_kv_printf (str, "active-channel", "%s",
gs_channel_get_name (priv->active_channel));
}
keys = g_hash_table_get_keys (priv->metadata);
for (GList *l = keys; l != NULL; l = l->next) {
GVariant *val;
......@@ -3985,6 +3999,80 @@ gs_app_get_priority (GsApp *app)
return priv->priority;
}
/**
* 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));
if (priv->active_channel == NULL && gs_channel_get_version (channel) != NULL)
priv->active_channel = 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_set_active_channel:
* @app: a #GsApp
* @channel: a #GsChannel
*
* Set the currently active channel.
*
* Since: 3.28
**/
void
gs_app_set_active_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_set_object (&priv->active_channel, channel);
}
/**
* gs_app_get_active_channel:
* @app: a #GsApp
*
* Gets the currently active channel.
*
* Returns: a #GsChannel or %NULL.
*
* Since: 3.28
**/
GsChannel *
gs_app_get_active_channel (GsApp *app)
{
GsAppPrivate *priv = gs_app_get_instance_private (app);
g_return_val_if_fail (GS_IS_APP (app), NULL);
return priv->active_channel;
}
/**
* gs_app_get_cancellable:
* @app: a #GsApp
......@@ -4166,6 +4254,8 @@ 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_clear_object (&priv->active_channel);
G_OBJECT_CLASS (gs_app_parent_class)->dispose (object);
}
......@@ -4353,6 +4443,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,
......
......@@ -28,6 +28,7 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <appstream-glib.h>
#include "gs-channel.h"
#include "gs-price.h"
G_BEGIN_DECLS
......@@ -325,6 +326,12 @@ 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);
void gs_app_set_active_channel (GsApp *app,
GsChannel *channel);
GsChannel *gs_app_get_active_channel (GsApp *app);
G_END_DECLS
#endif /* __GS_APP_H */
......
/* -*- 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: */
/* -*- 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: */
......@@ -52,6 +52,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);
......
......@@ -48,6 +48,7 @@ struct _GsPluginJob
GsCategory *category;
AsReview *review;
GsPrice *price;
GsChannel *channel;
gint64 time_created;
};
......@@ -67,6 +68,7 @@ enum {
PROP_REVIEW,
PROP_MAX_RESULTS,
PROP_PRICE,
PROP_CHANNEL,
PROP_TIMEOUT,
PROP_LAST
};
......@@ -125,6 +127,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));
......@@ -435,6 +440,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)
{
......@@ -480,6 +499,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;
......@@ -543,6 +565,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;
......@@ -562,6 +587,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);
}
......@@ -650,6 +676,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
......
......@@ -70,6 +70,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__))
......
......@@ -135,6 +135,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,
......@@ -637,6 +642,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:
......
......@@ -154,6 +154,7 @@ typedef enum {
* @GS_PLUGIN_REFINE_FLAGS_REQUIRE_RUNTIME: Require the runtime
* @GS_PLUGIN_REFINE_FLAGS_REQUIRE_SCREENSHOTS: Require screenshot information
* @GS_PLUGIN_REFINE_FLAGS_REQUIRE_CHANGELOG: Require the changelog
* @GS_PLUGIN_REFINE_FLAGS_REQUIRE_CHANNELS: Require channel information
*
* The refine flags.
**/
......@@ -186,6 +187,7 @@ typedef enum {
#define GS_PLUGIN_REFINE_FLAGS_REQUIRE_RUNTIME ((guint64) 1 << 25)
#define GS_PLUGIN_REFINE_FLAGS_REQUIRE_SCREENSHOTS ((guint64) 1 << 26)
#define GS_PLUGIN_REFINE_FLAGS_REQUIRE_CHANGELOG ((guint64) 1 << 27)
#define GS_PLUGIN_REFINE_FLAGS_REQUIRE_CHANNELS ((guint64) 1 << 28)
typedef guint64 GsPluginRefineFlags;
/**
......@@ -252,6 +254,7 @@ typedef enum {
* @GS_PLUGIN_ACTION_DESTROY: Destroy the plugin
* @GS_PLUGIN_ACTION_PURCHASE: Purchase an app
* @GS_PLUGIN_ACTION_DOWNLOAD: Download an application
* @GS_PLUGIN_ACTION_SWITCH_CHANNEL: Switch app channel
*
* The plugin action.
**/
......@@ -300,6 +303,7 @@ typedef enum {
GS_PLUGIN_ACTION_DESTROY,
GS_PLUGIN_ACTION_PURCHASE,
GS_PLUGIN_ACTION_DOWNLOAD,
GS_PLUGIN_ACTION_SWITCH_CHANNEL,
/*< private >*/
GS_PLUGIN_ACTION_LAST
} GsPluginAction;
......
......@@ -589,6 +589,24 @@ gboolean gs_plugin_app_install (GsPlugin *plugin,
GCancellable *cancellable,
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
......
......@@ -1794,6 +1794,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;
}
......@@ -1896,6 +1898,8 @@ gs_plugin_action_to_string (GsPluginAction action)
return "destroy";
if (action == GS_PLUGIN_ACTION_PURCHASE)
return "purchase";
if (action == GS_PLUGIN_ACTION_SWITCH_CHANNEL)
return "switch-channel";
return NULL;
}
......@@ -1998,6 +2002,8 @@ gs_plugin_action_from_string (const gchar *action)
return GS_PLUGIN_ACTION_DESTROY;
if (g_strcmp0 (action, "purchase") == 0)
return GS_PLUGIN_ACTION_PURCHASE;
if (g_strcmp0 (action, "switch-channel") == 0)
return GS_PLUGIN_ACTION_SWITCH_CHANNEL;
return GS_PLUGIN_ACTION_UNKNOWN;
}
......
......@@ -44,6 +44,11 @@ gs_utils_url_func (void)
g_autofree gchar *path3 = NULL;
g_autofree gchar *scheme1 = NULL;
g_autofree gchar *scheme2 = NULL;
g_autofree gchar *value1 = NULL;
g_autofree gchar *value2 = NULL;
g_autofree gchar *value3 = NULL;
g_autofree gchar *value4 = NULL;
g_autofree gchar *value5 = NULL;
scheme1 = gs_utils_get_url_scheme ("appstream://gimp.desktop");
g_assert_cmpstr (scheme1, ==, "appstream");
......@@ -56,6 +61,17 @@ gs_utils_url_func (void)
g_assert_cmpstr (path2, ==, "gimp.desktop");
path3 = gs_utils_get_url_path ("apt:/gimp");
g_assert_cmpstr (path3, ==, "gimp");
value1 = gs_utils_get_url_query_param ("snap://moon-buggy", "channel");
g_assert_null (value1);
value2 = gs_utils_get_url_query_param ("snap://moon-buggy?", "channel");
g_assert_null (value2);
value3 = gs_utils_get_url_query_param ("snap://moon-buggy?channel=beta", "channel");
g_assert_cmpstr (value3, ==, "beta");
value4 = gs_utils_get_url_query_param ("snap://moon-buggy?channel=beta&foo=bar", "channel");
g_assert_cmpstr (value4, ==, "beta");
value5 = gs_utils_get_url_query_param ("snap://moon-buggy?foo=bar&channel=beta", "channel");
g_assert_cmpstr (value5, ==, "beta");
}
static void
......
......@@ -983,6 +983,41 @@ gs_utils_get_url_path (const gchar *url)
return g_strdup (path);
}
/**
* gs_utils_get_url_query:
* @url: A URL, e.g. "snap://moon-buggy?channel=beta"
* @url: A parameter name, e.g. "channel"
*
* Gets a query parameter from the URL string.
*
* Returns: the URL query parameter, e.g. "beta"
*/
gchar *
gs_utils_get_url_query_param (const gchar *url, const gchar *name)
{
g_autoptr(SoupURI) uri = NULL;
const gchar *query;
g_autofree gchar *prefix = NULL;
g_auto(GStrv) params = NULL;
int i;
uri = soup_uri_new (url);
if (!SOUP_URI_IS_VALID (uri))
return NULL;
query = soup_uri_get_query (uri);
if (query == NULL)
return NULL;
params = g_strsplit (query, "&", -1);
prefix = g_strdup_printf ("%s=", name);
for (i = 0; params[i] != NULL; i++) {
if (g_str_has_prefix (params[i], prefix))
return g_strdup (params[i] + strlen (prefix));
}
return NULL;
}
/**
* gs_user_agent:
*
......
......@@ -88,6 +88,8 @@ gboolean gs_utils_is_low_resolution (GtkWidget *toplevel);
gchar *gs_utils_get_url_scheme (const gchar *url);
gchar *gs_utils_get_url_path (const gchar *url);
gchar *gs_utils_get_url_query_param (const gchar *url,
const gchar *name);
const gchar *gs_user_agent (void);
void gs_utils_append_key_value (GString *str,
gsize align_len,
......
......@@ -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',
......
......@@ -834,6 +834,17 @@ gs_plugin_refresh (GsPlugin *plugin,
return gs_plugin_dummy_delay (plugin, app, 3100, cancellable, error);
}
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)
......
......@@ -36,6 +36,27 @@ struct GsPluginData {
GHashTable *store_snaps;
};
typedef struct {
SnapdSnap *snap;
gboolean full_details;
} CacheEntry;
static CacheEntry *
cache_entry_new (SnapdSnap *snap, gboolean full_details)
{
CacheEntry *entry = g_slice_new (CacheEntry);
entry->snap = g_object_ref (snap);
entry->full_details = full_details;
return entry;
}
static void
cache_entry_free (CacheEntry *entry)
{
g_object_unref (entry->snap);
g_slice_free (CacheEntry, entry);
}
static SnapdClient *
get_client (GsPlugin *plugin, GError **error)
{
......@@ -70,7 +91,7 @@ gs_plugin_initialize (GsPlugin *plugin)
}
priv->store_snaps = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, (GDestroyNotify) g_object_unref);
g_free, (GDestroyNotify) cache_entry_free);
priv->auth = gs_auth_new ("snapd");
gs_auth_set_provider_name (priv->auth, "Snap Store");
......@@ -222,15 +243,24 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
}
static SnapdSnap *
store_snap_cache_lookup (GsPlugin *plugin, const gchar *name)
store_snap_cache_lookup (GsPlugin *plugin, const gchar *name, gboolean need_details)
{
GsPluginData *priv = gs_plugin_get_data (plugin);
CacheEntry *entry;
g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->store_snaps_lock);
return g_hash_table_lookup (priv->store_snaps, name);
entry = g_hash_table_lookup (priv->store_snaps, name);
if (entry == NULL)
return NULL;
if (need_details && !entry->full_details)
return NULL;
return entry->snap;
}
static void
store_snap_cache_update (GsPlugin *plugin, GPtrArray *snaps)
store_snap_cache_update (GsPlugin *plugin, GPtrArray *snaps, gboolean full_details)
{
GsPluginData *priv = gs_plugin_get_data (plugin);
g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->store_snaps_lock);
......@@ -238,7 +268,7 @@ store_snap_cache_update (GsPlugin *plugin, GPtrArray *snaps)
for (i = 0; i < snaps->len; i++) {
SnapdSnap *snap = snaps->pdata[i];
g_hash_table_insert (priv->store_snaps, g_strdup (snapd_snap_get_name (snap)), g_object_ref (snap));
g_hash_table_insert (priv->store_snaps, g_strdup (snapd_snap_get_name (snap)), cache_entry_new (snap, full_details));
}
}
......@@ -258,7 +288,7 @@ find_snaps (GsPlugin *plugin, SnapdFindFlags flags, const gchar *section, const
return NULL;
}
store_snap_cache_update (plugin, snaps);
store_snap_cache_update (plugin, snaps, flags & SNAPD_FIND_FLAGS_MATCH_NAME);
return g_steal_pointer (&snaps);
}
......@@ -321,6 +351,8 @@ snap_to_app (GsPlugin *plugin, SnapdSnap *snap)
if (priv->system_confinement == SNAPD_SYSTEM_CONFINEMENT_STRICT && confinement == SNAPD_CONFINEMENT_STRICT)
gs_app_add_kudo (app, GS_APP_KUDO_SANDBOXED);
else
gs_app_remove_kudo (app, GS_APP_KUDO_SANDBOXED);
return g_steal_pointer (&app);
}
......@@ -336,6 +368,7 @@ gs_plugin_url_to_app (GsPlugin *plugin,
g_autofree gchar *path = NULL;
g_autoptr(GPtrArray) snaps = NULL;
g_autoptr(GsApp) app = NULL;
g_autofree gchar *channel_name = NULL;
/* not us */
scheme = gs_utils_get_url_scheme (url);
......@@ -349,6 +382,9 @@ gs_plugin_url_to_app (GsPlugin *plugin,
return TRUE;
app = snap_to_app (plugin, g_ptr_array_index (snaps, 0));
channel_name = gs_utils_get_url_query_param (url, "channel");
if (channel_name != NULL)
gs_app_set_metadata (app, "snap::channel", channel_name);
gs_app_list_add (list, app);
return TRUE;
......@@ -602,13 +638,13 @@ gs_plugin_add_search (GsPlugin *plugin,
}
static SnapdSnap *
get_store_snap (GsPlugin *plugin, const gchar *name, GCancellable *cancellable, GError **error)
get_store_snap (GsPlugin *plugin, const gchar *name, gboolean need_details, GCancellable *cancellable, GError **error)
{