Commit 4cbbed99 authored by Robert Ancell's avatar Robert Ancell

Support purchasable apps.

parent 3e77d71e
......@@ -122,6 +122,7 @@ struct _GsApp
GFile *local_file;
AsContentRating *content_rating;
GdkPixbuf *pixbuf;
GsPrice *price;
};
enum {
......@@ -527,6 +528,10 @@ gs_app_to_string (GsApp *app)
gs_app_kv_size (str, "size-installed", app->size_installed);
if (app->size_download != 0)
gs_app_kv_size (str, "size-download", app->size_download);
if (app->price != NULL)
gs_app_kv_printf (str, "price", "%s %.2f",
gs_price_get_currency (app->price),
gs_price_get_amount (app->price));
if (app->related->len > 0)
gs_app_kv_printf (str, "related", "%u", app->related->len);
if (app->history->len > 0)
......@@ -803,7 +808,8 @@ gs_app_set_state_internal (GsApp *app, AsAppState state)
state == AS_APP_STATE_AVAILABLE_LOCAL ||
state == AS_APP_STATE_UPDATABLE ||
state == AS_APP_STATE_UPDATABLE_LIVE ||
state == AS_APP_STATE_UNAVAILABLE)
state == AS_APP_STATE_UNAVAILABLE ||
state == AS_APP_STATE_PURCHASABLE)
state_change_ok = TRUE;
break;
case AS_APP_STATE_INSTALLED:
......@@ -841,6 +847,7 @@ gs_app_set_state_internal (GsApp *app, AsAppState state)
/* removing has to go into an stable state */
if (state == AS_APP_STATE_UNKNOWN ||
state == AS_APP_STATE_AVAILABLE ||
state == AS_APP_STATE_PURCHASABLE ||
state == AS_APP_STATE_INSTALLED)
state_change_ok = TRUE;
break;
......@@ -870,6 +877,19 @@ gs_app_set_state_internal (GsApp *app, AsAppState state)
state == AS_APP_STATE_INSTALLING)
state_change_ok = TRUE;
break;
case AS_APP_STATE_PURCHASABLE:
/* local has to go into an action state */
if (state == AS_APP_STATE_UNKNOWN ||
state == AS_APP_STATE_PURCHASING)
state_change_ok = TRUE;
break;
case AS_APP_STATE_PURCHASING:
/* purchasing has to go into an stable state */
if (state == AS_APP_STATE_UNKNOWN ||
state == AS_APP_STATE_AVAILABLE ||
state == AS_APP_STATE_PURCHASABLE)
state_change_ok = TRUE;
break;
default:
g_warning ("state %s unhandled",
as_app_state_to_string (app->state));
......@@ -1648,6 +1668,43 @@ gs_app_set_pixbuf (GsApp *app, GdkPixbuf *pixbuf)
g_set_object (&app->pixbuf, pixbuf);
}
/**
* gs_app_get_price:
* @app: a #GsApp
*
* Gets the price required to purchase the application.
*
* Returns: (transfer none): a #GsPrice, or %NULL
*
* Since: 3.26
**/
GsPrice *
gs_app_get_price (GsApp *app)
{
g_return_val_if_fail (GS_IS_APP (app), NULL);
return app->price;
}
/**
* gs_app_set_price:
* @app: a #GsApp
* @amount: the amount of this price, e.g. 0.99
* @currency: an ISO 4217 currency code, e.g. "USD"
*
* Sets a price required to purchase the application.
*
* Since: 3.26
**/
void
gs_app_set_price (GsApp *app, gdouble amount, const gchar *currency)
{
g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&app->mutex);
g_return_if_fail (GS_IS_APP (app));
if (app->price != NULL)
g_object_unref (app->price);
app->price = gs_price_new (amount, currency);
}
typedef enum {
GS_APP_VERSION_FIXUP_RELEASE = 1,
GS_APP_VERSION_FIXUP_DISTRO_SUFFIX = 2,
......@@ -3637,6 +3694,8 @@ gs_app_finalize (GObject *object)
g_object_unref (app->content_rating);
if (app->pixbuf != NULL)
g_object_unref (app->pixbuf);
if (app->price != NULL)
g_object_unref (app->price);
G_OBJECT_CLASS (gs_app_parent_class)->finalize (object);
}
......
......@@ -27,6 +27,8 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <appstream-glib.h>
#include "gs-price.h"
G_BEGIN_DECLS
#define GS_TYPE_APP (gs_app_get_type ())
......@@ -203,6 +205,10 @@ void gs_app_set_management_plugin (GsApp *app,
GdkPixbuf *gs_app_get_pixbuf (GsApp *app);
void gs_app_set_pixbuf (GsApp *app,
GdkPixbuf *pixbuf);
GsPrice *gs_app_get_price (GsApp *app);
void gs_app_set_price (GsApp *app,
gdouble amount,
const gchar *currency);
GPtrArray *gs_app_get_icons (GsApp *app);
void gs_app_add_icon (GsApp *app,
AsIcon *icon);
......
......@@ -49,6 +49,7 @@ GsAppList *gs_plugin_job_get_list (GsPluginJob *self);
GFile *gs_plugin_job_get_file (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);
gchar *gs_plugin_job_to_string (GsPluginJob *self);
void gs_plugin_job_set_action (GsPluginJob *self,
GsPluginAction action);
......
......@@ -44,6 +44,7 @@ struct _GsPluginJob
GFile *file;
GsCategory *category;
AsReview *review;
GsPrice *price;
};
enum {
......@@ -61,6 +62,7 @@ enum {
PROP_CATEGORY,
PROP_REVIEW,
PROP_MAX_RESULTS,
PROP_PRICE,
PROP_LAST
};
......@@ -107,6 +109,10 @@ gs_plugin_job_to_string (GsPluginJob *self)
g_string_append_printf (str, " with review=%s",
as_review_get_id (self->review));
}
if (self->price != NULL) {
g_autofree gchar *price_string = gs_price_to_string (self->price);
g_string_append_printf (str, " with price=%s", price_string);
}
if (self->auth != NULL) {
g_string_append_printf (str, " with auth=%s",
gs_auth_get_provider_id (self->auth));
......@@ -367,6 +373,20 @@ gs_plugin_job_get_review (GsPluginJob *self)
return self->review;
}
void
gs_plugin_job_set_price (GsPluginJob *self, GsPrice *price)
{
g_return_if_fail (GS_IS_PLUGIN_JOB (self));
g_set_object (&self->price, price);
}
GsPrice *
gs_plugin_job_get_price (GsPluginJob *self)
{
g_return_val_if_fail (GS_IS_PLUGIN_JOB (self), NULL);
return self->price;
}
static void
gs_plugin_job_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec)
{
......@@ -409,6 +429,9 @@ gs_plugin_job_get_property (GObject *obj, guint prop_id, GValue *value, GParamSp
case PROP_REVIEW:
g_value_set_object (value, self->review);
break;
case PROP_PRICE:
g_value_set_object (value, self->price);
break;
case PROP_MAX_RESULTS:
g_value_set_uint (value, self->max_results);
break;
......@@ -463,6 +486,9 @@ gs_plugin_job_set_property (GObject *obj, guint prop_id, const GValue *value, GP
case PROP_MAX_RESULTS:
gs_plugin_job_set_max_results (self, g_value_get_uint (value));
break;
case PROP_PRICE:
gs_plugin_job_set_price (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
......@@ -480,6 +506,7 @@ gs_plugin_job_finalize (GObject *obj)
g_clear_object (&self->file);
g_clear_object (&self->category);
g_clear_object (&self->review);
g_clear_object (&self->price);
G_OBJECT_CLASS (gs_plugin_job_parent_class)->finalize (obj);
}
......@@ -558,6 +585,11 @@ gs_plugin_job_class_init (GsPluginJobClass *klass)
0, G_MAXUINT, 0,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_MAX_RESULTS, pspec);
pspec = g_param_spec_object ("price", NULL, NULL,
GS_TYPE_PRICE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_PRICE, pspec);
}
static void
......
......@@ -28,6 +28,7 @@
#include "gs-auth.h"
#include "gs-category.h"
#include "gs-plugin-types.h"
#include "gs-price.h"
G_BEGIN_DECLS
......@@ -63,6 +64,8 @@ void gs_plugin_job_set_category (GsPluginJob *self,
GsCategory *category);
void gs_plugin_job_set_review (GsPluginJob *self,
AsReview *review);
void gs_plugin_job_set_price (GsPluginJob *self,
GsPrice *price);
#define gs_plugin_job_newv(a,...) GS_PLUGIN_JOB(g_object_new(GS_TYPE_PLUGIN_JOB, "action", a, __VA_ARGS__))
......
......@@ -131,6 +131,11 @@ typedef gboolean (*GsPluginActionFunc) (GsPlugin *plugin,
GsApp *app,
GCancellable *cancellable,
GError **error);
typedef gboolean (*GsPluginPurchaseFunc) (GsPlugin *plugin,
GsApp *app,
GsPrice *price,
GCancellable *cancellable,
GError **error);
typedef gboolean (*GsPluginReviewFunc) (GsPlugin *plugin,
GsApp *app,
AsReview *review,
......@@ -572,6 +577,14 @@ gs_plugin_loader_call_vfunc (GsPluginLoaderHelper *helper,
ret = plugin_func (plugin, app, cancellable, &error_local);
}
break;
case GS_PLUGIN_ACTION_PURCHASE:
{
GsPluginPurchaseFunc plugin_func = func;
ret = plugin_func (plugin, app,
gs_plugin_job_get_price (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:
......
......@@ -267,6 +267,7 @@ typedef enum {
* @GS_PLUGIN_ACTION_GET_RECENT: Get the apps recently released
* @GS_PLUGIN_ACTION_INITIALIZE: Initialize the plugin
* @GS_PLUGIN_ACTION_DESTROY: Destroy the plugin
* @GS_PLUGIN_ACTION_PURCHASE: Purchase an app
*
* The plugin action.
**/
......@@ -313,6 +314,7 @@ typedef enum {
GS_PLUGIN_ACTION_GET_UPDATES_HISTORICAL,
GS_PLUGIN_ACTION_INITIALIZE,
GS_PLUGIN_ACTION_DESTROY,
GS_PLUGIN_ACTION_PURCHASE,
/*< private >*/
GS_PLUGIN_ACTION_LAST
} GsPluginAction;
......
......@@ -40,6 +40,7 @@
#include "gs-app.h"
#include "gs-app-list.h"
#include "gs-category.h"
#include "gs-price.h"
G_BEGIN_DECLS
......@@ -560,6 +561,27 @@ gboolean gs_plugin_update_cancel (GsPlugin *plugin,
GCancellable *cancellable,
GError **error);
/**
* gs_plugin_app_purchase:
* @plugin: a #GsPlugin
* @app: a #GsApp
* @price: a #GsPrice
* @cancellable: a #GCancellable, or %NULL
* @error: a #GError, or %NULL
*
* Purchase the application.
*
* NOTE: Once the action is complete, the plugin must set the new state of @app
* to %AS_APP_STATE_AVAILABLE.
*
* Returns: %TRUE for success or if not relevant
**/
gboolean gs_plugin_app_purchase (GsPlugin *plugin,
GsApp *app,
GsPrice *price,
GCancellable *cancellable,
GError **error);
/**
* gs_plugin_app_install:
* @plugin: a #GsPlugin
......
......@@ -1758,6 +1758,8 @@ gs_plugin_action_to_function_name (GsPluginAction action)
return "gs_plugin_initialize";
if (action == GS_PLUGIN_ACTION_DESTROY)
return "gs_plugin_destroy";
if (action == GS_PLUGIN_ACTION_PURCHASE)
return "gs_plugin_app_purchase";
return NULL;
}
......@@ -1856,6 +1858,8 @@ gs_plugin_action_to_string (GsPluginAction action)
return "initialize";
if (action == GS_PLUGIN_ACTION_DESTROY)
return "destroy";
if (action == GS_PLUGIN_ACTION_PURCHASE)
return "purchase";
return NULL;
}
......
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2016 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-price.h"
struct _GsPrice
{
GObject parent_instance;
gdouble amount;
gchar *currency;
};
G_DEFINE_TYPE (GsPrice, gs_price, G_TYPE_OBJECT)
/**
* gs_price_get_amount:
* @price: a #GsPrice
*
* Get the amount of money in this price.
*
* Returns: The amount of money in this price, e.g. 0.99
*/
gdouble
gs_price_get_amount (GsPrice *price)
{
g_return_val_if_fail (GS_IS_PRICE (price), 0);
return price->amount;
}
/**
* gs_price_set_amount:
* @price: a #GsPrice
* @amount: The amount of this price, e.g. 0.99
*
* Set the amount of money in this price.
*/
void
gs_price_set_amount (GsPrice *price, gdouble amount)
{
g_return_if_fail (GS_IS_PRICE (price));
price->amount = amount;
}
/**
* gs_price_get_currency:
* @price: a #GsPrice
*
* Get the currency a price is using.
*
* Returns: an ISO 4217 currency code for this price, e.g. "USD"
*/
const gchar *
gs_price_get_currency (GsPrice *price)
{
g_return_val_if_fail (GS_IS_PRICE (price), NULL);
return price->currency;
}
/**
* gs_price_set_currency:
* @price: a #GsPrice
* @currency: An ISO 4217 currency code, e.g. "USD"
*
* Set the currency this price is using.
*/
void
gs_price_set_currency (GsPrice *price, const gchar *currency)
{
g_return_if_fail (GS_IS_PRICE (price));
g_free (price->currency);
price->currency = g_strdup (currency);
}
/**
* gs_price_to_string:
* @price: a #GsPrice
*
* Convert a price object to a human readable string.
*
* Returns: A human readable string for this price, e.g. "US$0.99"
*/
gchar *
gs_price_to_string (GsPrice *price)
{
g_return_val_if_fail (GS_IS_PRICE (price), NULL);
if (g_strcmp0 (price->currency, "AUD") == 0) {
return g_strdup_printf (_("A$%.2f"), price->amount);
} else if (g_strcmp0 (price->currency, "CAD") == 0) {
return g_strdup_printf (_("C$%.2f"), price->amount);
} else if (g_strcmp0 (price->currency, "CNY") == 0) {
return g_strdup_printf (_("CN¥%.2f"), price->amount);
} else if (g_strcmp0 (price->currency, "EUR") == 0) {
return g_strdup_printf (_("€%.2f"), price->amount);
} else if (g_strcmp0 (price->currency, "GBP") == 0) {
return g_strdup_printf (_("£%.2f"), price->amount);
} else if (g_strcmp0 (price->currency, "JPY") == 0) {
return g_strdup_printf (_("¥%.2f"), price->amount);
} else if (g_strcmp0 (price->currency, "NZD") == 0) {
return g_strdup_printf (_("NZ$%.2f"), price->amount);
} else if (g_strcmp0 (price->currency, "RUB") == 0) {
return g_strdup_printf (_("₽%.2f"), price->amount);
} else if (g_strcmp0 (price->currency, "USD") == 0) {
return g_strdup_printf (_("US$%.2f"), price->amount);
} else {
return g_strdup_printf (_("%s %f"), price->currency, price->amount);
}
}
static void
gs_price_finalize (GObject *object)
{
GsPrice *price = GS_PRICE (object);
g_free (price->currency);
G_OBJECT_CLASS (gs_price_parent_class)->finalize (object);
}
static void
gs_price_class_init (GsPriceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gs_price_finalize;
}
static void
gs_price_init (GsPrice *price)
{
}
/**
* gs_price_new:
* @amount: The amount of this price, e.g. 0.99
* @currency: An ISO 4217 currency code, e.g. "USD"
*
* Creates a new price object.
*
* Return value: a new #GsPrice object.
**/
GsPrice *
gs_price_new (gdouble amount, const gchar *currency)
{
GsPrice *price;
price = g_object_new (GS_TYPE_PRICE, NULL);
price->amount = amount;
price->currency = g_strdup (currency);
return GS_PRICE (price);
}
/* vim: set noexpandtab: */
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2016 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_PRICE_H
#define __GS_PRICE_H
#include <glib-object.h>
G_BEGIN_DECLS
#define GS_TYPE_PRICE (gs_price_get_type ())
G_DECLARE_FINAL_TYPE (GsPrice, gs_price, GS, PRICE, GObject)
GsPrice *gs_price_new (gdouble amount,
const gchar *currency);
gdouble gs_price_get_amount (GsPrice *price);
void gs_price_set_amount (GsPrice *price,
gdouble amount);
const gchar *gs_price_get_currency (GsPrice *price);
void gs_price_set_currency (GsPrice *price,
const gchar *currency);
gchar *gs_price_to_string (GsPrice *price);
G_END_DECLS
#endif /* __GS_PRICE_H */
/* vim: set noexpandtab: */
......@@ -47,6 +47,7 @@ install_headers([
'gs-plugin-event.h',
'gs-plugin-types.h',
'gs-plugin-vfuncs.h',
'gs-price.h',
'gs-utils.h'
],
subdir : 'gnome-software'
......@@ -66,6 +67,7 @@ libgnomesoftware = static_library(
'gs-plugin-job.c',
'gs-plugin-loader.c',
'gs-plugin-loader-sync.c',
'gs-price.c',
'gs-test.c',
'gs-utils.c',
],
......
......@@ -809,6 +809,28 @@ gs_plugin_update_cancel (GsPlugin *plugin, GsApp *app,
return TRUE;
}
gboolean
gs_plugin_app_purchase (GsPlugin *plugin,
GsApp *app,
GsPrice *price,
GCancellable *cancellable,
GError **error)
{
g_debug ("Purchasing app");
/* purchase app */
if (g_strcmp0 (gs_app_get_id (app), "chiron-paid.desktop") == 0) {
gs_app_set_state (app, AS_APP_STATE_PURCHASING);
if (!gs_plugin_dummy_delay (plugin, app, 500, cancellable, error)) {
gs_app_set_state_recover (app);
return FALSE;
}
gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
}
return TRUE;
}
gboolean
gs_plugin_review_submit (GsPlugin *plugin,
GsApp *app,
......
......@@ -546,6 +546,31 @@ gs_plugins_dummy_wildcard_func (GsPluginLoader *plugin_loader)
}
}
static void
gs_plugins_dummy_purchase_func (GsPluginLoader *plugin_loader)
{
gboolean ret;
g_autoptr(GsApp) app = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GsAppList) list = NULL;
g_autoptr(GsPluginJob) plugin_job = NULL;
/* get the updates list */
app = gs_app_new ("chiron-paid.desktop");
gs_app_set_management_plugin (app, "dummy");
gs_app_set_state (app, AS_APP_STATE_PURCHASABLE);
gs_app_set_price (app, 100, "USD");
plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_PURCHASE,
"app", app,
"price", gs_app_get_price (app),
NULL);
ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
gs_test_flush_main_context ();
g_assert_no_error (error);
g_assert (ret);
g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_AVAILABLE);
}
int
main (int argc, char **argv)
{
......@@ -684,7 +709,10 @@ main (int argc, char **argv)
g_test_add_data_func ("/gnome-software/plugins/dummy/distro-upgrades",
plugin_loader,
(GTestDataFunc) gs_plugins_dummy_distro_upgrades_func);
g_test_add_data_func ("/gnome-software/plugins/dummy/purchase",
plugin_loader,
(GTestDataFunc) gs_plugins_dummy_purchase_func);
;
return g_test_run ();
}
......
......@@ -42,6 +42,7 @@ src/gs-page.c
lib/gs-plugin-loader.c
src/gs-popular-tile.c
src/gs-popular-tile.ui
lib/gs-price.c
src/gs-removal-dialog.c
src/gs-removal-dialog.ui
src/gs-review-dialog.c
......
......@@ -235,6 +235,8 @@ gs_details_page_switch_to (GsPage *page, gboolean scroll_up)
GsDetailsPage *self = GS_DETAILS_PAGE (page);
AsAppState state;
GtkWidget *widget;
GsPrice *price;
g_autofree gchar *text = NULL;
GtkStyleContext *sc;
GtkAdjustment *adj;
......@@ -280,6 +282,16 @@ gs_details_page_switch_to (GsPage *page, gboolean scroll_up)
case AS_APP_STATE_INSTALLING:
gtk_widget_set_visible (self->button_install, FALSE);
break;
case AS_APP_STATE_PURCHASABLE:
gtk_widget_set_visible (self->button_install, TRUE);
gtk_style_context_add_class (gtk_widget_get_style_context (self->button_install), "suggested-action");
price = gs_app_get_price (self->app);
text = gs_price_to_string (price);
gtk_button_set_label (GTK_BUTTON (self->button_install), text);
break;
case AS_APP_STATE_PURCHASING:
gtk_widget_set_visible (self->button_install, FALSE);
break;
case AS_APP_STATE_UNKNOWN:
case AS_APP_STATE_INSTALLED:
case AS_APP_STATE_REMOVING:
......@@ -371,6 +383,8 @@ gs_details_page_switch_to (GsPage *page, gboolean scroll_up)
case AS_APP_STATE_REMOVING:
case AS_APP_STATE_UNAVAILABLE:
case AS_APP_STATE_UNKNOWN:
case AS_APP_STATE_PURCHASABLE:
case AS_APP_STATE_PURCHASING:
gtk_widget_set_visible (self->button_remove, FALSE);
break;
default:
......
......@@ -49,6 +49,7 @@ typedef struct {
GtkWidget *button_install;
GsPluginAction action;
GsShellInteraction interaction;
GsPrice *price;
} GsPageHelper;
static void
......@@ -64,6 +65,8 @@ gs_page_helper_free (GsPageHelper *helper)
g_object_unref (helper->cancellable);
if (helper->soup_session != NULL)
g_object_unref (helper->soup_session);
if (helper->price != NULL)
g_object_unref (helper->price);
g_slice_free (GsPageHelper, helper);
}
......@@ -275,6 +278,136 @@ gs_page_set_header_end_widget (GsPage *page, GtkWidget *widget)
g_set_object (&priv->header_end_widget, widget);
}
static void
gs_page_app_purchased_cb (GObject *source,
GAsyncResult *res,
gpointer user_data);
static void
gs_page_purchase_authenticate_cb (GtkDialog *dialog,
GtkResponseType response_type,
GsPageHelper *helper)
{
GsPagePrivate *priv = gs_page_get_instance_private (helper->page);
g_autoptr(GsPluginJob) plugin_job = NULL;
/* unmap the dialog */
gtk_widget_destroy (GTK_WIDGET (dialog));
if (response_type != GTK_RESPONSE_OK) {
gs_page_helper_free (helper);
return;
}
plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_PURCHASE,
"app", helper->app,
"failure-flags", GS_PLUGIN_FAILURE_FLAGS_USE_EVENTS,
NULL);
gs_plugin_loader_job_process_async (priv->plugin_loader, plugin_job,
helper->cancellable,
gs_page_app_purchased_cb,
helper);
}
static void
gs_page_app_purchased_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GsPageHelper) helper = (GsPageHelper *) user_data;
GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
GsPage *page = helper->page;
GCancellable *cancellable = helper->cancellable;
GsPagePrivate *priv = gs_page_get_instance_private (page);