[gnome-software/wip/rancell/paid: 63/63] Purchase dialog WIP



commit f1cebf3f615a478e6bd8e861ee0d2251f5a3c947
Author: Robert Ancell <robert ancell canonical com>
Date:   Fri Aug 12 15:11:47 2016 +1200

    Purchase dialog WIP

 src/Makefile.am               |    3 -
 src/gs-page.c                 |  137 +++++++++++++++++++++++++++++-----------
 src/gs-payment-method-list.c  |  128 --------------------------------------
 src/gs-payment-method-list.h  |   46 --------------
 src/gs-plugin-loader.c        |   17 ++++--
 src/gs-plugin-loader.h        |    8 +-
 src/gs-plugin-vfuncs.h        |    6 +-
 src/gs-purchase-dialog.c      |   49 +++++++++++++++
 src/gs-purchase-dialog.h      |   16 +++++-
 src/gs-purchase-dialog.ui     |   42 +++++++++++++
 src/plugins/gs-plugin-dummy.c |    4 +-
 11 files changed, 226 insertions(+), 230 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 8d581c2..333cada 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -119,7 +119,6 @@ gnome_software_cmd_SOURCES =                                \
        gs-utils.c                                      \
        gs-os-release.c                                 \
        gs-payment-method.c                             \
-       gs-payment-method-list.c                        \
        gs-plugin-loader.c                              \
        gs-plugin-loader-sync.c                         \
        gs-category.c                                   \
@@ -197,8 +196,6 @@ gnome_software_SOURCES =                            \
        gs-page.h                                       \
        gs-payment-method.c                             \
        gs-payment-method.h                             \
-       gs-payment-method-list.c                        \
-       gs-payment-method-list.h                        \
        gs-plugin.c                                     \
        gs-plugin.h                                     \
        gs-plugin-private.h                             \
diff --git a/src/gs-page.c b/src/gs-page.c
index cd2f8ef..ce08ea7 100644
--- a/src/gs-page.c
+++ b/src/gs-page.c
@@ -30,6 +30,7 @@
 #include "gs-common.h"
 #include "gs-auth-dialog.h"
 #include "gs-price.h"
+#include "gs-purchase-dialog.h"
 
 typedef struct
 {
@@ -80,10 +81,10 @@ gs_page_app_purchased_cb (GObject *source,
                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);
+               gs_app_notify_failed_modal (helper->app,
+                                           gs_shell_get_window (priv->shell),
+                                           GS_PLUGIN_LOADER_ACTION_PURCHASE,
+                                           error);
                return;
        }
 }
@@ -320,6 +321,31 @@ gs_page_set_header_end_widget (GsPage *page, GtkWidget *widget)
 }
 
 static void
+gs_page_payment_methods_cb (GObject *source,
+                           GAsyncResult *res,
+                           gpointer user_data);
+
+static void
+gs_page_payment_methods_authenticate_cb (GtkDialog *dialog,
+                                        GtkResponseType response_type,
+                                        GsPageHelper *helper)
+{
+       GsPagePrivate *priv = gs_page_get_instance_private (helper->page);
+
+       /* unmap the dialog */
+       gtk_widget_destroy (GTK_WIDGET (dialog));
+
+       if (response_type != GTK_RESPONSE_OK) {
+               gs_page_helper_free (helper);
+               return;
+       }
+       gs_plugin_loader_get_payment_methods_async (priv->plugin_loader,
+                                                   helper->cancellable,
+                                                   gs_page_payment_methods_cb,
+                                                   helper);
+}
+
+static void
 gs_page_purchase_app_response_cb (GtkDialog *dialog,
                                  gint response,
                                  GsPageHelper *helper)
@@ -342,54 +368,89 @@ gs_page_purchase_app_response_cb (GtkDialog *dialog,
                                             helper);
 }
 
-void
-gs_page_purchase_app (GsPage *page, GsApp *app, GCancellable *cancellable)
+static void
+gs_page_payment_methods_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);
-       GsPageHelper *helper;
+       g_autoptr(GPtrArray) payment_methods = NULL;
+       g_autoptr(GError) error = NULL;
        GtkWidget *dialog;
-       GPtrArray *prices;
-       g_autofree gchar *price_text = NULL, *escaped = NULL;
 
-       helper = g_slice_new0 (GsPageHelper);
-       helper->app = g_object_ref (app);
-       helper->page = g_object_ref (page);
-       helper->cancellable = g_object_ref (cancellable);
+       payment_methods = gs_plugin_loader_get_payment_methods_finish (plugin_loader,
+                                                                      res,
+                                                                      &error);
+       if (g_error_matches (error,
+                            G_IO_ERROR,
+                            G_IO_ERROR_CANCELLED)) {
+               g_debug ("%s", error->message);
+               return;
+       }
+       if (payment_methods == NULL) {
+               /* try to authenticate then retry */
+               if (g_error_matches (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_AUTH_REQUIRED)) {
+                       g_autoptr(GError) error_local = NULL;
+                       GtkWidget *auth_dialog;
+                       auth_dialog = gs_auth_dialog_new (priv->plugin_loader,
+                                                         helper->app,
+                                                         gs_utils_get_error_value (error),
+                                                         &error_local);
+                       if (auth_dialog == NULL) {
+                               g_warning ("%s", error_local->message);
+                               return;
+                       }
+                       gs_shell_modal_dialog_present (priv->shell, GTK_DIALOG (auth_dialog));
+                       g_signal_connect (auth_dialog, "response",
+                                         G_CALLBACK (gs_page_payment_methods_authenticate_cb),
+                                         g_steal_pointer (&helper));
+                       return;
+               }
 
-       /* ask for confirmation */
-       dialog = gtk_message_dialog_new (gs_shell_get_window (priv->shell),
-                                        GTK_DIALOG_MODAL,
-                                        GTK_MESSAGE_QUESTION,
-                                        GTK_BUTTONS_CANCEL,
-                                        /* TRANSLATORS: this is a prompt message, and
-                                         * '%s' is an application summary, e.g. 'GNOME Clocks' */
-                                        _("Are you sure you want to purchase %s?"),
-                                        gs_app_get_name (app));
-       prices = gs_app_get_prices (app);
-       if (prices->len > 0) {
-               GsPrice *price = g_ptr_array_index (prices, 0); // FIXME: Give option of price to choose
-               g_autofree gchar *text = NULL;
-               price_text = gs_price_to_string (price);
-       } else {
-               price_text = g_strdup ("nothing"); // FIXME
+               g_warning ("failed to get payment methods: %s",
+                          error->message);
+               gs_app_notify_failed_modal (helper->app,
+                                           gs_shell_get_window (priv->shell),
+                                           GS_PLUGIN_LOADER_ACTION_PURCHASE,
+                                           error);
+               return;
        }
-       escaped = g_markup_escape_text (gs_app_get_name (app), -1);
-       gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog),
-                                                   /* TRANSLATORS: Describing what will be purchased */
-                                                    _("You will be charged %s and %s will be installed."),
-                                                    price_text,
-                                                    escaped);
 
-       /* TRANSLATORS: this is button text to purchase the application */
-       gtk_dialog_add_button (GTK_DIALOG (dialog), _("Purchase"), GTK_RESPONSE_OK);
+       /* ask for confirmation */
+       dialog = gs_purchase_dialog_new ();
+       gs_purchase_dialog_set_app (GS_PURCHASE_DIALOG (dialog), helper->app);
+       gs_purchase_dialog_set_payment_methods (GS_PURCHASE_DIALOG (dialog), payment_methods);
 
        /* handle this async */
        g_signal_connect (dialog, "response",
-                         G_CALLBACK (gs_page_purchase_app_response_cb), helper);
+                         G_CALLBACK (gs_page_purchase_app_response_cb), g_steal_pointer (&helper));
        gs_shell_modal_dialog_present (priv->shell, GTK_DIALOG (dialog));
 }
 
 void
+gs_page_purchase_app (GsPage *page, GsApp *app, GCancellable *cancellable)
+{
+       GsPagePrivate *priv = gs_page_get_instance_private (page);
+       GsPageHelper *helper;
+
+       helper = g_slice_new0 (GsPageHelper);
+       helper->app = g_object_ref (app);
+       helper->page = g_object_ref (page);
+       helper->cancellable = g_object_ref (cancellable);
+
+       /* load payment methods */
+       gs_plugin_loader_get_payment_methods_async (priv->plugin_loader,
+                                                   helper->cancellable,
+                                                   gs_page_payment_methods_cb,
+                                                   helper);
+}
+
+void
 gs_page_install_app (GsPage *page, GsApp *app, GCancellable *cancellable)
 {
        GsPagePrivate *priv = gs_page_get_instance_private (page);
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 731bac5..9377fde 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -108,7 +108,7 @@ typedef gboolean     (*GsPluginAuthFunc)            (GsPlugin       *plugin,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 typedef gboolean        (*GsPluginPaymentMethodFunc)   (GsPlugin       *plugin,
-                                                        GsPaymentMethodList    *payment_methods,
+                                                        GPtrArray      *payment_methods,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 typedef gboolean        (*GsPluginPurchaseFunc)        (GsPlugin       *plugin,
@@ -156,7 +156,7 @@ typedef struct {
        GsApp                           *app;
        AsReview                        *review;
        GsAuth                          *auth;
-       GsPaymentMethodList             *payment_methods;
+       GPtrArray                       *payment_methods;
        GsPrice                         *price;
 } GsPluginLoaderAsyncState;
 
@@ -172,7 +172,7 @@ gs_plugin_loader_free_async_state (GsPluginLoaderAsyncState *state)
        if (state->review != NULL)
                g_object_unref (state->review);
        if (state->payment_methods != NULL)
-               g_object_unref (state->payment_methods);
+               g_ptr_array_unref (state->payment_methods);
        if (state->price != NULL)
                g_object_unref (state->price);
        if (state->file != NULL)
@@ -2887,6 +2887,13 @@ gs_plugin_loader_add_payment_methods_thread_cb (GTask *task,
                                   cancellable, &error_local);
                gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
+                       /* abort early to allow main thread to process */
+                       if (gs_plugin_loader_is_auth_error (error_local)) {
+                               g_task_return_error (task, error_local);
+                               error_local = NULL;
+                               return;
+                       }
+
                        g_warning ("failed to call %s on %s: %s",
                                   state->function_name,
                                   gs_plugin_get_name (plugin),
@@ -3230,7 +3237,7 @@ gs_plugin_loader_get_payment_methods_async (GsPluginLoader *plugin_loader,
 
        /* save state */
        state = g_slice_new0 (GsPluginLoaderAsyncState);
-       state->payment_methods = gs_payment_method_list_new ();
+       state->payment_methods = g_ptr_array_new_with_free_func (g_object_unref);
        state->function_name = "gs_plugin_add_payment_methods";
 
        /* run in a thread */
@@ -3244,7 +3251,7 @@ gs_plugin_loader_get_payment_methods_async (GsPluginLoader *plugin_loader,
  *
  * Return value: (element-type GsPaymentMethod) (transfer full): A list of payment methods.
  **/
-GsPaymentMethodList *
+GPtrArray *
 gs_plugin_loader_get_payment_methods_finish (GsPluginLoader *plugin_loader,
                                             GAsyncResult *res,
                                             GError **error)
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 2b7f8cf..8e5a74c 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -28,7 +28,6 @@
 #include "gs-auth.h"
 #include "gs-category.h"
 #include "gs-plugin.h"
-#include "gs-payment-method-list.h"
 
 G_BEGIN_DECLS
 
@@ -48,6 +47,7 @@ struct _GsPluginLoaderClass
 };
 
 typedef enum {
+       GS_PLUGIN_LOADER_ACTION_PURCHASE,
        GS_PLUGIN_LOADER_ACTION_INSTALL,
        GS_PLUGIN_LOADER_ACTION_REMOVE,
        GS_PLUGIN_LOADER_ACTION_UPDATE,
@@ -244,9 +244,9 @@ void                 gs_plugin_loader_get_payment_methods_async (GsPluginLoader 
*plugin_loader
                                                             GCancellable *cancellable,
                                                             GAsyncReadyCallback callback,
                                                             gpointer user_data);
-GsPaymentMethodList    *gs_plugin_loader_get_payment_methods_finish    (GsPluginLoader *plugin_loader,
-                                                                        GAsyncResult   *res,
-                                                                        GError         **error);
+GPtrArray      *gs_plugin_loader_get_payment_methods_finish    (GsPluginLoader *plugin_loader,
+                                                                GAsyncResult   *res,
+                                                                GError         **error);
 void            gs_plugin_loader_app_purchase_async    (GsPluginLoader *plugin_loader,
                                                         GsApp          *app,
                                                         GsPrice        *price,
diff --git a/src/gs-plugin-vfuncs.h b/src/gs-plugin-vfuncs.h
index c38192c..5fef056 100644
--- a/src/gs-plugin-vfuncs.h
+++ b/src/gs-plugin-vfuncs.h
@@ -40,8 +40,8 @@
 #include "gs-app.h"
 #include "gs-app-list.h"
 #include "gs-category.h"
-#include "gs-payment-method-list.h"
 #include "gs-price.h"
+#include "gs-payment-method.h"
 
 G_BEGIN_DECLS
 
@@ -501,7 +501,7 @@ gboolean     gs_plugin_update_cancel                (GsPlugin       *plugin,
 /**
  * gs_plugin_add_payment_methods:
  * @plugin: a #GsPlugin
- * @payment_methods: A #GsPaymentMethodList
+ * @payment_methods: (element-type GsPaymentMethod): a #GPtrArray
  * @cancellable: a #GCancellable, or %NULL
  * @error: a #GError, or %NULL
  *
@@ -510,7 +510,7 @@ gboolean     gs_plugin_update_cancel                (GsPlugin       *plugin,
  * Returns: %TRUE for success or if not relevant
  **/
 gboolean        gs_plugin_add_payment_methods          (GsPlugin               *plugin,
-                                                        GsPaymentMethodList    *payment_methods,
+                                                        GPtrArray              *payment_methods,
                                                         GCancellable           *cancellable,
                                                         GError                 **error);
 
diff --git a/src/gs-purchase-dialog.c b/src/gs-purchase-dialog.c
index 2e332a1..58d799f 100644
--- a/src/gs-purchase-dialog.c
+++ b/src/gs-purchase-dialog.c
@@ -29,10 +29,55 @@
 struct _GsPurchaseDialog
 {
        GtkDialog        parent_instance;
+
+       GtkWidget       *label_title;
+       GtkWidget       *combo_payment_method;
+       GtkListStore    *model_payment_method;
 };
 
 G_DEFINE_TYPE (GsPurchaseDialog, gs_purchase_dialog, GTK_TYPE_DIALOG)
 
+void
+gs_purchase_dialog_set_app (GsPurchaseDialog *dialog, GsApp *app)
+{
+       g_return_if_fail (GS_IS_PURCHASE_DIALOG (dialog));
+}
+
+void
+gs_purchase_dialog_set_payment_methods (GsPurchaseDialog *dialog, GPtrArray *payment_methods)
+{
+       guint i;
+
+       g_return_if_fail (GS_IS_PURCHASE_DIALOG (dialog));
+
+       gtk_list_store_clear (dialog->model_payment_method);
+       for (i = 0; i < payment_methods->len; i++) {
+               GtkTreeIter iter;
+               GsPaymentMethod *method = payment_methods->pdata[i];
+
+               gtk_list_store_append (dialog->model_payment_method, &iter);
+               gtk_list_store_set (dialog->model_payment_method, &iter,
+                                   0, gs_payment_method_get_description (method),
+                                   1, method,
+                                   -1);
+       }
+}
+
+
+GsPrice *
+gs_purchase_dialog_get_price (GsPurchaseDialog *dialog)
+{
+       g_return_val_if_fail (GS_IS_PURCHASE_DIALOG (dialog), NULL);
+       return NULL;
+}
+
+GsPaymentMethod *
+gs_purchase_dialog_get_payment_method (GsPurchaseDialog *dialog)
+{
+       g_return_val_if_fail (GS_IS_PURCHASE_DIALOG (dialog), NULL);
+       return NULL;
+}
+
 static void
 gs_purchase_dialog_init (GsPurchaseDialog *dialog)
 {
@@ -45,6 +90,10 @@ gs_purchase_dialog_class_init (GsPurchaseDialogClass *klass)
        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
        gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Software/gs-purchase-dialog.ui");
+
+       gtk_widget_class_bind_template_child (widget_class, GsPurchaseDialog, label_title);
+       gtk_widget_class_bind_template_child (widget_class, GsPurchaseDialog, combo_payment_method);
+       gtk_widget_class_bind_template_child (widget_class, GsPurchaseDialog, model_payment_method);
 }
 
 GtkWidget *
diff --git a/src/gs-purchase-dialog.h b/src/gs-purchase-dialog.h
index e91ff21..9ef63a1 100644
--- a/src/gs-purchase-dialog.h
+++ b/src/gs-purchase-dialog.h
@@ -24,13 +24,27 @@
 
 #include <gtk/gtk.h>
 
+#include "gs-app.h"
+#include "gs-price.h"
+#include "gs-payment-method.h"
+
 G_BEGIN_DECLS
 
 #define GS_TYPE_PURCHASE_DIALOG (gs_purchase_dialog_get_type ())
 
 G_DECLARE_FINAL_TYPE (GsPurchaseDialog, gs_purchase_dialog, GS, PURCHASE_DIALOG, GtkDialog)
 
-GtkWidget      *gs_purchase_dialog_new         (void);
+GtkWidget      *gs_purchase_dialog_new                 (void);
+
+void            gs_purchase_dialog_set_app             (GsPurchaseDialog       *dialog,
+                                                        GsApp                  *app);
+
+void            gs_purchase_dialog_set_payment_methods (GsPurchaseDialog       *dialog,
+                                                        GPtrArray              *payment_methods);
+
+GsPrice                *gs_purchase_dialog_get_price           (GsPurchaseDialog       *dialog);
+
+GsPaymentMethod        *gs_purchase_dialog_get_payment_method  (GsPurchaseDialog       *dialog);
 
 G_END_DECLS
 
diff --git a/src/gs-purchase-dialog.ui b/src/gs-purchase-dialog.ui
index 6dafab6..0d993a6 100644
--- a/src/gs-purchase-dialog.ui
+++ b/src/gs-purchase-dialog.ui
@@ -66,6 +66,40 @@
             <property name="position">0</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkGrid">
+            <property name="name">intro</property>
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">start</property>
+            <property name="valign">start</property>
+            <property name="row_spacing">9</property>
+            <property name="column_spacing">9</property>
+            <child>
+              <object class="GtkLabel" id="label_title">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="label">Are you sure you want to purchase %NAME% for %PRICE%?</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="combo_payment_method">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="model">model_payment_method</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
       </object>
     </child>
   </template>
@@ -77,4 +111,12 @@
       <widget name="purchase_button"/>
     </widgets>
   </object>
+  <object class="GtkListStore" id="model_payment_method">
+    <columns>
+      <!-- column-name label -->
+      <column type="gchar"/>
+      <!-- column-name object -->
+      <column type="GObject"/>
+    </columns>
+  </object>
 </interface>
diff --git a/src/plugins/gs-plugin-dummy.c b/src/plugins/gs-plugin-dummy.c
index 5b94b1d..a9290f1 100644
--- a/src/plugins/gs-plugin-dummy.c
+++ b/src/plugins/gs-plugin-dummy.c
@@ -656,7 +656,7 @@ gs_plugin_update_cancel (GsPlugin *plugin, GsApp *app,
  */
 gboolean
 gs_plugin_add_payment_methods (GsPlugin *plugin,
-                              GsPaymentMethodList *payment_methods,
+                              GPtrArray *payment_methods,
                               GCancellable *cancellable,
                               GError **error)
 {
@@ -665,7 +665,7 @@ gs_plugin_add_payment_methods (GsPlugin *plugin,
        method = gs_payment_method_new ();
        gs_payment_method_set_description (method, "Test Payment Method");
        gs_payment_method_add_metadata (method, "card-number", "0000 0000 0000 0000");
-       gs_payment_method_list_add (payment_methods, method);
+       g_ptr_array_add (payment_methods, method);
        return TRUE;
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]