[gnome-software] gs-shell: Add a menu popover with sign in/out shortcuts.



commit 796d20b4c5c243c87f43850db948fc871437fcf3
Author: Andrea Azzarone <azzaronea gmail com>
Date:   Wed May 16 16:31:11 2018 -0500

    gs-shell: Add a menu popover with sign in/out shortcuts.

 lib/gs-auth.c                           |  62 ++++++++++++++
 lib/gs-auth.h                           |   4 +
 lib/gs-plugin-loader.c                  |   7 ++
 lib/gs-plugin-loader.h                  |   1 +
 plugins/snap/gs-plugin-snap.c           |  24 +++++-
 plugins/ubuntuone/gs-plugin-ubuntuone.c |  21 +++++
 src/gnome-software.ui                   |  33 +++++++-
 src/gs-auth-dialog.c                    |   7 +-
 src/gs-details-page.ui                  |   1 -
 src/gs-page.c                           |   4 +-
 src/gs-shell.c                          | 145 ++++++++++++++++++++++++++++++++
 11 files changed, 302 insertions(+), 7 deletions(-)
---
diff --git a/lib/gs-auth.c b/lib/gs-auth.c
index 78c6aef9..098efd13 100644
--- a/lib/gs-auth.c
+++ b/lib/gs-auth.c
@@ -558,6 +558,68 @@ gs_auth_store_save (GsAuth *auth, GsAuthStoreFlags flags,
        return TRUE;
 }
 
+gboolean
+gs_auth_store_clear (GsAuth *auth, GsAuthStoreFlags flags,
+                    GCancellable *cancellable, GError **error)
+{
+       SecretSchema schema = {
+               auth->provider_schema,
+               SECRET_SCHEMA_NONE,
+               { { "key", SECRET_SCHEMA_ATTRIBUTE_STRING } }
+       };
+
+       /* no schema */
+       if (auth->provider_schema == NULL) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "No provider schema set for %s",
+                            auth->provider_id);
+               return FALSE;
+       }
+
+       /* username */
+       if ((flags & GS_AUTH_STORE_FLAG_USERNAME) > 0) {
+               if (!secret_password_clear_sync (&schema,
+                                                cancellable, error,
+                                                "key", "username", NULL))
+                       return FALSE;
+       }
+
+       g_free (auth->username);
+       auth->username = NULL;
+
+       /* password */
+       if ((flags & GS_AUTH_STORE_FLAG_PASSWORD) > 0) {
+               if (!secret_password_clear_sync (&schema,
+                                                cancellable, error,
+                                                "key", "password", NULL))
+                       return FALSE;
+       }
+
+       g_free (auth->password);
+       auth->password = NULL;
+
+       /* metadata */
+       if (flags & GS_AUTH_STORE_FLAG_METADATA) {
+               g_autoptr(GList) keys = NULL;
+               keys = g_hash_table_get_keys (auth->metadata);
+               for (GList *l = keys; l != NULL; l = l->next) {
+                       const gchar *key = l->data;
+                       if (!secret_password_clear_sync (&schema,
+                                                        cancellable, error,
+                                                        "key", key, NULL))
+                               return FALSE;
+
+               }
+       }
+
+       g_hash_table_remove_all (auth->metadata);
+
+       /* success */
+       return TRUE;
+}
+
 static void
 gs_auth_get_property (GObject *object, guint prop_id,
                        GValue *value, GParamSpec *pspec)
diff --git a/lib/gs-auth.h b/lib/gs-auth.h
index c9f572f7..55df1a7d 100644
--- a/lib/gs-auth.h
+++ b/lib/gs-auth.h
@@ -108,6 +108,10 @@ gboolean    gs_auth_store_save             (GsAuth         *auth,
                                                 GsAuthStoreFlags flags,
                                                 GCancellable   *cancellable,
                                                 GError         **error);
+gboolean        gs_auth_store_clear            (GsAuth         *auth,
+                                                GsAuthStoreFlags flags,
+                                                GCancellable   *cancellable,
+                                                GError         **error);
 
 G_END_DECLS
 
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index 2fb02e3c..55ddc95d 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -2266,6 +2266,13 @@ gs_plugin_loader_get_auth_by_id (GsPluginLoader *plugin_loader,
        return NULL;
 }
 
+GPtrArray *
+gs_plugin_loader_get_auths (GsPluginLoader *plugin_loader)
+{
+       GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+       return priv->auth_array;
+}
+
 void
 gs_plugin_loader_add_location (GsPluginLoader *plugin_loader, const gchar *location)
 {
diff --git a/lib/gs-plugin-loader.h b/lib/gs-plugin-loader.h
index ea603985..ac1c2204 100644
--- a/lib/gs-plugin-loader.h
+++ b/lib/gs-plugin-loader.h
@@ -82,6 +82,7 @@ void           gs_plugin_loader_add_location          (GsPluginLoader *plugin_loader,
                                                         const gchar    *location);
 GsAuth         *gs_plugin_loader_get_auth_by_id        (GsPluginLoader *plugin_loader,
                                                         const gchar    *provider_id);
+GPtrArray      *gs_plugin_loader_get_auths             (GsPluginLoader *plugin_loader);
 guint           gs_plugin_loader_get_scale             (GsPluginLoader *plugin_loader);
 void            gs_plugin_loader_set_scale             (GsPluginLoader *plugin_loader,
                                                         guint           scale);
diff --git a/plugins/snap/gs-plugin-snap.c b/plugins/snap/gs-plugin-snap.c
index b45f87ea..2a26fe6a 100644
--- a/plugins/snap/gs-plugin-snap.c
+++ b/plugins/snap/gs-plugin-snap.c
@@ -74,7 +74,7 @@ gs_plugin_initialize (GsPlugin *plugin)
 
        priv->auth = gs_auth_new ("snapd");
        gs_auth_set_provider_name (priv->auth, "Snap Store");
-       gs_auth_set_provider_schema (priv->auth, "com.ubuntu.UbuntuOne.GnomeSoftware");
+       gs_auth_set_provider_schema (priv->auth, "com.ubuntu.SnapStore.GnomeSoftware");
        gs_plugin_add_auth (plugin, priv->auth);
 
        gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "desktop-categories");
@@ -183,6 +183,7 @@ load_auth (GsPlugin *plugin)
        g_variant_get (macaroon_variant, "(&s^as)", &macaroon, &discharges);
        g_clear_object (&priv->auth_data);
        priv->auth_data = snapd_auth_data_new (macaroon, discharges);
+       gs_auth_add_flags (priv->auth, GS_AUTH_FLAG_VALID);
 }
 
 gboolean
@@ -1071,6 +1072,27 @@ gs_plugin_auth_login (GsPlugin *plugin, GsAuth *auth,
        return TRUE;
 }
 
+gboolean
+gs_plugin_auth_logout (GsPlugin *plugin, GsAuth *auth,
+                      GCancellable *cancellable, GError **error)
+{
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+
+       if (auth != priv->auth)
+               return TRUE;
+
+       /* clear */
+       if (!gs_auth_store_clear (auth,
+                                 GS_AUTH_STORE_FLAG_USERNAME |
+                                 GS_AUTH_STORE_FLAG_METADATA,
+                                 cancellable, error))
+               return FALSE;
+
+       g_clear_object (&priv->auth_data);
+       gs_auth_set_flags (priv->auth, 0);
+       return TRUE;
+}
+
 gboolean
 gs_plugin_auth_lost_password (GsPlugin *plugin, GsAuth *auth,
                              GCancellable *cancellable, GError **error)
diff --git a/plugins/ubuntuone/gs-plugin-ubuntuone.c b/plugins/ubuntuone/gs-plugin-ubuntuone.c
index 9d693572..00f2a4f2 100644
--- a/plugins/ubuntuone/gs-plugin-ubuntuone.c
+++ b/plugins/ubuntuone/gs-plugin-ubuntuone.c
@@ -233,6 +233,27 @@ gs_plugin_auth_login (GsPlugin *plugin, GsAuth *auth,
        return TRUE;
 }
 
+gboolean
+gs_plugin_auth_logout (GsPlugin *plugin, GsAuth *auth,
+                      GCancellable *cancellable, GError **error)
+{
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+
+       if (auth != priv->auth)
+               return TRUE;
+
+       /* clear */
+       if (!gs_auth_store_clear (auth,
+                                 GS_AUTH_STORE_FLAG_USERNAME |
+                                 GS_AUTH_STORE_FLAG_METADATA,
+                                 cancellable, error))
+               return FALSE;
+
+       gs_auth_set_flags (priv->auth, 0);
+       return TRUE;
+}
+
+
 gboolean
 gs_plugin_auth_lost_password (GsPlugin *plugin, GsAuth *auth,
                              GCancellable *cancellable, GError **error)
diff --git a/src/gnome-software.ui b/src/gnome-software.ui
index 33c8bb4d..a8755701 100644
--- a/src/gnome-software.ui
+++ b/src/gnome-software.ui
@@ -17,6 +17,18 @@
       </object>
     </child>
   </object>
+  <object class="GtkPopoverMenu" id="account_popover">
+    <child>
+      <object class="GtkBox" id="account_box">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="margin">6</property>
+        <child>
+          <placeholder/>
+        </child>
+      </object>
+    </child>
+  </object>
   <object class="GtkApplicationWindow" id="window_software">
     <property name="can_focus">False</property>
     <property name="default-width">1200</property>
@@ -263,8 +275,28 @@
                   </object>
                 </child>
               </object>
+              <packing>
+                <property name="position">1</property>
+                <property name="pack-type">end</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkMenuButton" id="account_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="sensitive">True</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">open-menu-symbolic</property>
+                    <property name="icon_size">1</property>
+                  </object>
+                </child>
+              </object>
               <packing>
                 <property name="pack-type">end</property>
+                <property name="position">0</property>
               </packing>
             </child>
           </object>
@@ -274,7 +306,6 @@
             <property name="position">0</property>
           </packing>
         </child>
-
         <child>
           <object class="GtkSearchBar" id="search_bar">
             <property name="visible">True</property>
diff --git a/src/gs-auth-dialog.c b/src/gs-auth-dialog.c
index 4b8a2bbf..9eff3404 100644
--- a/src/gs-auth-dialog.c
+++ b/src/gs-auth-dialog.c
@@ -335,7 +335,7 @@ gs_auth_dialog_new (GsPluginLoader *plugin_loader,
                             GS_PLUGIN_ERROR,
                             GS_PLUGIN_ERROR_FAILED,
                             "no auth-provider given for %s",
-                            gs_app_get_id (app));
+                            app != NULL ? gs_app_get_id (app) : NULL);
                return NULL;
        }
        auth = gs_plugin_loader_get_auth_by_id (plugin_loader, provider_id);
@@ -344,7 +344,8 @@ gs_auth_dialog_new (GsPluginLoader *plugin_loader,
                             GS_PLUGIN_ERROR,
                             GS_PLUGIN_ERROR_NOT_SUPPORTED,
                             "no auth-provider %s for %s",
-                            provider_id, gs_app_get_id (app));
+                            provider_id,
+                            app != NULL ? gs_app_get_id (app) : NULL);
                return NULL;
        }
 
@@ -353,7 +354,7 @@ gs_auth_dialog_new (GsPluginLoader *plugin_loader,
                               "use-header-bar", TRUE,
                               NULL);
        dialog->plugin_loader = g_object_ref (plugin_loader);
-       dialog->app = g_object_ref (app);
+       dialog->app = app != NULL ? g_object_ref (app) : NULL;
        dialog->auth = g_object_ref (auth);
        gs_auth_dialog_setup (dialog);
 
diff --git a/src/gs-details-page.ui b/src/gs-details-page.ui
index e6cb7dc0..9836384d 100644
--- a/src/gs-details-page.ui
+++ b/src/gs-details-page.ui
@@ -1608,5 +1608,4 @@
       </object>
     </child>
   </object>
-
 </interface>
diff --git a/src/gs-page.c b/src/gs-page.c
index 7819523c..9b34310e 100644
--- a/src/gs-page.c
+++ b/src/gs-page.c
@@ -85,7 +85,8 @@ gs_page_authenticate_cb (GtkDialog *dialog,
        /* unmap the dialog */
        gtk_widget_destroy (GTK_WIDGET (dialog));
 
-       helper->callback (helper->page, response_type == GTK_RESPONSE_OK, helper->callback_data);
+       if (helper->callback != NULL)
+               helper->callback (helper->page, response_type == GTK_RESPONSE_OK, helper->callback_data);
 }
 
 void
@@ -102,6 +103,7 @@ gs_page_authenticate (GsPage *page,
        g_autoptr(GError) error = NULL;
 
        helper = g_slice_new0 (GsPageHelper);
+       helper->app = app != NULL ? g_object_ref (app) : NULL;
        helper->page = g_object_ref (page);
        helper->callback = callback;
        helper->callback_data = user_data;
diff --git a/src/gs-shell.c b/src/gs-shell.c
index be0c9a77..1d59228b 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -242,6 +242,17 @@ gs_shell_clean_back_entry_stack (GsShell *shell)
        }
 }
 
+static void
+gs_shell_update_account_button_visibility (GsShell *shell)
+{
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+       GPtrArray *auths = gs_plugin_loader_get_auths (priv->plugin_loader);
+       GtkWidget *account_button;
+
+       account_button = GTK_WIDGET (gtk_builder_get_object (priv->builder, "account_button"));
+       gtk_widget_set_visible (account_button, auths->len > 0);
+}
+
 void
 gs_shell_change_mode (GsShell *shell,
                      GsShellMode mode,
@@ -377,6 +388,8 @@ gs_shell_change_mode (GsShell *shell,
        widget = gs_page_get_header_end_widget (page);
        gs_shell_set_header_end_widget (shell, widget);
 
+       gs_shell_update_account_button_visibility (shell);
+
        /* destroy any existing modals */
        if (priv->modal_dialogs != NULL) {
                gsize i = 0;
@@ -648,6 +661,132 @@ search_mode_enabled_cb (GtkSearchBar *search_bar, GParamSpec *pspec, GsShell *sh
                                      gtk_search_bar_get_search_mode (search_bar));
 }
 
+static void
+gs_shell_signin_button_cb (GtkButton *button, GsShell *shell)
+{
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+       GsAuth *auth;
+
+       auth = GS_AUTH (g_object_get_data (G_OBJECT (button), "auth"));
+       gs_page_authenticate (priv->page_last, NULL,
+                             gs_auth_get_provider_id (auth),
+                             priv->cancellable,
+                             NULL, NULL);
+}
+
+static void
+gs_shell_logout_cb (GObject *source,
+                   GAsyncResult *res,
+                   gpointer user_data)
+{
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+       g_autoptr(GError) error = NULL;
+
+       if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) {
+               g_warning ("failed to logout: %s",  error->message);
+               return;
+       }
+}
+
+static void
+gs_shell_signout_button_cb (GtkButton *button, GsShell *shell)
+{
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+       g_autoptr(GsPluginJob) plugin_job = NULL;
+
+       plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_AUTH_LOGOUT,
+                                        "auth", g_object_get_data (G_OBJECT (button), "auth"),
+                                        NULL);
+
+       gs_plugin_loader_job_process_async (priv->plugin_loader, plugin_job,
+                                           priv->cancellable,
+                                           gs_shell_logout_cb,
+                                           shell);
+
+}
+
+static void
+add_buttons_for_auth (GsShell *shell, GsAuth *auth)
+{
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+       GtkWidget *account_box;
+       gboolean logged_in;
+       GtkWidget *signin_button;
+       GtkWidget *signout_button;
+       g_autofree gchar *signout_label = NULL;
+       g_autofree gchar *signin_label = NULL;
+
+       account_box = GTK_WIDGET (gtk_builder_get_object (priv->builder, "account_box"));
+       logged_in = gs_auth_has_flag (auth, GS_AUTH_FLAG_VALID);
+       signin_button = gtk_model_button_new ();
+       signout_button = gtk_model_button_new ();
+
+       signout_label = g_strdup_printf (_("Sign out from %s"),
+                                        gs_auth_get_provider_name (auth));
+       if (logged_in)
+               signin_label = g_strdup_printf (_("Signed in into %s as %s"),
+                                               gs_auth_get_provider_name (auth),
+                                               gs_auth_get_username (auth));
+       else
+               signin_label = g_strdup_printf (_("Sign in to %s…"),
+                                               gs_auth_get_provider_name (auth));
+
+       g_object_set (signin_button,
+                     "text", signin_label,
+                     "sensitive", !logged_in, NULL);
+       g_object_set_data (G_OBJECT (signin_button), "auth", auth);
+
+       g_object_set (signout_button,
+                     "text", signout_label,
+                     "sensitive", logged_in, NULL);
+       g_object_set_data (G_OBJECT (signout_button), "auth", auth);
+
+       g_signal_connect (signin_button, "clicked",
+                         G_CALLBACK (gs_shell_signin_button_cb), shell);
+       g_signal_connect (signout_button, "clicked",
+                         G_CALLBACK (gs_shell_signout_button_cb), shell);
+
+       gtk_widget_show (signin_button);
+       gtk_widget_show (signout_button);
+       gtk_box_pack_start (GTK_BOX (account_box), signin_button, TRUE, TRUE, 0);
+       gtk_box_pack_start (GTK_BOX (account_box), signout_button, TRUE, TRUE, 0);
+}
+
+static void
+account_button_clicked_cb (GtkButton *button, GsShell *shell)
+{
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+       GPtrArray *auth_array;
+       GtkWidget *account_popover;
+       GtkWidget *account_box;
+       g_autoptr(GList) children = NULL;
+
+       auth_array = gs_plugin_loader_get_auths (priv->plugin_loader);
+       account_popover = GTK_WIDGET (gtk_builder_get_object (priv->builder, "account_popover"));
+       account_box = GTK_WIDGET (gtk_builder_get_object (priv->builder, "account_box"));
+
+       /* Remove existing buttons... */
+       children = gtk_container_get_children (GTK_CONTAINER (account_box));
+       for (GList *l = children; l != NULL; l = l->next)
+               gtk_container_remove (GTK_CONTAINER (account_box), GTK_WIDGET (l->data));
+
+       /* Add new ones... */
+       for (guint i = 0; i < auth_array->len; i++) {
+               GsAuth *auth = g_ptr_array_index (auth_array, i);
+               add_buttons_for_auth (shell, auth);
+
+               /* Add sepeartor between each block */
+               if (i < auth_array->len - 1) {
+                       GtkWidget *seprator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+                       gtk_widget_show (seprator);
+                       gtk_box_pack_start (GTK_BOX (account_box), seprator, TRUE, TRUE, 0);
+               }
+       }
+
+       gtk_popover_set_relative_to (GTK_POPOVER (account_popover), GTK_WIDGET (button));
+       gtk_popover_popup (GTK_POPOVER (account_popover));
+}
+
 static gboolean
 window_key_press_event (GtkWidget *win, GdkEventKey *event, GsShell *shell)
 {
@@ -1895,6 +2034,12 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
                          G_CALLBACK (search_mode_enabled_cb),
                          shell);
 
+       /* show the account popover when clicking on the account button */
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "account_button"));
+       g_signal_connect (widget, "clicked",
+                         G_CALLBACK (account_button_clicked_cb),
+                         shell);
+
        /* setup buttons */
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_back"));
        g_signal_connect (widget, "clicked",


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