[gnome-software/wip/rancell/paid: 1/3] Add basic purchasing concepts
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/rancell/paid: 1/3] Add basic purchasing concepts
- Date: Tue, 26 Apr 2016 13:41:42 +0000 (UTC)
commit 3c7b4c99c90074cf6fad76f7927e0144c11ebfe0
Author: Robert Ancell <robert ancell canonical com>
Date: Tue Feb 23 14:55:05 2016 +1300
Add basic purchasing concepts
src/gs-app.c | 117 +++++++++++++++++++++++++++++++++++++++++
src/gs-app.h | 9 +++
src/gs-page.c | 60 +++++++++++++++++++++
src/gs-page.h | 3 +
src/gs-plugin-loader.h | 1 +
src/gs-plugin.h | 4 ++
src/gs-shell-details.c | 16 +++++-
src/gs-utils.c | 31 +++++++++++
src/gs-utils.h | 3 +
src/plugins/gs-plugin-dummy.c | 14 +++++
10 files changed, 257 insertions(+), 1 deletions(-)
---
diff --git a/src/gs-app.c b/src/gs-app.c
index 96499b8..3a34345 100644
--- a/src/gs-app.c
+++ b/src/gs-app.c
@@ -85,6 +85,9 @@ struct _GsApp
AsUrgencyKind update_urgency;
gchar *management_plugin;
guint match_value;
+ guint price;
+ gchar *currency;
+ gboolean owned;
gint rating;
GArray *review_ratings;
GPtrArray *reviews; /* of GsReview */
@@ -117,6 +120,9 @@ enum {
PROP_VERSION,
PROP_SUMMARY,
PROP_DESCRIPTION,
+ PROP_PRICE,
+ PROP_CURRENCY,
+ PROP_OWNED,
PROP_RATING,
PROP_KIND,
PROP_STATE,
@@ -304,6 +310,11 @@ gs_app_to_string (GsApp *app)
gs_app_kv_lpad (str, "origin", app->origin);
if (app->origin_ui != NULL && app->origin_ui[0] != '\0')
gs_app_kv_lpad (str, "origin-ui", app->origin_ui);
+ if (app->price > 0) {
+ gs_app_kv_printf (str, "price", "%u", app->price);
+ if (app->currency != NULL)
+ gs_app_kv_printf (str, "currency", "%s", app->currency);
+ }
if (app->rating != -1)
gs_app_kv_printf (str, "rating", "%i", app->rating);
if (app->review_ratings != NULL) {
@@ -1749,6 +1760,69 @@ gs_app_set_management_plugin (GsApp *app, const gchar *management_plugin)
}
/**
+ * gs_app_get_price:
+ */
+guint
+gs_app_get_price (GsApp *app)
+{
+ g_return_val_if_fail (GS_IS_APP (app), 0);
+ return app->price;
+}
+
+/**
+ * gs_app_set_price:
+ */
+void
+gs_app_set_price (GsApp *app, guint price)
+{
+ g_return_if_fail (GS_IS_APP (app));
+ app->price = price;
+ gs_app_queue_notify (app, "price");
+}
+
+/**
+ * gs_app_get_currency:
+ */
+const gchar *
+gs_app_get_currency (GsApp *app)
+{
+ g_return_val_if_fail (GS_IS_APP (app), NULL);
+ return app->currency;
+}
+
+/**
+ * gs_app_set_currency:
+ */
+void
+gs_app_set_currency (GsApp *app, const gchar *currency)
+{
+ g_return_if_fail (GS_IS_APP (app));
+ app->currency = currency;
+ gs_app_queue_notify (app, "currency");
+}
+
+/**
+ * gs_app_get_owned:
+ */
+gboolean
+gs_app_get_owned (GsApp *app)
+{
+ g_return_val_if_fail (GS_IS_APP (app), FALSE);
+ return app->owned;
+}
+
+/**
+ * gs_app_set_owned:
+ */
+void
+gs_app_set_owned (GsApp *app, gboolean owned)
+{
+ g_return_if_fail (GS_IS_APP (app));
+ app->owned = owned;
+ gs_app_queue_notify (app, "owned");
+}
+
+/**
* gs_app_get_rating:
*/
gint
@@ -2346,6 +2420,15 @@ gs_app_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *
case PROP_DESCRIPTION:
g_value_set_string (value, app->description);
break;
+ case PROP_PRICE:
+ g_value_set_uint (value, app->price);
+ break;
+ case PROP_CURRENCY:
+ g_value_set_string (value, app->currency);
+ break;
+ case PROP_OWNED:
+ g_value_set_boolean (value, app->owned);
+ break;
case PROP_RATING:
g_value_set_int (value, app->rating);
break;
@@ -2400,6 +2483,15 @@ gs_app_set_property (GObject *object, guint prop_id, const GValue *value, GParam
GS_APP_QUALITY_UNKNOWN,
g_value_get_string (value));
break;
+ case PROP_PRICE:
+ gs_app_set_price (app, g_value_get_uint (value));
+ break;
+ case PROP_CURRENCY:
+ gs_app_set_currency (app, g_value_get_string (value));
+ break;
+ case PROP_OWNED:
+ gs_app_set_owned (app, g_value_get_boolean (value));
+ break;
case PROP_RATING:
gs_app_set_rating (app, g_value_get_int (value));
break;
@@ -2474,6 +2566,7 @@ gs_app_finalize (GObject *object)
g_free (app->update_version_ui);
g_free (app->update_details);
g_free (app->management_plugin);
+ g_free (app->currency);
g_hash_table_unref (app->metadata);
g_hash_table_unref (app->addons_hash);
g_hash_table_unref (app->related_hash);
@@ -2541,6 +2634,30 @@ gs_app_class_init (GsAppClass *klass)
g_object_class_install_property (object_class, PROP_DESCRIPTION, pspec);
/**
+ * GsApp:price:
+ */
+ pspec = g_param_spec_uint ("price", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_PRICE, pspec);
+
+ /**
+ * GsApp:currency:
+ */
+ pspec = g_param_spec_string ("currency", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_CURRENCY, pspec);
+
+ /**
+ * GsApp:owned:
+ */
+ pspec = g_param_spec_boolean ("owned", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_OWNED, pspec);
+
+ /**
* GsApp:rating:
*/
pspec = g_param_spec_int ("rating", NULL, NULL,
diff --git a/src/gs-app.h b/src/gs-app.h
index 0e476d7..d87d7cf 100644
--- a/src/gs-app.h
+++ b/src/gs-app.h
@@ -195,6 +195,15 @@ const gchar *gs_app_get_metadata_item (GsApp *app,
void gs_app_set_metadata (GsApp *app,
const gchar *key,
const gchar *value);
+guint gs_app_get_price (GsApp *app);
+void gs_app_set_price (GsApp *app,
+ guint price);
+const gchar *gs_app_get_currency (GsApp *app);
+void gs_app_set_currency (GsApp *app,
+ const gchar *currency);
+gboolean gs_app_get_owned (GsApp *app);
+void gs_app_set_owned (GsApp *app,
+ gboolean owned);
gint gs_app_get_rating (GsApp *app);
void gs_app_set_rating (GsApp *app,
gint rating);
diff --git a/src/gs-page.c b/src/gs-page.c
index 5e8d2dc..1051051 100644
--- a/src/gs-page.c
+++ b/src/gs-page.c
@@ -58,6 +58,41 @@ gs_page_helper_free (GsPageHelper *helper)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GsPageHelper, gs_page_helper_free);
static void
+gs_page_app_bought_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;
+ GsPagePrivate *priv = gs_page_get_instance_private (page);
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+
+ ret = gs_plugin_loader_app_action_finish (plugin_loader,
+ res,
+ &error);
+ if (!ret) {
+ g_warning ("failed to purchase %s: %s",
+ gs_app_get_id (helper->app),
+ error->message);
+ gs_app_notify_failed_modal (helper->app,
+ gs_shell_get_window (priv->shell),
+ GS_PLUGIN_LOADER_ACTION_PURCHASE,
+ error);
+ return;
+ }
+
+ /* only show this if the window is not active */
+ if (gs_app_get_state (helper->app) != AS_APP_STATE_QUEUED_FOR_INSTALL &&
+ !gs_shell_is_active (priv->shell))
+ gs_app_notify_installed (helper->app);
+
+ if (GS_PAGE_GET_CLASS (page)->app_installed != NULL)
+ GS_PAGE_GET_CLASS (page)->app_installed (page, helper->app);
+}
+
+static void
gs_page_app_installed_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
@@ -181,6 +216,31 @@ gs_page_set_header_end_widget (GsPage *page, GtkWidget *widget)
}
void
+gs_page_purchase_app (GsPage *page, GsApp *app)
+{
+ GsPagePrivate *priv = gs_page_get_instance_private (page);
+ GsPageHelper *helper;
+ GtkResponseType response;
+
+ /* probably non-free */
+ if (gs_app_get_state (app) == AS_APP_STATE_UNAVAILABLE) {
+ response = gs_app_notify_unavailable (app, gs_shell_get_window (priv->shell));
+ if (response != GTK_RESPONSE_OK)
+ return;
+ }
+
+ helper = g_slice_new0 (GsPageHelper);
+ helper->app = g_object_ref (app);
+ helper->page = g_object_ref (page);
+ gs_plugin_loader_app_action_async (priv->plugin_loader,
+ app,
+ GS_PLUGIN_LOADER_ACTION_PURCHASE,
+ priv->cancellable,
+ gs_page_app_bought_cb,
+ helper);
+}
+
+void
gs_page_install_app (GsPage *page, GsApp *app)
{
GsPagePrivate *priv = gs_page_get_instance_private (page);
diff --git a/src/gs-page.h b/src/gs-page.h
index b63bd8e..e305ea4 100644
--- a/src/gs-page.h
+++ b/src/gs-page.h
@@ -47,12 +47,15 @@ struct _GsPageClass
};
GsPage *gs_page_new (void);
+
GtkWidget *gs_page_get_header_start_widget (GsPage *page);
void gs_page_set_header_start_widget (GsPage *page,
GtkWidget *widget);
GtkWidget *gs_page_get_header_end_widget (GsPage *page);
void gs_page_set_header_end_widget (GsPage *page,
GtkWidget *widget);
+void gs_page_purchase_app (GsPage *page,
+ GsApp *app);
void gs_page_install_app (GsPage *page,
GsApp *app);
void gs_page_remove_app (GsPage *page,
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 010db62..26ee54d 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -53,6 +53,7 @@ typedef enum
} GsPluginLoaderError;
typedef enum {
+ GS_PLUGIN_LOADER_ACTION_PURCHASE,
GS_PLUGIN_LOADER_ACTION_INSTALL,
GS_PLUGIN_LOADER_ACTION_REMOVE,
GS_PLUGIN_LOADER_ACTION_UPDATE,
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index e48a9af..5ad92fb 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -270,6 +270,10 @@ gboolean gs_plugin_update_cancel (GsPlugin *plugin,
GsApp *app,
GCancellable *cancellable,
GError **error);
+gboolean gs_plugin_app_purchase (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error);
gboolean gs_plugin_app_install (GsPlugin *plugin,
GsApp *app,
GCancellable *cancellable,
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 6969862..2cbe276 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -254,7 +254,13 @@ gs_shell_details_switch_to (GsPage *page, gboolean scroll_up)
gtk_style_context_add_class (gtk_widget_get_style_context (self->button_install),
"suggested-action");
/* TRANSLATORS: button text in the header when an application
* can be installed */
- gtk_button_set_label (GTK_BUTTON (self->button_install), _("_Install"));
+ if (gs_app_get_price (self->app) > 0) {
+ g_autofree gchar *price = NULL;
+ price = gs_format_price (gs_app_get_currency (self->app), gs_app_get_price
(self->app));
+ gtk_button_set_label (GTK_BUTTON (self->button_install), price);
+ } else {
+ gtk_button_set_label (GTK_BUTTON (self->button_install), _("_Install"));
+ }
break;
case AS_APP_STATE_QUEUED_FOR_INSTALL:
gtk_widget_set_visible (self->button_install, FALSE);
@@ -1307,6 +1313,9 @@ gs_shell_details_file_to_app_cb (GObject *source,
g_signal_connect_object (self->app, "notify::license",
G_CALLBACK (gs_shell_details_notify_state_changed_cb),
self, 0);
+ g_signal_connect_object (self->app, "notify::owned",
+ G_CALLBACK (gs_shell_details_notify_state_changed_cb),
+ self, 0);
g_signal_connect_object (self->app, "notify::progress",
G_CALLBACK (gs_shell_details_progress_changed_cb),
self, 0);
@@ -1446,6 +1455,11 @@ gs_shell_details_app_install_button_cb (GtkWidget *widget, GsShellDetails *self)
GList *l;
g_autoptr(GList) addons = NULL;
+ if (gs_app_get_price (self->app) > 0 && !gs_app_get_owned (self->app)) {
+ gs_page_purchase_app (GS_PAGE (self), self->app);
+ return;
+ }
+
/* Mark ticked addons to be installed together with the app */
addons = gtk_container_get_children (GTK_CONTAINER (self->list_box_addons));
for (l = addons; l; l = l->next) {
diff --git a/src/gs-utils.c b/src/gs-utils.c
index 730afab..a027c8e 100644
--- a/src/gs-utils.c
+++ b/src/gs-utils.c
@@ -185,6 +185,11 @@ gs_app_notify_failed_modal (GsApp *app,
g_string_append_printf (msg, _("Upgrade to %s failed."),
gs_app_get_name (app));
break;
+ case GS_PLUGIN_LOADER_ACTION_PURCHASE:
+ /* TRANSLATORS: this is when the purchase fails */
+ g_string_append_printf (msg, _("Purchase of %s failed."),
+ gs_app_get_name (app));
+ break;
default:
g_assert_not_reached ();
break;
@@ -893,4 +898,30 @@ gs_utils_widget_set_custom_css (GsApp *app, GtkWidget *widget, const gchar *meta
g_object_unref);
}
+gchar *
+gs_format_price (const gchar *currency, guint price)
+{
+ if (strcmp (currency, "AUD") == 0) {
+ return g_strdup_printf (_("A$%u"), price);
+ } else if (strcmp (currency, "CAD") == 0) {
+ return g_strdup_printf (_("C$%u"), price);
+ } else if (strcmp (currency, "CNY") == 0) {
+ return g_strdup_printf (_("CN¥%u"), price);
+ } else if (strcmp (currency, "EUR") == 0) {
+ return g_strdup_printf (_("€%u"), price);
+ } else if (strcmp (currency, "GBP") == 0) {
+ return g_strdup_printf (_("£%u"), price);
+ } else if (strcmp (currency, "JPY") == 0) {
+ return g_strdup_printf (_("¥%u"), price);
+ } else if (strcmp (currency, "NZD") == 0) {
+ return g_strdup_printf (_("NZ$%u"), price);
+ } else if (strcmp (currency, "RUB") == 0) {
+ return g_strdup_printf (_("₽%u"), price);
+ } else if (strcmp (currency, "USD") == 0) {
+ return g_strdup_printf (_("US$%u"), price);
+ } else {
+ return g_strdup_printf (_("%s %u"), currency, price);
+ }
+}
+
/* vim: set noexpandtab: */
diff --git a/src/gs-utils.h b/src/gs-utils.h
index e407bd0..1143d16 100644
--- a/src/gs-utils.h
+++ b/src/gs-utils.h
@@ -73,6 +73,9 @@ void gs_utils_widget_set_custom_css (GsApp *app,
GtkWidget *widget,
const gchar *metadata_css);
+gchar *gs_format_price (const gchar *currency,
+ guint price);
+
G_END_DECLS
#endif /* __GS_UTILS_H */
diff --git a/src/plugins/gs-plugin-dummy.c b/src/plugins/gs-plugin-dummy.c
index e5c0115..3639cd2 100644
--- a/src/plugins/gs-plugin-dummy.c
+++ b/src/plugins/gs-plugin-dummy.c
@@ -603,6 +603,20 @@ gs_plugin_update_cancel (GsPlugin *plugin, GsApp *app,
}
/**
+ * gs_plugin_app_purchase:
+ */
+gboolean
+gs_plugin_app_purchase (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_debug ("Purchasing app");
+ gs_app_set_owned (app, TRUE);
+ return TRUE;
+}
+
+/**
* gs_plugin_review_submit:
*/
gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]