[gnome-control-center/gbsneto/online-accounts-gtk4] online-accounts: Port to GTK4




commit 05216280f436e3d2ed751e0d45b6c286e680caa9
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Mon Jan 17 16:05:05 2022 -0300

    online-accounts: Port to GTK4
    
    This is a massive rewrite of the panel. Because we can't have
    nice things and WebKit2GTK for GTK4 won't be ready in time,
    rework the panel to spawn a new subprocess with a dialog that
    handles online accounts - both creation and editing.

 panels/meson.build                                 |    2 +-
 panels/online-accounts/cc-online-accounts-panel.c  | 1342 ++++++++++----------
 panels/online-accounts/cc-online-accounts-panel.ui |  112 ++
 .../gnome-control-center-goa-helper.c              |  483 +++++++
 panels/online-accounts/meson.build                 |   33 +-
 .../online-accounts/online-accounts.gresource.xml  |    2 +-
 panels/online-accounts/online-accounts.ui          |  237 ----
 shell/cc-panel-loader.c                            |    4 +-
 8 files changed, 1332 insertions(+), 883 deletions(-)
---
diff --git a/panels/meson.build b/panels/meson.build
index d379a9fb1..4498f198a 100644
--- a/panels/meson.build
+++ b/panels/meson.build
@@ -17,7 +17,7 @@ panels = [
   'mouse',
   'multitasking',
   'notifications',
-#  'online-accounts',
+  'online-accounts',
   'power',
   'printers',
   'region',
diff --git a/panels/online-accounts/cc-online-accounts-panel.c 
b/panels/online-accounts/cc-online-accounts-panel.c
index a696b4b8c..2abba524a 100644
--- a/panels/online-accounts/cc-online-accounts-panel.c
+++ b/panels/online-accounts/cc-online-accounts-panel.c
@@ -26,13 +26,16 @@
 
 #define GOA_API_IS_SUBJECT_TO_CHANGE
 #include <goa/goa.h>
-#define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE
-#include <goabackend/goabackend.h>
 
 #include "cc-online-accounts-panel.h"
 #include "cc-online-accounts-resources.h"
 
-#include "list-box-helper.h"
+#ifdef GDK_WINDOWING_X11
+#include <gdk/x11/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/wayland/gdkwayland.h>
+#endif
 
 struct _CcGoaPanel
 {
@@ -40,16 +43,15 @@ struct _CcGoaPanel
 
   GtkFrame      *accounts_frame;
   GtkListBox    *accounts_listbox;
+  GtkWidget     *close_notification_button;
   GtkDialog     *edit_account_dialog;
   GtkHeaderBar  *edit_account_headerbar;
   GtkBox        *editor_box;
-  GtkBox        *new_account_vbox;
   GtkLabel      *notification_label;
   GtkRevealer   *notification_revealer;
   GtkLabel      *offline_label;
   GtkListBox    *providers_listbox;
   GtkButton     *remove_account_button;
-  GtkStack      *stack;
   GtkBox        *accounts_vbox;
 
   GoaClient *client;
@@ -57,44 +59,10 @@ struct _CcGoaPanel
   GoaObject *removed_object;
 
   guint      remove_account_timeout_id;
+  gchar     *window_export_handle;
 };
 
-static gboolean on_edit_account_dialog_delete_event (CcGoaPanel *self);
-
-static void on_listbox_row_activated (CcGoaPanel    *self,
-                                      GtkListBoxRow *activated_row);
-
-static void fill_accounts_listbox (CcGoaPanel *self);
-
-static void on_account_added (GoaClient  *client,
-                              GoaObject  *object,
-                              gpointer    user_data);
-
-static void on_account_changed (GoaClient  *client,
-                                GoaObject  *object,
-                                gpointer    user_data);
-
-static void on_account_removed (GoaClient  *client,
-                                GoaObject  *object,
-                                gpointer    user_data);
-
-static void select_account_by_id (CcGoaPanel    *panel,
-                                  const gchar   *account_id);
-
-static void get_all_providers_cb (GObject      *source,
-                                  GAsyncResult *res,
-                                  gpointer      user_data);
-
-static void show_page_account (CcGoaPanel *panel,
-                               GoaObject  *object);
-
-static void on_remove_button_clicked (CcGoaPanel *self);
-
-static void on_notification_closed (GtkButton  *button,
-                                    CcGoaPanel *self);
-
-static void on_undo_button_clicked (GtkButton  *button,
-                                    CcGoaPanel *self);
+static gboolean remove_account_timeout_cb (gpointer user_data);
 
 CC_PANEL_REGISTER (CcGoaPanel, cc_goa_panel);
 
@@ -103,842 +71,936 @@ enum {
   PROP_PARAMETERS
 };
 
-/* ---------------------------------------------------------------------------------------------------- */
+/* Rows methods */
+
+typedef void (*RowForAccountCallback) (CcGoaPanel *self, GtkWidget *row, GList *other_rows);
 
 static void
-reset_headerbar (CcGoaPanel *self)
+hide_row_for_account_cb (CcGoaPanel *self,
+                         GtkWidget  *row,
+                         GList      *other_rows)
 {
-  gtk_header_bar_set_title (self->edit_account_headerbar, NULL);
-  gtk_header_bar_set_subtitle (self->edit_account_headerbar, NULL);
-  gtk_header_bar_set_show_close_button (self->edit_account_headerbar, TRUE);
-
-  /* Remove any leftover widgets */
-  gtk_container_foreach (GTK_CONTAINER (self->edit_account_headerbar),
-                         (GtkCallback) gtk_widget_destroy,
-                         NULL);
-
+  gtk_widget_hide (row);
+  gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), other_rows != NULL);
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
-
 static void
-add_provider_row (CcGoaPanel  *self,
-                  GoaProvider *provider)
+remove_row_for_account_cb (CcGoaPanel *self,
+                           GtkWidget  *row,
+                           GList      *other_rows)
 {
-  GIcon *icon;
-  GtkWidget *image;
-  GtkWidget *label;
-  GtkWidget *row;
-  GtkWidget *row_grid;
-  gchar *markup;
-  gchar *name;
+  gtk_list_box_remove (self->accounts_listbox, row);
+  gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), other_rows != NULL);
+}
 
-  row = gtk_list_box_row_new ();
+static void
+show_row_for_account_cb (CcGoaPanel *self,
+                         GtkWidget  *row,
+                         GList      *other_rows)
+{
+  gtk_widget_show (row);
+  gtk_widget_show (GTK_WIDGET (self->accounts_frame));
+}
 
-  row_grid = gtk_grid_new ();
-  gtk_widget_show (row_grid);
-  gtk_orientable_set_orientation (GTK_ORIENTABLE (row_grid), GTK_ORIENTATION_HORIZONTAL);
-  gtk_grid_set_column_spacing (GTK_GRID (row_grid), 6);
-  gtk_container_add (GTK_CONTAINER (row), row_grid);
+static void
+modify_row_for_account (CcGoaPanel            *self,
+                        GoaObject             *object,
+                        RowForAccountCallback  callback)
+{
+  GtkWidget *child;
+  GList *children = NULL;
+  GList *l;
 
-  if (provider == NULL)
-    {
-      g_object_set_data (G_OBJECT (row), "goa-provider", NULL);
-      icon = g_themed_icon_new_with_default_fallbacks ("goa-account");
-      name = g_strdup (C_("Online Account", "Other"));
-    }
-  else
+  for (child = gtk_widget_get_first_child (GTK_WIDGET (self->accounts_listbox));
+       child;
+       child = gtk_widget_get_next_sibling (child))
     {
-      g_object_set_data_full (G_OBJECT (row), "goa-provider", g_object_ref (provider), g_object_unref);
-      icon = goa_provider_get_provider_icon (provider, NULL);
-      name = goa_provider_get_provider_name (provider, NULL);
+      children = g_list_prepend (children, child);
     }
 
-  image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
-  gtk_style_context_add_class (gtk_widget_get_style_context (image), "lowres-icon");
-  gtk_widget_show (image);
-  gtk_container_add (GTK_CONTAINER (row_grid), image);
-  g_object_set (image, "margin", 6, NULL);
+  children = g_list_reverse (children);
 
-  markup = g_strdup_printf ("<b>%s</b>", name);
-  label = gtk_label_new (NULL);
-  gtk_widget_show (label);
-  gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
-  gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
-  gtk_label_set_markup (GTK_LABEL (label), markup);
-  gtk_container_add (GTK_CONTAINER (row_grid), label);
+  for (l = children; l != NULL; l = l->next)
+    {
+      GoaObject *row_object;
 
-  gtk_widget_show (row);
-  gtk_container_add (GTK_CONTAINER (self->providers_listbox), row);
+      row_object = g_object_get_data (G_OBJECT (l->data), "goa-object");
+      if (row_object == object)
+        {
+          GtkWidget *row = GTK_WIDGET (l->data);
+
+          children = g_list_remove_link (children, l);
+          callback (self, row, children);
+          g_list_free (l);
+          break;
+        }
+    }
 
-  g_free (markup);
-  g_free (name);
-  g_object_unref (icon);
+  g_list_free (children);
 }
 
-static gint
-sort_providers_func (GtkListBoxRow *a,
-                     GtkListBoxRow *b,
-                     gpointer       user_data)
+/* Auxiliary methods */
+
+static char *
+run_goa_helper_sync (const char *command,
+                     ...)
 {
-  GoaProvider *a_provider, *b_provider;
-  gboolean a_branded, b_branded;
+  g_autoptr(GPtrArray) argv = NULL;
+  g_autofree char *output = NULL;
+  g_autoptr(GError) error = NULL;
+  const char *param;
+  va_list args;
+  int status;
+
+  argv = g_ptr_array_new_with_free_func (g_free);
+  g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/gnome-control-center-goa-helper"));
+  g_ptr_array_add (argv, g_strdup (command));
+
+  va_start (args, command);
+  while ((param = va_arg (args, const char*)) != NULL)
+    g_ptr_array_add (argv, g_strdup (param));
+  va_end (args);
+
+  g_ptr_array_add (argv, NULL);
+
+  if (!g_spawn_sync (NULL,
+                     (char **) argv->pdata,
+                     NULL,
+                     0,
+                     NULL,
+                     NULL,
+                     &output,
+                     NULL,
+                     &status,
+                     &error))
+    {
+      g_warning ("Failed to run online accounts helper: %s", error->message);
+      return NULL;
+    }
 
-  a_provider = g_object_get_data (G_OBJECT (a), "goa-provider");
-  b_provider = g_object_get_data (G_OBJECT (b), "goa-provider");
+  if (!g_spawn_check_wait_status (status, NULL))
+    return NULL;
 
-  a_branded = (goa_provider_get_provider_features (a_provider) & GOA_PROVIDER_FEATURE_BRANDED) != 0;
-  b_branded = (goa_provider_get_provider_features (b_provider) & GOA_PROVIDER_FEATURE_BRANDED) != 0;
+  if (output == NULL || *output == '\0')
+    return NULL;
 
-  if (a_branded != b_branded)
+  return g_steal_pointer (&output);
+}
+
+static void
+run_goa_helper_in_thread_func (GTask        *task,
+                               gpointer      source_object,
+                               gpointer      task_data,
+                               GCancellable *cancellable)
+{
+  g_autofree char *output = NULL;
+  g_autoptr (GError) error = NULL;
+  GPtrArray *argv = task_data;
+  int status;
+
+  g_spawn_sync (NULL,
+                (char **) argv->pdata,
+                NULL, 0, NULL, NULL,
+                &output,
+                NULL,
+                &status,
+                &error);
+
+  if (error)
     {
-      if (a_branded)
-        return -1;
-      else
-        return 1;
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
     }
 
-  return gtk_list_box_row_get_index (b) - gtk_list_box_row_get_index (a);
+  if (!g_spawn_check_wait_status (status, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  g_task_return_pointer (task, g_steal_pointer (&output), g_free);
 }
 
 static void
-add_account (CcGoaPanel  *self,
-             GoaProvider *provider)
+run_goa_helper_async (const gchar         *command,
+                      const gchar         *param,
+                      const gchar         *window_handle,
+                      GCancellable        *cancellable,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data)
 {
-  GoaObject *object;
-  g_autoptr(GError) error = NULL;
+  g_autoptr (GPtrArray) argv = NULL;
+  g_autoptr (GTask) task = NULL;
 
-  gtk_container_foreach (GTK_CONTAINER (self->new_account_vbox),
-                         (GtkCallback) gtk_widget_destroy,
-                         NULL);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  reset_headerbar (self);
+  argv = g_ptr_array_new_with_free_func (g_free);
+  g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/gnome-control-center-goa-helper"));
+  g_ptr_array_add (argv, g_strdup (command));
+  g_ptr_array_add (argv, g_strdup (param));
+  g_ptr_array_add (argv, g_strdup (window_handle));
+  g_ptr_array_add (argv, NULL);
 
-  /* Move to the new account page */
-  gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->new_account_vbox));
-
-  /* Reset the dialog size */
-  gtk_window_resize (GTK_WINDOW (self->edit_account_dialog), 1, 1);
-
-  /* This spins gtk_dialog_run() */
-  object = goa_provider_add_account (provider,
-                                     self->client,
-                                     self->edit_account_dialog,
-                                     self->new_account_vbox,
-                                     &error);
-
-  if (object == NULL)
-    gtk_widget_hide (GTK_WIDGET (self->edit_account_dialog));
-  else
-    show_page_account (self, object);
+  task = g_task_new (NULL, cancellable, callback, user_data);
+  g_task_set_source_tag (task, run_goa_helper_async);
+  g_task_set_task_data (task, g_steal_pointer (&argv), (GDestroyNotify) g_ptr_array_unref);
+  g_task_run_in_thread (task, run_goa_helper_in_thread_func);
 }
 
 static void
-on_provider_row_activated (CcGoaPanel    *self,
-                           GtkListBoxRow *activated_row)
+cancel_notification_timeout (CcGoaPanel *self)
 {
-  GoaProvider *provider;
-
-  provider = g_object_get_data (G_OBJECT (activated_row), "goa-provider");
-
-  add_account (self, provider);
+  g_clear_handle_id (&self->remove_account_timeout_id, g_source_remove);
+  self->removed_object = NULL;
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
-
-static gint
-sort_func (GtkListBoxRow *a,
-           GtkListBoxRow *b,
-           gpointer       user_data)
+static void
+start_remove_account_timeout (CcGoaPanel *self)
 {
-  GoaObject *a_obj, *b_obj;
-  GoaAccount *a_account, *b_account;
+  GoaAccount *account;
+  g_autofree gchar *id = NULL;
+  g_autofree gchar *label = NULL;
+
+  if (self->active_object == NULL)
+    return;
 
-  a_obj = g_object_get_data (G_OBJECT (a), "goa-object");
-  a_account = goa_object_peek_account (a_obj);
+  if (self->removed_object != NULL)
+    gtk_widget_activate (self->close_notification_button);
 
-  b_obj = g_object_get_data (G_OBJECT (b), "goa-object");
-  b_account = goa_object_peek_account (b_obj);
+  self->removed_object = g_steal_pointer (&self->active_object);
 
-  return g_strcmp0 (goa_account_get_id (a_account), goa_account_get_id (b_account));
+  account = goa_object_peek_account (self->removed_object);
+  id = g_strdup_printf ("<b>%s</b>", goa_account_get_presentation_identity (account));
+  /* Translators: The %s is the username (eg., debarshi ray gmail com
+   * or rishi).
+   */
+  label = g_strdup_printf (_("%s removed"), id);
+  gtk_label_set_markup (self->notification_label, label);
+  gtk_revealer_set_reveal_child (self->notification_revealer, TRUE);
+
+  modify_row_for_account (self, self->removed_object, hide_row_for_account_cb);
+  self->remove_account_timeout_id = g_timeout_add_seconds (10, remove_account_timeout_cb, self);
 }
 
 static void
-command_add (CcGoaPanel *panel,
-             GVariant   *parameters)
+on_show_account_finish_cb (GObject      *source_object,
+                           GAsyncResult *result,
+                           gpointer      user_data)
 {
-  GVariant *v = NULL;
-  GoaProvider *provider = NULL;
-  const gchar *provider_name = NULL;
+  CcGoaPanel *self = CC_GOA_PANEL (user_data);
+  g_autofree char *output = NULL;
+  g_autoptr(GError) error = NULL;
 
-  g_assert (panel != NULL);
-  g_assert (parameters != NULL);
+  output = g_task_propagate_pointer (G_TASK (result), &error);
 
-  switch (g_variant_n_children (parameters))
+  if (error)
     {
-      case 2:
-        g_variant_get_child (parameters, 1, "v", &v);
-        if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
-          provider_name = g_variant_get_string (v, NULL);
-        else
-          g_warning ("Wrong type for the second argument (provider name) GVariant, expected 's' but got 
'%s'",
-                     (gchar *)g_variant_get_type (v));
-        g_variant_unref (v);
-        break;
-      default:
-        g_warning ("Unexpected parameters found, ignore request");
-        goto out;
+      g_warning ("Error showing account: %s", error->message);
+      return;
     }
 
-  if (provider_name != NULL)
-    {
-      provider = goa_provider_get_for_provider_type (provider_name);
-      if (provider == NULL)
-        {
-          g_warning ("Unable to get a provider for type '%s'", provider_name);
-          goto out;
-        }
-
-      add_account (panel, provider);
-    }
+  if (g_strcmp0 (output, "remove") == 0)
+    start_remove_account_timeout (self);
 
-out:
-  g_clear_object (&provider);
+  self->active_object = NULL;
 }
 
 static void
-cc_goa_panel_set_property (GObject *object,
-                        guint property_id,
-                        const GValue *value,
-                        GParamSpec *pspec)
+show_account (CcGoaPanel *self,
+              GoaObject  *object)
 {
-  switch (property_id)
-    {
-      case PROP_PARAMETERS:
-        {
-          GVariant *parameters, *v;
-          const gchar *first_arg = NULL;
+  GoaAccount *account;
 
-          parameters = g_value_get_variant (value);
-          if (parameters == NULL)
-            return;
+  if (!self->window_export_handle)
+    return;
 
-          if (g_variant_n_children (parameters) > 0)
-            {
-                g_variant_get_child (parameters, 0, "v", &v);
-                if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
-                  first_arg = g_variant_get_string (v, NULL);
-                else
-                  g_warning ("Wrong type for the second argument GVariant, expected 's' but got '%s'",
-                             (gchar *)g_variant_get_type (v));
-                g_variant_unref (v);
-            }
+  self->active_object = g_object_ref (object);
 
-          if (g_strcmp0 (first_arg, "add") == 0)
-            command_add (CC_GOA_PANEL (object), parameters);
-          else if (first_arg != NULL)
-            select_account_by_id (CC_GOA_PANEL (object), first_arg);
+  account = goa_object_peek_account (object);
+  run_goa_helper_async ("show-account",
+                        goa_account_get_id (account),
+                        self->window_export_handle,
+                        cc_panel_get_cancellable (CC_PANEL (self)),
+                        on_show_account_finish_cb,
+                        self);
+}
 
-          return;
-        }
+static void
+on_create_account_finish_cb (GObject      *source_object,
+                             GAsyncResult *result,
+                             gpointer      user_data)
+{
+  CcGoaPanel *self = CC_GOA_PANEL (user_data);
+  g_autofree char *new_account_id = NULL;
+  g_autoptr(GoaObject) object = NULL;
+  g_autoptr(GError) error = NULL;
+
+  new_account_id = g_task_propagate_pointer (G_TASK (result), &error);
+
+  if (error)
+    {
+      g_warning ("Error showing account: %s", error->message);
+      return;
     }
 
-  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+  if (new_account_id)
+    object = goa_client_lookup_by_id (self->client, new_account_id);
+
+  if (object)
+    show_account (self, object);
 }
 
 static void
-cc_goa_panel_dispose (GObject *object)
+create_account (CcGoaPanel *self,
+                GVariant   *provider)
 {
-  CcGoaPanel *panel = CC_GOA_PANEL (object);
+  g_autofree char *provider_type = NULL;
+
+  if (!self->window_export_handle)
+    return;
 
-  /* Must be destroyed in dispose, not finalize. */
-  g_clear_pointer ((GtkWidget **) &panel->edit_account_dialog, gtk_widget_destroy);
+  g_variant_get (provider, "(ssviu)", &provider_type, NULL, NULL, NULL, NULL);
 
-  G_OBJECT_CLASS (cc_goa_panel_parent_class)->dispose (object);
+  run_goa_helper_async ("create-account",
+                        provider_type,
+                        self->window_export_handle,
+                        cc_panel_get_cancellable (CC_PANEL (self)),
+                        on_create_account_finish_cb,
+                        self);
 }
 
 static void
-cc_goa_panel_finalize (GObject *object)
+add_provider_row (CcGoaPanel *self,
+                  GVariant   *provider)
 {
-  CcGoaPanel *panel = CC_GOA_PANEL (object);
+  g_autofree char *markup = NULL;
+  g_autofree char *name = NULL;
+  g_autoptr(GIcon) icon = NULL;
+  GtkWidget *image;
+  GtkWidget *row;
 
-  if (panel->removed_object != NULL)
+  row = adw_action_row_new ();
+  gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE);
+
+  if (provider == NULL)
     {
-      g_autoptr(GError) error = NULL;
-      goa_account_call_remove_sync (goa_object_peek_account (panel->removed_object),
-                                    NULL, /* GCancellable */
-                                    &error);
+      g_object_set_data (G_OBJECT (row), "goa-provider", NULL);
+      icon = g_themed_icon_new_with_default_fallbacks ("goa-account");
+      name = g_strdup (C_("Online Account", "Other"));
+    }
+  else
+    {
+      g_autoptr(GVariant) icon_variant = NULL;
 
-      if (error != NULL)
-        {
-          g_warning ("Error removing account: %s (%s, %d)",
-                     error->message,
-                     g_quark_to_string (error->domain),
-                     error->code);
-        }
+      g_object_set_data_full (G_OBJECT (row),
+                              "goa-provider",
+                              g_variant_ref (provider),
+                              (GDestroyNotify) g_variant_unref);
+
+      g_variant_get (provider, "(ssviu)",
+                     NULL,
+                     &name,
+                     &icon_variant,
+                     NULL,
+                     NULL);
+
+      icon = g_icon_deserialize (icon_variant);
     }
 
-  g_clear_object (&panel->client);
+  image = gtk_image_new_from_gicon (icon);
+  gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
+  gtk_widget_add_css_class (image, "lowres-icon");
+  adw_action_row_add_prefix (ADW_ACTION_ROW (row), image);
 
-  G_OBJECT_CLASS (cc_goa_panel_parent_class)->finalize (object);
+  markup = g_strdup_printf ("<b>%s</b>", name);
+  adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), markup);
+
+  gtk_list_box_append (self->providers_listbox, row);
 }
 
 static void
-cc_goa_panel_init (CcGoaPanel *panel)
+list_providers (CcGoaPanel *self)
 {
-  GNetworkMonitor *monitor;
+  g_autoptr(GVariant) providers_variant = NULL;
   g_autoptr(GError) error = NULL;
+  g_autofree char *providers = NULL;
+  GVariantIter iter;
+  GVariant *provider;
+
+  providers = run_goa_helper_sync ("list-providers", NULL);
+  providers_variant = g_variant_parse (G_VARIANT_TYPE ("a(ssviu)"),
+                                       providers,
+                                       NULL,
+                                       NULL,
+                                       &error);
+
+  if (error)
+    {
+      g_warning ("Error listing providers: %s", error->message);
+      return;
+    }
 
-  g_resources_register (cc_online_accounts_get_resource ());
+  g_variant_iter_init (&iter, providers_variant);
 
-  gtk_widget_init_template (GTK_WIDGET (panel));
+  while ((provider = g_variant_iter_next_value (&iter)))
+    add_provider_row (self, provider);
+}
 
-  gtk_list_box_set_header_func (panel->accounts_listbox,
-                                cc_list_box_update_header_func,
-                                NULL,
-                                NULL);
-  gtk_list_box_set_sort_func (panel->accounts_listbox,
-                              sort_func,
-                              panel,
-                              NULL);
+static void
+add_account (CcGoaPanel *self,
+             GoaObject  *object)
+{
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GIcon) gicon = NULL;
+  GoaAccount *account;
+  GtkWidget *row, *icon;
 
-  gtk_list_box_set_header_func (panel->providers_listbox,
-                                cc_list_box_update_header_func,
-                                NULL,
-                                NULL);
-  gtk_list_box_set_sort_func (panel->providers_listbox,
-                              sort_providers_func,
-                              panel,
-                              NULL);
+  account = goa_object_peek_account (object);
 
-  monitor = g_network_monitor_get_default();
+  row = adw_action_row_new ();
+  g_object_set_data (G_OBJECT (row), "goa-object", object);
+  gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE);
 
-  g_object_bind_property (monitor, "network-available",
-                          panel->offline_label, "visible",
-                          G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+  adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row),
+                                 goa_account_get_provider_name (account));
+  adw_action_row_set_subtitle (ADW_ACTION_ROW (row),
+                               goa_account_get_presentation_identity (account));
 
-  g_object_bind_property (monitor, "network-available",
-                          panel->providers_listbox, "sensitive",
-                          G_BINDING_SYNC_CREATE);
+  /* The provider icon */
+  icon = gtk_image_new ();
+  gtk_widget_add_css_class (icon, "lowres-icon");
+  gtk_image_set_icon_size (GTK_IMAGE (icon), GTK_ICON_SIZE_LARGE);
 
-  /* TODO: probably want to avoid _sync() ... */
-  panel->client = goa_client_new_sync (cc_panel_get_cancellable (CC_PANEL (panel)), &error);
-  if (panel->client == NULL)
+  gicon = g_icon_new_for_string (goa_account_get_provider_icon (account), &error);
+  if (error != NULL)
     {
-      g_warning ("Error getting a GoaClient: %s (%s, %d)",
-                 error->message, g_quark_to_string (error->domain), error->code);
-      gtk_widget_set_sensitive (GTK_WIDGET (panel), FALSE);
-      return;
+      g_warning ("Error creating GIcon for account: %s (%s, %d)",
+                 error->message,
+                 g_quark_to_string (error->domain),
+                 error->code);
+    }
+  else
+    {
+      gtk_image_set_from_gicon (GTK_IMAGE (icon), gicon);
     }
+  adw_action_row_add_prefix (ADW_ACTION_ROW (row), icon);
 
-  g_signal_connect (panel->client,
-                    "account-added",
-                    G_CALLBACK (on_account_added),
-                    panel);
+  /* "Needs attention" icon */
+  icon = gtk_image_new_from_icon_name ("dialog-warning-symbolic");
+  g_object_bind_property (account,
+                          "attention-needed",
+                          icon,
+                          "visible",
+                          G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+  adw_action_row_add_suffix (ADW_ACTION_ROW (row), icon);
 
-  g_signal_connect (panel->client,
-                    "account-changed",
-                    G_CALLBACK (on_account_changed),
-                    panel);
+  /* Add to the listbox */
+  gtk_list_box_append (self->accounts_listbox, row);
+  gtk_widget_show (GTK_WIDGET (self->accounts_frame));
+}
 
-  g_signal_connect (panel->client,
-                    "account-removed",
-                    G_CALLBACK (on_account_removed),
-                    panel);
+static void
+fill_accounts_listbox (CcGoaPanel *self)
+{
+  g_autolist(GoaAccount) accounts = NULL;
+  GList *l;
 
-  fill_accounts_listbox (panel);
+  accounts = goa_client_get_accounts (self->client);
 
-  gtk_widget_show (GTK_WIDGET (panel));
+  for (l = accounts; l != NULL; l = l->next)
+    add_account (self, l->data);
 }
 
-static const char *
-cc_goa_panel_get_help_uri (CcPanel *panel)
+#ifdef GDK_WINDOWING_WAYLAND
+static void
+wayland_window_exported_cb (GdkToplevel *toplevel,
+                            const char  *handle,
+                            gpointer     data)
+
 {
-  return "help:gnome-help/accounts";
+  CcGoaPanel *self = data;
+
+  self->window_export_handle = g_strdup_printf ("wayland:%s", handle);
 }
+#endif
 
 static void
-cc_goa_panel_constructed (GObject *object)
+export_window_handle (CcGoaPanel *self)
 {
-  CcGoaPanel *self = CC_GOA_PANEL (object);
-  GtkWindow *parent;
-
-  /* Setup account editor dialog */
-  parent = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))));
+  GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self));
 
-  gtk_window_set_transient_for (GTK_WINDOW (self->edit_account_dialog), parent);
+#ifdef GDK_WINDOWING_X11
+  if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (native))))
+    {
+      GdkSurface *surface = gtk_native_get_surface (native);
+      guint32 xid = (guint32) gdk_x11_surface_get_xid (surface);
 
-  goa_provider_get_all (get_all_providers_cb, g_object_ref_sink (self));
+      self->window_export_handle = g_strdup_printf ("x11:%x", xid);
+    }
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+  if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (native))))
+    {
+      GdkSurface *surface = gtk_native_get_surface (native);
 
-  G_OBJECT_CLASS (cc_goa_panel_parent_class)->constructed (object);
+      gdk_wayland_toplevel_export_handle (GDK_TOPLEVEL (surface),
+                                          wayland_window_exported_cb,
+                                          self,
+                                          NULL);
+    }
+#endif
 }
 
 static void
-cc_goa_panel_class_init (CcGoaPanelClass *klass)
+unexport_window_handle (CcGoaPanel *self)
 {
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
+  if (!self->window_export_handle)
+    return;
 
-  panel_class->get_help_uri = cc_goa_panel_get_help_uri;
+#ifdef GDK_WINDOWING_WAYLAND
+  GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self));
 
-  object_class->set_property = cc_goa_panel_set_property;
-  object_class->finalize = cc_goa_panel_finalize;
-  object_class->constructed = cc_goa_panel_constructed;
-  object_class->dispose = cc_goa_panel_dispose;
+  if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (native))))
+    {
+      GdkSurface *surface = gtk_native_get_surface (native);
+      gdk_wayland_toplevel_unexport_handle (GDK_TOPLEVEL (surface));
+    }
+#endif
+}
 
-  g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
+static void
+select_account_by_id (CcGoaPanel  *self,
+                      const gchar *account_id)
+{
+  GtkWidget *child;
 
-  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/control-center/online-accounts/online-accounts.ui");
+  for (child = gtk_widget_get_first_child (GTK_WIDGET (self->accounts_listbox));
+       child;
+       child = gtk_widget_get_next_sibling (child))
+    {
+      GoaAccount *account;
+      GoaObject *row_object;
 
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, accounts_frame);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, accounts_listbox);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, accounts_vbox);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, edit_account_dialog);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, edit_account_headerbar);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, editor_box);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, new_account_vbox);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, notification_label);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, notification_revealer);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, offline_label);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, providers_listbox);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, remove_account_button);
-  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, stack);
+      row_object = g_object_get_data (G_OBJECT (child), "goa-object");
+      account = goa_object_peek_account (row_object);
 
-  gtk_widget_class_bind_template_callback (widget_class, on_edit_account_dialog_delete_event);
-  gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated);
-  gtk_widget_class_bind_template_callback (widget_class, on_notification_closed);
-  gtk_widget_class_bind_template_callback (widget_class, on_provider_row_activated);
-  gtk_widget_class_bind_template_callback (widget_class, on_remove_button_clicked);
-  gtk_widget_class_bind_template_callback (widget_class, on_undo_button_clicked);
+      if (g_strcmp0 (goa_account_get_id (account), account_id) == 0)
+        {
+          show_account (self, row_object);
+          break;
+        }
+    }
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
-
 static void
-show_page_nothing_selected (CcGoaPanel *panel)
+command_add (CcGoaPanel *self,
+             GVariant   *parameters)
 {
-}
+  const gchar *provider_name = NULL;
+  GVariant *v = NULL;
 
-static void
-show_page_account (CcGoaPanel  *panel,
-                   GoaObject *object)
-{
-  GList *children;
-  GList *l;
-  GoaProvider *provider;
-  GoaAccount *account;
-  gboolean is_locked;
-  const gchar *provider_name;
-  const gchar *provider_type;
-  gchar *title;
+  g_assert (self != NULL);
+  g_assert (parameters != NULL);
 
-  provider = NULL;
+  switch (g_variant_n_children (parameters))
+    {
+      case 2:
+        g_variant_get_child (parameters, 1, "v", &v);
+        if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
+          provider_name = g_variant_get_string (v, NULL);
+        else
+          g_warning ("Wrong type for the second argument (provider name) GVariant, expected 's' but got 
'%s'",
+                     (gchar *)g_variant_get_type (v));
+        g_variant_unref (v);
+        break;
 
-  panel->active_object = object;
-  reset_headerbar (panel);
+      default:
+        g_warning ("Unexpected parameters found, ignore request");
+        return;
+    }
 
-  /* Move to the account editor page */
-  gtk_stack_set_visible_child (panel->stack, GTK_WIDGET (panel->editor_box));
+  if (provider_name != NULL)
+    {
+      GtkWidget *child;
+      GVariant *provider;
 
-  /* Out with the old */
-  children = gtk_container_get_children (GTK_CONTAINER (panel->accounts_vbox));
-  for (l = children; l != NULL; l = l->next)
-    gtk_container_remove (GTK_CONTAINER (panel->accounts_vbox), GTK_WIDGET (l->data));
-  g_list_free (children);
+      for (child = gtk_widget_get_first_child (GTK_WIDGET (self->providers_listbox));
+           child;
+           child = gtk_widget_get_next_sibling (child))
+        {
+          g_autofree gchar *provider_type = NULL;
 
-  account = goa_object_peek_account (object);
+          provider = g_object_get_data (G_OBJECT (child), "goa-provider");
+          g_variant_get (provider, "(ssviu)", &provider_type, NULL, NULL, NULL, NULL);
 
-  is_locked = goa_account_get_is_locked (account);
-  gtk_widget_set_visible (GTK_WIDGET (panel->remove_account_button), !is_locked);
+          if (g_strcmp0 (provider_type, provider_name) == 0)
+            break;
+        }
 
-  provider_type = goa_account_get_provider_type (account);
-  provider = goa_provider_get_for_provider_type (provider_type);
+      if (child == NULL)
+        {
+          g_warning ("Unable to get a provider for type '%s'", provider_name);
+          return;
+        }
 
-  if (provider != NULL)
-    {
-      goa_provider_show_account (provider,
-                                 panel->client,
-                                 object,
-                                 panel->accounts_vbox,
-                                 NULL,
-                                 NULL);
-      /*
-       * The above call doesn't set any widgets to visible, so we have to do that.
-       * https://gitlab.gnome.org/GNOME/gnome-online-accounts/issues/56
-       */
-      gtk_widget_show_all (GTK_WIDGET (panel->accounts_vbox));
+      create_account (self, provider);
     }
-
-  provider_name = goa_account_get_provider_name (account);
-  /* translators: This is the title of the "Show Account" dialog. The
-   * %s is the name of the provider. e.g., 'Google'. */
-  title = g_strdup_printf (_("%s Account"), provider_name);
-  gtk_header_bar_set_title (panel->edit_account_headerbar, title);
-  g_free (title);
-
-  /* Reset the dialog size */
-  gtk_window_resize (GTK_WINDOW (panel->edit_account_dialog), 1, 1);
-
-  gtk_widget_show (GTK_WIDGET (panel->accounts_vbox));
-  gtk_widget_show (GTK_WIDGET (panel->edit_account_dialog));
-
-  g_clear_object (&provider);
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
+/* Callbacks */
 
-static void
-select_account_by_id (CcGoaPanel    *panel,
-                      const gchar *account_id)
+static gint
+sort_accounts_func (GtkListBoxRow *a,
+                    GtkListBoxRow *b,
+                    gpointer       user_data)
 {
-  GList *children, *l;
-
-  children = gtk_container_get_children (GTK_CONTAINER (panel->accounts_listbox));
-
-  for (l = children; l != NULL; l = l->next)
-    {
-      GoaAccount *account;
-      GoaObject *row_object;
+  GoaAccount *a_account, *b_account;
+  GoaObject *a_object, *b_object;
 
-      row_object = g_object_get_data (l->data, "goa-object");
-      account = goa_object_peek_account (row_object);
+  a_object = g_object_get_data (G_OBJECT (a), "goa-object");
+  a_account = goa_object_peek_account (a_object);
 
-      if (g_strcmp0 (goa_account_get_id (account), account_id) == 0)
-        {
-          show_page_account (panel, row_object);
-          break;
-        }
-    }
+  b_object = g_object_get_data (G_OBJECT (b), "goa-object");
+  b_account = goa_object_peek_account (b_object);
 
-  g_list_free (children);
+  return g_strcmp0 (goa_account_get_id (a_account), goa_account_get_id (b_account));
 }
 
-static gboolean
-on_edit_account_dialog_delete_event (CcGoaPanel *self)
+static gint
+sort_providers_func (GtkListBoxRow *a,
+                     GtkListBoxRow *b,
+                     gpointer       user_data)
 {
-  self->active_object = NULL;
-  gtk_widget_hide (GTK_WIDGET (self->edit_account_dialog));
-  return TRUE;
-}
+  GVariant *a_provider, *b_provider;
+  gboolean a_branded, b_branded;
+  gint a_features, b_features;
 
-static void
-on_listbox_row_activated (CcGoaPanel    *self,
-                          GtkListBoxRow *activated_row)
-{
-  GoaObject *object;
+  a_provider = g_object_get_data (G_OBJECT (a), "goa-provider");
+  b_provider = g_object_get_data (G_OBJECT (b), "goa-provider");
 
-  object = g_object_get_data (G_OBJECT (activated_row), "goa-object");
-  show_page_account (self, object);
-}
+  g_variant_get (a_provider, "(ssviu)", NULL, NULL, NULL, &a_features, NULL);
+  g_variant_get (b_provider, "(ssviu)", NULL, NULL, NULL, &b_features, NULL);
 
-static void
-fill_accounts_listbox (CcGoaPanel *self)
-{
-  GList *accounts, *l;
+  /* FIXME: this needs to go away once libgoa-backend is ported to GTK4 */
+#define FEATURE_BRANDED (1 << 1)
 
-  accounts = goa_client_get_accounts (self->client);
+  a_branded = (a_features & FEATURE_BRANDED) != 0;
+  b_branded = (a_features & FEATURE_BRANDED) != 0;
 
-  if (accounts == NULL)
-    {
-      show_page_nothing_selected (self);
-    }
-  else
+#undef FEATURE_BRANDED
+
+  if (a_branded != b_branded)
     {
-      for (l = accounts; l != NULL; l = l->next)
-        on_account_added (self->client, l->data, self);
+      if (a_branded)
+        return -1;
+      else
+        return 1;
     }
 
-  g_list_free_full (accounts, g_object_unref);
+  return gtk_list_box_row_get_index (b) - gtk_list_box_row_get_index (a);
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
-
-typedef void (*RowForAccountCallback) (CcGoaPanel *self, GtkWidget *row, GList *other_rows);
-
 static void
-hide_row_for_account (CcGoaPanel *self, GtkWidget *row, GList *other_rows)
+on_account_added_cb (GoaClient  *client,
+                     GoaObject  *object,
+                     CcGoaPanel *self)
 {
-  gtk_widget_hide (row);
-  gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), other_rows != NULL);
+  add_account (self, object);
 }
 
 static void
-remove_row_for_account (CcGoaPanel *self, GtkWidget *row, GList *other_rows)
+on_account_changed_cb (GoaClient *client,
+                       GoaObject *object,
+                       CcGoaPanel *self)
 {
-  gtk_widget_destroy (row);
-  gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), other_rows != NULL);
+  if (self->active_object == object)
+    show_account (self, self->active_object);
 }
 
 static void
-show_row_for_account (CcGoaPanel *self, GtkWidget *row, GList *other_rows)
+on_account_removed_cb (GoaClient  *client,
+                       GoaObject  *object,
+                       CcGoaPanel *self)
 {
-  gtk_widget_show (row);
-  gtk_widget_show (GTK_WIDGET (self->accounts_frame));
+  modify_row_for_account (self, object, remove_row_for_account_cb);
 }
 
 static void
-modify_row_for_account (CcGoaPanel *self,
-                        GoaObject *object,
-                        RowForAccountCallback callback)
+on_accounts_listbox_row_activated (CcGoaPanel    *self,
+                                   GtkListBoxRow *activated_row)
 {
-  GList *children, *l;
+  GoaObject *object = g_object_get_data (G_OBJECT (activated_row), "goa-object");
 
-  children = gtk_container_get_children (GTK_CONTAINER (self->accounts_listbox));
+  show_account (self, object);
+}
 
-  for (l = children; l != NULL; l = l->next)
-    {
-      GoaObject *row_object;
+static void
+on_client_remove_account_finish_cb (GoaAccount   *account,
+                                    GAsyncResult *res,
+                                    CcGoaPanel   *self)
+{
+  g_autoptr(GError) error = NULL;
 
-      row_object = g_object_get_data (G_OBJECT (l->data), "goa-object");
-      if (row_object == object)
-        {
-          GtkWidget *row = GTK_WIDGET (l->data);
+  goa_account_call_remove_finish (account, res, &error);
 
-          children = g_list_remove_link (children, l);
-          callback (self, row, children);
-          g_list_free (l);
-          break;
-        }
+  if (error)
+    {
+      GtkWidget *dialog;
+      dialog = gtk_message_dialog_new (GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL 
(self)))),
+                                       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                       GTK_MESSAGE_ERROR,
+                                       GTK_BUTTONS_CLOSE,
+                                       _("Error removing account"));
+      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                                "%s",
+                                                error->message);
+      gtk_window_present (GTK_WINDOW (dialog));
     }
 
-  g_list_free (children);
+  g_object_unref (self);
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
-
 static void
-on_account_added (GoaClient *client,
-                  GoaObject *object,
-                  gpointer   user_data)
+on_notification_closed_cb (GtkButton  *button,
+                           CcGoaPanel *self)
 {
-  CcGoaPanel *self = user_data;
-  GtkWidget *row, *icon, *label, *box;
-  GoaAccount *account;
-  GIcon *gicon;
-  gchar* title = NULL;
-  g_autoptr(GError) error = NULL;
-
-  account = goa_object_peek_account (object);
+  goa_account_call_remove (goa_object_peek_account (self->removed_object),
+                           cc_panel_get_cancellable (CC_PANEL (self)),
+                           (GAsyncReadyCallback) on_client_remove_account_finish_cb,
+                           g_object_ref (self));
 
-  /* The main grid */
-  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
-  gtk_widget_show (box);
+  gtk_revealer_set_reveal_child (self->notification_revealer, FALSE);
 
-  /* The provider icon */
-  icon = gtk_image_new ();
-  gtk_widget_show (icon);
+  cancel_notification_timeout (self);
+  self->removed_object = NULL;
+}
 
-  gicon = g_icon_new_for_string (goa_account_get_provider_icon (account), &error);
-  if (error != NULL)
-    {
-      g_warning ("Error creating GIcon for account: %s (%s, %d)",
-                 error->message,
-                 g_quark_to_string (error->domain),
-                 error->code);
-    }
-  else
-    {
-      gtk_image_set_from_gicon (GTK_IMAGE (icon), gicon, GTK_ICON_SIZE_DIALOG);
-    }
+static void
+on_undo_button_clicked_cb (GtkButton  *button,
+                           CcGoaPanel *self)
+{
+  /* Simply show the account row and hide the notification */
+  modify_row_for_account (self, self->removed_object, show_row_for_account_cb);
+  gtk_revealer_set_reveal_child (self->notification_revealer, FALSE);
 
-  g_object_set (icon, "margin", 6, NULL);
+  cancel_notification_timeout (self);
+  self->removed_object = NULL;
+}
 
-  gtk_container_add (GTK_CONTAINER (box), icon);
+static void
+on_provider_row_activated_cb (CcGoaPanel    *self,
+                              GtkListBoxRow *activated_row)
+{
+  GVariant *provider = g_object_get_data (G_OBJECT (activated_row), "goa-provider");
 
-  /* The name of the provider */
-  title = g_strdup_printf ("<b>%s</b>\n<small>%s</small>",
-                           goa_account_get_provider_name (account),
-                           goa_account_get_presentation_identity (account));
+  create_account (self, provider);
+}
 
-  label = gtk_label_new (NULL);
-  gtk_label_set_markup (GTK_LABEL (label), title);
-  gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
-  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
-  gtk_widget_set_hexpand (label, TRUE);
-  gtk_widget_show (label);
-  gtk_container_add (GTK_CONTAINER (box), label);
+static gboolean
+remove_account_timeout_cb (gpointer user_data)
+{
+  CcGoaPanel *self = CC_GOA_PANEL (user_data);
 
-  /* "Needs attention" icon */
-  icon = gtk_image_new_from_icon_name ("dialog-warning-symbolic", GTK_ICON_SIZE_BUTTON);
-  gtk_widget_hide (icon);
-  g_object_set (icon, "margin_end", 30, NULL);
-  g_object_bind_property (goa_object_peek_account (object),
-                          "attention-needed",
-                          icon,
-                          "visible",
-                          G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
-  gtk_container_add (GTK_CONTAINER (box), icon);
+  gtk_widget_activate (self->close_notification_button);
 
-  /* The row */
-  row = gtk_list_box_row_new ();
-  gtk_widget_show (row);
-  g_object_set_data (G_OBJECT (row), "goa-object", object);
-  gtk_container_add (GTK_CONTAINER (row), box);
+  return G_SOURCE_REMOVE;
+}
 
-  /* Add to the listbox */
-  gtk_container_add (GTK_CONTAINER (self->accounts_listbox), row);
-  gtk_widget_show (GTK_WIDGET (self->accounts_frame));
+/* CcPanel overrides */
 
-  g_clear_pointer (&title, g_free);
-  g_clear_object (&gicon);
+static const char *
+cc_goa_panel_get_help_uri (CcPanel *panel)
+{
+  return "help:gnome-help/accounts";
 }
 
+/* GtkWidget overrides */
+
 static void
-on_account_changed (GoaClient  *client,
-                    GoaObject  *object,
-                    gpointer    user_data)
+cc_goa_panel_realize (GtkWidget *widget)
 {
-  CcGoaPanel *panel = CC_GOA_PANEL (user_data);
+  GTK_WIDGET_CLASS (cc_goa_panel_parent_class)->realize (widget);
 
-  if (panel->active_object != object)
-    return;
-
-  show_page_account (panel, panel->active_object);
+  export_window_handle (CC_GOA_PANEL (widget));
 }
 
 static void
-on_account_removed (GoaClient *client,
-                    GoaObject *object,
-                    gpointer   user_data)
+cc_goa_panel_unrealize (GtkWidget *widget)
 {
-  CcGoaPanel *self = user_data;
-  modify_row_for_account (self, object, remove_row_for_account);
+  unexport_window_handle (CC_GOA_PANEL (widget));
+
+  GTK_WIDGET_CLASS (cc_goa_panel_parent_class)->unrealize (widget);
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
+/* GObject overrides */
 
 static void
-get_all_providers_cb (GObject      *source,
-                      GAsyncResult *res,
-                      gpointer      user_data)
+cc_goa_panel_set_property (GObject      *object,
+                           guint         property_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
 {
-  g_autoptr(CcGoaPanel) self = user_data;
-  GList *providers;
-  GList *l;
-  g_autoptr(GError) error = NULL;
-
-  providers = NULL;
-  if (!goa_provider_get_all_finish (&providers, res, &error))
+  switch (property_id)
     {
-      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-        g_warning ("Failed to get GOA providers: %s", error->message);
+      case PROP_PARAMETERS:
+        {
+          GVariant *parameters, *v;
+          const gchar *first_arg = NULL;
 
-      return;
-    }
+          parameters = g_value_get_variant (value);
+          if (parameters == NULL)
+            return;
 
-  for (l = providers; l != NULL; l = l->next)
-    {
-      GoaProvider *provider;
-      provider = GOA_PROVIDER (l->data);
+          if (g_variant_n_children (parameters) > 0)
+            {
+                g_variant_get_child (parameters, 0, "v", &v);
+                if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
+                  first_arg = g_variant_get_string (v, NULL);
+                else
+                  g_warning ("Wrong type for the second argument GVariant, expected 's' but got '%s'",
+                             (gchar *)g_variant_get_type (v));
+                g_variant_unref (v);
+            }
 
-      add_provider_row (self, provider);
+          if (g_strcmp0 (first_arg, "add") == 0)
+            command_add (CC_GOA_PANEL (object), parameters);
+          else if (first_arg != NULL)
+            select_account_by_id (CC_GOA_PANEL (object), first_arg);
+
+          return;
+        }
     }
 
-  g_list_free_full (providers, g_object_unref);
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
-
-/* ---------------------------------------------------------------------------------------------------- */
-
 static void
-cancel_notification_timeout (CcGoaPanel *self)
+cc_goa_panel_constructed (GObject *object)
 {
-  if (self->remove_account_timeout_id == 0)
-    return;
+  CcGoaPanel *self = CC_GOA_PANEL (object);
 
-  g_source_remove (self->remove_account_timeout_id);
+  G_OBJECT_CLASS (cc_goa_panel_parent_class)->constructed (object);
 
-  self->remove_account_timeout_id = 0;
+  list_providers (self);
 }
 
 static void
-remove_account_cb (GoaAccount    *account,
-                   GAsyncResult  *res,
-                   gpointer       user_data)
+cc_goa_panel_finalize (GObject *object)
 {
-  CcGoaPanel *panel = CC_GOA_PANEL (user_data);
-  g_autoptr(GError) error = NULL;
+  CcGoaPanel *panel = CC_GOA_PANEL (object);
 
-  if (!goa_account_call_remove_finish (account, res, &error))
+  if (panel->removed_object != NULL)
     {
-      GtkWidget *dialog;
-      dialog = gtk_message_dialog_new (GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL 
(panel)))),
-                                       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-                                       GTK_MESSAGE_ERROR,
-                                       GTK_BUTTONS_CLOSE,
-                                       _("Error removing account"));
-      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                                                "%s",
-                                                error->message);
-      gtk_widget_show (dialog);
-      gtk_dialog_run (GTK_DIALOG (dialog));
-      gtk_widget_destroy (dialog);
+      g_autoptr(GError) error = NULL;
+      goa_account_call_remove_sync (goa_object_peek_account (panel->removed_object),
+                                    NULL, /* GCancellable */
+                                    &error);
+
+      if (error != NULL)
+        {
+          g_warning ("Error removing account: %s (%s, %d)",
+                     error->message,
+                     g_quark_to_string (error->domain),
+                     error->code);
+        }
     }
-  g_object_unref (panel);
+
+  g_clear_object (&panel->client);
+
+  G_OBJECT_CLASS (cc_goa_panel_parent_class)->finalize (object);
 }
 
 static void
-on_notification_closed (GtkButton  *button,
-                        CcGoaPanel *self)
+cc_goa_panel_class_init (CcGoaPanelClass *klass)
 {
-  goa_account_call_remove (goa_object_peek_account (self->removed_object),
-                           cc_panel_get_cancellable (CC_PANEL (self)),
-                           (GAsyncReadyCallback) remove_account_cb,
-                           g_object_ref (self));
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
 
-  gtk_revealer_set_reveal_child (self->notification_revealer, FALSE);
+  panel_class->get_help_uri = cc_goa_panel_get_help_uri;
 
-  cancel_notification_timeout (self);
-  self->removed_object = NULL;
-}
+  object_class->set_property = cc_goa_panel_set_property;
+  object_class->finalize = cc_goa_panel_finalize;
+  object_class->constructed = cc_goa_panel_constructed;
 
-static void
-on_undo_button_clicked (GtkButton  *button,
-                        CcGoaPanel *self)
-{
-  /* Simply show the account row and hide the notification */
-  modify_row_for_account (self, self->removed_object, show_row_for_account);
-  gtk_revealer_set_reveal_child (self->notification_revealer, FALSE);
+  widget_class->realize = cc_goa_panel_realize;
+  widget_class->unrealize = cc_goa_panel_unrealize;
 
-  cancel_notification_timeout (self);
-  self->removed_object = NULL;
-}
+  g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
 
-static gboolean
-on_remove_account_timeout (gpointer user_data)
-{
-  on_notification_closed (NULL, user_data);
-  return G_SOURCE_REMOVE;
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/control-center/online-accounts/cc-online-accounts-panel.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, accounts_frame);
+  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, accounts_listbox);
+  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, close_notification_button);
+  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, notification_label);
+  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, notification_revealer);
+  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, offline_label);
+  gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, providers_listbox);
+
+  gtk_widget_class_bind_template_callback (widget_class, on_accounts_listbox_row_activated);
+  gtk_widget_class_bind_template_callback (widget_class, on_notification_closed_cb);
+  gtk_widget_class_bind_template_callback (widget_class, on_provider_row_activated_cb);
+  gtk_widget_class_bind_template_callback (widget_class, on_undo_button_clicked_cb);
 }
 
 static void
-on_remove_button_clicked (CcGoaPanel *panel)
+cc_goa_panel_init (CcGoaPanel *self)
 {
-  GoaAccount *account;
-  g_autofree gchar *id = NULL;
-  g_autofree gchar *label = NULL;
+  g_autoptr(GError) error = NULL;
+  GNetworkMonitor *monitor;
 
-  if (panel->active_object == NULL)
-    return;
+  g_resources_register (cc_online_accounts_get_resource ());
 
-  if (panel->removed_object != NULL)
-    on_notification_closed (NULL, panel);
+  gtk_widget_init_template (GTK_WIDGET (self));
 
-  panel->removed_object = panel->active_object;
-  panel->active_object = NULL;
+  gtk_list_box_set_sort_func (self->accounts_listbox,
+                              sort_accounts_func,
+                              self,
+                              NULL);
 
-  account = goa_object_peek_account (panel->removed_object);
-  id = g_strdup_printf ("<b>%s</b>", goa_account_get_presentation_identity (account));
-  /* Translators: The %s is the username (eg., debarshi ray gmail com
-   * or rishi).
-   */
-  label = g_strdup_printf (_("%s removed"), id);
-  gtk_label_set_markup (panel->notification_label, label);
-  gtk_revealer_set_reveal_child (panel->notification_revealer, TRUE);
+  gtk_list_box_set_sort_func (self->providers_listbox,
+                              sort_providers_func,
+                              self,
+                              NULL);
 
-  modify_row_for_account (panel, panel->removed_object, hide_row_for_account);
-  gtk_widget_hide (GTK_WIDGET (panel->edit_account_dialog));
+  monitor = g_network_monitor_get_default();
+  g_object_bind_property (monitor,
+                          "network-available",
+                          self->offline_label,
+                          "visible",
+                          G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+
+  g_object_bind_property (monitor,
+                          "network-available",
+                          self->providers_listbox,
+                          "sensitive",
+                          G_BINDING_SYNC_CREATE);
+
+  /* TODO: probably want to avoid _sync() ... */
+  self->client = goa_client_new_sync (cc_panel_get_cancellable (CC_PANEL (self)), &error);
+  if (self->client == NULL)
+    {
+      g_warning ("Error getting a GoaClient: %s (%s, %d)",
+                 error->message, g_quark_to_string (error->domain), error->code);
+      gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+      return;
+    }
+
+  g_signal_connect (self->client,
+                    "account-added",
+                    G_CALLBACK (on_account_added_cb),
+                    self);
+
+  g_signal_connect (self->client,
+                    "account-changed",
+                    G_CALLBACK (on_account_changed_cb),
+                    self);
+
+  g_signal_connect (self->client,
+                    "account-removed",
+                    G_CALLBACK (on_account_removed_cb),
+                    self);
 
-  panel->remove_account_timeout_id = g_timeout_add_seconds (10, on_remove_account_timeout, panel);
+  fill_accounts_listbox (self);
 }
diff --git a/panels/online-accounts/cc-online-accounts-panel.ui 
b/panels/online-accounts/cc-online-accounts-panel.ui
new file mode 100644
index 000000000..e179954d6
--- /dev/null
+++ b/panels/online-accounts/cc-online-accounts-panel.ui
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="CcGoaPanel" parent="CcPanel">
+    <child>
+      <object class="GtkOverlay">
+        <child type="overlay">
+          <object class="GtkRevealer" id="notification_revealer">
+            <property name="halign">center</property>
+            <property name="valign">start</property>
+            <property name="transition_type">slide-down</property>
+            <child>
+              <object class="GtkFrame">
+                <child>
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="notification_label">
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="undo_button">
+                        <property name="label" translatable="yes">Undo</property>
+                        <signal name="clicked" handler="on_undo_button_clicked_cb" object="CcGoaPanel" 
swapped="no" />
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="close_notification_button">
+                        <property name="icon-name">window-close-symbolic</property>
+                        <signal name="clicked" handler="on_notification_closed_cb" object="CcGoaPanel" 
swapped="no" />
+                        <style>
+                          <class name="flat" />
+                        </style>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <style>
+                  <class name="app-notification" />
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <child>
+          <object class="AdwPreferencesPage">
+
+            <child>
+              <object class="AdwPreferencesGroup">
+                <child>
+                  <object class="GtkLabel" id="accounts_label">
+                    <property name="wrap">True</property>
+                    <property name="justify">center</property>
+                    <property name="label" translatable="yes">Connect to your data in the cloud</property>
+                    <style>
+                      <class name="large-title" />
+                    </style>
+                  </object>
+                </child>
+
+                <child>
+                  <object class="GtkLabel" id="offline_label">
+                    <property name="margin-top">24</property>
+                    <property name="wrap">True</property>
+                    <property name="label" translatable="yes">No internet connection — connect to set up new 
online accounts</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+
+            <child>
+              <object class="AdwPreferencesGroup" id="accounts_frame">
+                <child>
+                  <object class="GtkListBox" id="accounts_listbox">
+                    <property name="hexpand">True</property>
+                    <property name="selection_mode">none</property>
+                    <signal name="row-activated" handler="on_accounts_listbox_row_activated" 
object="CcGoaPanel" swapped="yes" />
+                    <style>
+                      <class name="boxed-list" />
+                    </style>
+                    <accessibility>
+                      <relation name="labelled-by">accounts_label</relation>
+                    </accessibility>
+                  </object>
+                </child>
+              </object>
+            </child>
+
+            <child>
+              <object class="AdwPreferencesGroup">
+                <property name="margin-top">20</property>
+                <property name="title" translatable="yes">Add an account</property>
+                <child>
+                  <object class="GtkListBox" id="providers_listbox">
+                    <property name="selection_mode">none</property>
+                    <signal name="row-activated" handler="on_provider_row_activated_cb" object="CcGoaPanel" 
swapped="yes" />
+                    <style>
+                      <class name="boxed-list" />
+                    </style>
+                  </object>
+                </child>
+              </object>
+            </child>
+
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+
+</interface>
diff --git a/panels/online-accounts/gnome-control-center-goa-helper.c 
b/panels/online-accounts/gnome-control-center-goa-helper.c
new file mode 100644
index 000000000..918cbc19d
--- /dev/null
+++ b/panels/online-accounts/gnome-control-center-goa-helper.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2022 Endless OS Foundation, LLC
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author:
+ *   Georges Basile Stavracas Neto <georges stavracas gmail com>
+ */
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE
+#include <goabackend/goabackend.h>
+
+#ifdef HAVE_GTK_X11
+#include <gdk/gdkx.h>
+#endif
+#ifdef HAVE_GTK_WAYLAND
+#include <gdk/gdkwayland.h>
+#endif
+
+static GdkDisplay *
+get_wayland_display (void)
+{
+  static GdkDisplay *wayland_display = NULL;
+
+  if (wayland_display)
+    return wayland_display;
+
+  gdk_set_allowed_backends ("wayland");
+  wayland_display = gdk_display_open (NULL);
+  gdk_set_allowed_backends (NULL);
+  if (!wayland_display)
+    g_warning ("Failed to open Wayland display");
+
+  return wayland_display;
+}
+
+static GdkDisplay *
+get_x11_display (void)
+{
+  static GdkDisplay *x11_display = NULL;
+
+  if (x11_display)
+    return x11_display;
+
+  gdk_set_allowed_backends ("x11");
+  x11_display = gdk_display_open (NULL);
+  gdk_set_allowed_backends (NULL);
+  if (!x11_display)
+    g_warning ("Failed to open X11 display");
+
+  return x11_display;
+}
+
+static void
+set_external_parent_from_handle (GtkApplication *application,
+                                 GtkWindow      *dialog,
+                                 const char     *handle_str)
+{
+  GdkDisplay *display;
+  GtkWindow *fake_parent;
+  GdkScreen *screen;
+
+#ifdef HAVE_GTK_X11
+    {
+      const char *x11_prefix = "x11:";
+      if (g_str_has_prefix (handle_str, x11_prefix))
+        {
+          display = get_x11_display ();
+          if (!display)
+            {
+              g_warning ("No X display connection, ignoring X11 parent");
+              return;
+            }
+        }
+    }
+#endif
+#ifdef HAVE_GTK_WAYLAND
+    {
+      const char *wayland_prefix = "wayland:";
+
+      if (g_str_has_prefix (handle_str, wayland_prefix))
+        {
+          display = get_wayland_display ();
+          if (!display)
+            {
+              g_warning ("No Wayland display connection, ignoring Wayland parent");
+              return;
+            }
+        }
+    }
+#endif
+
+  screen = gdk_display_get_default_screen (gdk_display_get_default ());
+  fake_parent = g_object_new (GTK_TYPE_APPLICATION_WINDOW,
+                              "application", application,
+                              "type", GTK_WINDOW_TOPLEVEL,
+                              "screen", screen,
+                              NULL);
+  g_object_ref_sink (fake_parent);
+
+  gtk_window_set_transient_for (dialog, GTK_WINDOW (fake_parent));
+  gtk_window_set_modal (dialog, TRUE);
+  gtk_widget_realize (GTK_WIDGET (dialog));
+
+#ifdef HAVE_GTK_X11
+    {
+      const char *x11_prefix = "x11:";
+      if (g_str_has_prefix (handle_str, x11_prefix))
+        {
+          GdkWindow *foreign_gdk_window;
+          int xid;
+
+          errno = 0;
+          xid = strtol (handle_str, NULL, 16);
+          if (errno != 0)
+            {
+              g_warning ("Failed to reference external X11 window, invalid XID %s", handle_str);
+              return;
+            }
+
+          foreign_gdk_window = gdk_x11_window_foreign_new_for_display (display, xid);
+          if (!foreign_gdk_window)
+            {
+              g_warning ("Failed to create foreign window for XID %d", xid);
+              return;
+            }
+
+          gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (dialog)),
+                                        foreign_gdk_window);
+        }
+    }
+#endif
+#ifdef HAVE_GTK_WAYLAND
+    {
+      const char *wayland_prefix = "wayland:";
+
+      if (g_str_has_prefix (handle_str, wayland_prefix))
+        {
+          const char *wayland_handle_str = handle_str + strlen (wayland_prefix);
+
+          if (!gdk_wayland_window_set_transient_for_exported (gtk_widget_get_window (GTK_WIDGET (dialog)),
+                                                              (char *) wayland_handle_str))
+            {
+              g_warning ("Failed to set window transient for external parent");
+              return;
+            }
+        }
+    }
+#endif
+
+  gtk_window_present (dialog);
+}
+
+/* create-account */
+
+static void
+on_application_activate_create_account_cb (GtkApplication  *application,
+                                           char           **argv)
+{
+  g_autoptr (GoaProvider) provider = NULL;
+  g_autoptr (GoaClient) client = NULL;
+  g_autoptr (GError) error = NULL;
+  GoaAccount *account;
+  GtkWidget *content_area;
+  GtkWidget *dialog;
+  GoaObject *object;
+
+  client = goa_client_new_sync (NULL, &error);
+  if (error)
+    {
+      g_printerr ("Error retrieving online accounts client");
+      exit (EXIT_FAILURE);
+      return;
+    }
+
+
+  /* Find the provider with a matching type */
+  provider = goa_provider_get_for_provider_type (argv[2]);
+  if (!provider)
+    {
+      g_printerr ("Provider type not supported");
+      exit (EXIT_FAILURE);
+      return;
+    }
+
+  dialog = g_object_new (GTK_TYPE_DIALOG,
+                         "use-header-bar", 1,
+                         NULL);
+  g_signal_connect_swapped (dialog, "response", G_CALLBACK (g_application_quit), application);
+  set_external_parent_from_handle (application, GTK_WINDOW (dialog), argv[3]);
+
+  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+  gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+
+  object = goa_provider_add_account (provider,
+                                     client,
+                                     GTK_DIALOG (dialog),
+                                     GTK_BOX (content_area),
+                                     &error);
+  if (error)
+    {
+      g_printerr ("Failed to create account: %s", error->message);
+      exit (EXIT_FAILURE);
+      return;
+    }
+
+  account = goa_object_peek_account (object);
+  g_print ("%s", goa_account_get_id (account));
+}
+
+static int
+create_account (int    argc,
+                char **argv)
+{
+  g_autoptr (GtkApplication) application = NULL;
+
+  gtk_init (&argc, &argv);
+
+  if (argc != 4)
+    {
+      g_printerr ("Not enough arguments");
+      return EXIT_FAILURE;
+    }
+
+  application = gtk_application_new ("org.gnome.ControlCenter.GoaHelper",
+                                     G_APPLICATION_FLAGS_NONE);
+  g_signal_connect (application, "activate", G_CALLBACK (on_application_activate_create_account_cb), argv);
+
+  return g_application_run (G_APPLICATION (application), 0, NULL);
+}
+
+/* list-providers */
+
+typedef struct {
+  GMainLoop *mainloop;
+  GList *providers;
+  GError *error;
+} GetAllProvidersData;
+
+static void
+get_all_providers_cb (GObject      *source,
+                      GAsyncResult *res,
+                      gpointer      user_data)
+{
+  g_autolist(GoaProvider) providers = NULL;
+  GetAllProvidersData *data;
+
+  data = user_data;
+
+  goa_provider_get_all_finish (&providers, res, &data->error);
+  if (data->error)
+    goto out;
+
+  data->providers = g_steal_pointer (&providers);
+
+out:
+  g_main_loop_quit (data->mainloop);
+}
+
+static GList *
+get_all_providers (GError **error)
+{
+  GetAllProvidersData data = (GetAllProvidersData) {
+    .mainloop = g_main_loop_new (NULL, FALSE),
+    .providers = NULL,
+    .error = NULL,
+  };
+
+  goa_provider_get_all (get_all_providers_cb, &data);
+
+  g_main_loop_run (data.mainloop);
+
+  if (data.error)
+    g_propagate_error (error, data.error);
+
+  return data.providers;
+}
+
+static int
+list_providers (int    argc,
+                char **argv)
+{
+  g_autolist (GoaProvider) providers = NULL;
+  g_autofree char *serialized_result = NULL;
+  g_autoptr (GVariant) result = NULL;
+  g_autoptr (GError) error = NULL;
+  GVariantBuilder b;
+  GList *l;
+
+  providers = get_all_providers (&error);
+
+  if (error)
+    {
+      g_printerr ("%s", error->message);
+      return EXIT_FAILURE;
+    }
+
+  g_variant_builder_init (&b, G_VARIANT_TYPE ("a(ssviu)"));
+  for (l = providers; l; l = l->next)
+    {
+      GoaProvider *provider = l->data;
+      g_autofree char *name = NULL;
+      g_autoptr(GVariant) icon_variant = NULL;
+      g_autoptr(GIcon) icon = NULL;
+
+      name = goa_provider_get_provider_name (provider, NULL);
+      icon = goa_provider_get_provider_icon (provider, NULL);
+      icon_variant = g_icon_serialize (icon);
+
+      g_variant_builder_add (&b, "(ssviu)",
+                             goa_provider_get_provider_type (provider),
+                             name,
+                             icon_variant,
+                             goa_provider_get_provider_features (provider),
+                             goa_provider_get_credentials_generation (provider));
+    }
+  result = g_variant_builder_end (&b);
+
+  serialized_result = g_variant_print (result, TRUE);
+  g_print ("%s", serialized_result);
+
+  return EXIT_SUCCESS;
+}
+
+/* show-account */
+
+static void
+on_remove_button_clicked_cb (GtkButton    *button,
+                             GApplication *application)
+{
+  g_print ("remove");
+  g_application_quit (application);
+}
+
+static void
+on_application_activate_show_account_cb (GtkApplication  *application,
+                                         char           **argv)
+{
+  g_autoptr (GoaProvider) provider = NULL;
+  g_autoptr (GoaObject) object = NULL;
+  g_autoptr (GoaClient) client = NULL;
+  g_autoptr (GError) error = NULL;
+  g_autofree char *title = NULL;
+  GoaAccount *account;
+  GtkWidget *content_area;
+  GtkWidget *button;
+  GtkWidget *dialog;
+  GtkWidget *box;
+  const char *provider_type;
+
+  client = goa_client_new_sync (NULL, &error);
+  if (error)
+    {
+      g_printerr ("Error retrieving online accounts client");
+      exit (EXIT_FAILURE);
+      return;
+    }
+
+  object = goa_client_lookup_by_id (client, argv[2]);
+  if (!object)
+    {
+      g_printerr ("Online account does not exist");
+      exit (EXIT_FAILURE);
+      return;
+    }
+
+  /* Find the provider with a matching type */
+  account = goa_object_peek_account (object);
+  provider_type = goa_account_get_provider_type (account);
+  provider = goa_provider_get_for_provider_type (provider_type);
+  if (!provider)
+    {
+      g_printerr ("Provider type not supported");
+      exit (EXIT_FAILURE);
+      return;
+    }
+
+  dialog = g_object_new (GTK_TYPE_DIALOG,
+                         "use-header-bar", 1,
+                         NULL);
+  g_signal_connect_swapped (dialog, "response", G_CALLBACK (g_application_quit), application);
+  set_external_parent_from_handle (application, GTK_WINDOW (dialog), argv[3]);
+
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 42);
+  gtk_widget_set_margin_bottom (box, 24);
+
+  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+  gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+  gtk_container_add (GTK_CONTAINER (content_area), box);
+
+  goa_provider_show_account (provider,
+                             client,
+                             object,
+                             GTK_BOX (box),
+                             NULL,
+                             NULL);
+
+  /*
+   * The above call doesn't set any widgets to visible, so we have to do that.
+   * https://gitlab.gnome.org/GNOME/gnome-online-accounts/issues/56
+   */
+  gtk_widget_show_all (box);
+
+  /* translators: This is the title of the "Show Account" dialog. The
+   * %s is the name of the provider. e.g., 'Google'. */
+  title = g_strdup_printf (_("%s Account"), goa_account_get_provider_name (account));
+  gtk_window_set_title (GTK_WINDOW (dialog), title);
+
+  button = gtk_button_new_with_label (_("Remove Account"));
+  gtk_widget_set_margin_start (box, 24);
+  gtk_widget_set_margin_end (box, 24);
+  gtk_widget_set_halign (button, GTK_ALIGN_END);
+  gtk_widget_set_valign (button, GTK_ALIGN_END);
+  gtk_widget_set_visible (button, !goa_account_get_is_locked (account));
+  gtk_style_context_add_class (gtk_widget_get_style_context (button), "destructive-action");
+  gtk_container_add (GTK_CONTAINER (box), button);
+  g_signal_connect (button, "clicked", G_CALLBACK (on_remove_button_clicked_cb), application);
+}
+
+static int
+show_account (int    argc,
+              char **argv)
+{
+  g_autoptr (GtkApplication) application = NULL;
+
+  gtk_init (&argc, &argv);
+
+  if (argc != 4)
+    {
+      g_printerr ("Not enough arguments");
+      return EXIT_FAILURE;
+    }
+
+  application = gtk_application_new ("org.gnome.ControlCenter.GoaHelper",
+                                     G_APPLICATION_FLAGS_NONE);
+  g_signal_connect (application, "activate", G_CALLBACK (on_application_activate_show_account_cb), argv);
+
+  return g_application_run (G_APPLICATION (application), 0, NULL);
+}
+
+struct {
+  const char *command_name;
+  int (*command_func) (int    argc,
+                       char **argv);
+} commands[] = {
+  { "create-account", create_account, },
+  { "list-providers", list_providers, },
+  { "show-account", show_account, },
+};
+
+int
+main (int    argc,
+      char **argv)
+{
+  gsize i;
+
+  if (argc < 2)
+    return EXIT_FAILURE;
+
+  for (i = 0; i < G_N_ELEMENTS (commands); i++)
+    {
+      if (g_strcmp0 (commands[i].command_name, argv[1]) == 0)
+        return commands[i].command_func (argc, argv);
+    }
+
+  return EXIT_SUCCESS;
+}
diff --git a/panels/online-accounts/meson.build b/panels/online-accounts/meson.build
index 687097724..407f24ca1 100644
--- a/panels/online-accounts/meson.build
+++ b/panels/online-accounts/meson.build
@@ -16,9 +16,13 @@ i18n.merge_file(
   install_dir: control_center_desktopdir
 )
 
+cflags += [
+  '-DLIBEXECDIR="@0@"'.format(control_center_libexecdir),
+]
+
 sources = files('cc-online-accounts-panel.c')
 
-resource_data = files('online-accounts.ui')
+resource_data = files('cc-online-accounts-panel.ui')
 
 sources += gnome.compile_resources(
   'cc-' + cappletname + '-resources',
@@ -30,7 +34,6 @@ sources += gnome.compile_resources(
 
 deps = common_deps + [
   goa_dep,
-  dependency('goa-backend-1.0', version: goa_req_version)
 ]
 
 panels_libs += static_library(
@@ -41,4 +44,30 @@ panels_libs += static_library(
   c_args: cflags
 )
 
+goa_helper_deps = [
+  dependency('goa-backend-1.0', version: goa_req_version),
+]
+
+goa_helper_cflags = cflags
+
+gtk_x11_dep = dependency('gtk+-x11-3.0', required: false)
+if gtk_x11_dep.found()
+  goa_helper_cflags += ['-DHAVE_GTK_X11']
+endif
+
+gtk_wayland_dep = dependency('gtk+-wayland-3.0', required: false)
+if gtk_wayland_dep.found()
+  goa_helper_cflags += ['-DHAVE_GTK_WAYLAND']
+endif
+
+executable(
+  'gnome-control-center-goa-helper',
+  'gnome-control-center-goa-helper.c',
+  include_directories: [ top_inc ],
+  dependencies: goa_helper_deps,
+  c_args: goa_helper_cflags,
+  install: true,
+  install_dir: control_center_libexecdir,
+)
+
 subdir('icons')
diff --git a/panels/online-accounts/online-accounts.gresource.xml 
b/panels/online-accounts/online-accounts.gresource.xml
index b428394d2..a82edbec7 100644
--- a/panels/online-accounts/online-accounts.gresource.xml
+++ b/panels/online-accounts/online-accounts.gresource.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/gnome/control-center/online-accounts">
-    <file preprocess="xml-stripblanks">online-accounts.ui</file>
+    <file preprocess="xml-stripblanks">cc-online-accounts-panel.ui</file>
   </gresource>
 </gresources>
diff --git a/shell/cc-panel-loader.c b/shell/cc-panel-loader.c
index 403a44267..ee1c1fd72 100644
--- a/shell/cc-panel-loader.c
+++ b/shell/cc-panel-loader.c
@@ -49,7 +49,7 @@ extern GType cc_network_panel_get_type (void);
 extern GType cc_wifi_panel_get_type (void);
 #endif /* BUILD_NETWORK */
 extern GType cc_notifications_panel_get_type (void);
-//extern GType cc_goa_panel_get_type (void);
+extern GType cc_goa_panel_get_type (void);
 extern GType cc_power_panel_get_type (void);
 extern GType cc_printers_panel_get_type (void);
 extern GType cc_region_panel_get_type (void);
@@ -120,7 +120,7 @@ static CcPanelLoaderVtable default_panels[] =
   PANEL_TYPE("wifi",             cc_wifi_panel_get_type,                 cc_wifi_panel_static_init_func),
 #endif
   PANEL_TYPE("notifications",    cc_notifications_panel_get_type,        NULL),
-  //PANEL_TYPE("online-accounts",  cc_goa_panel_get_type,                  NULL),
+  PANEL_TYPE("online-accounts",  cc_goa_panel_get_type,                  NULL),
   PANEL_TYPE("power",            cc_power_panel_get_type,                NULL),
   PANEL_TYPE("printers",         cc_printers_panel_get_type,             NULL),
   PANEL_TYPE("region",           cc_region_panel_get_type,               NULL),


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