gnome: account creation wizard

Show wizard when no Ring account is present

Refs #69438

Change-Id: I8f3438498b033e6975aac395e3d971428c276aef
diff --git a/src/ringmainwindow.cpp b/src/ringmainwindow.cpp
index 66c4208..6560b4f 100644
--- a/src/ringmainwindow.cpp
+++ b/src/ringmainwindow.cpp
@@ -48,7 +48,10 @@
 #include "dialogs.h"
 #include "videosettingsview.h"
 #include <video/previewmanager.h>
+
 #define CALL_VIEW_NAME "calls"
+#define CREATE_ACCOUNT_1_VIEW_NAME "create1"
+#define CREATE_ACCOUNT_2_VIEW_NAME "create2"
 #define GENERAL_SETTINGS_VIEW_NAME "general"
 #define AUDIO_SETTINGS_VIEW_NAME "audio"
 #define VIDEO_SETTINGS_VIEW_NAME "video"
@@ -97,6 +100,20 @@
     GtkWidget *radiobutton_account_settings;
 
     gboolean   show_settings;
+
+    /* account creation */
+    GtkWidget *account_creation_1;
+    GtkWidget *image_ring_logo;
+    GtkWidget *label_enter_alias;
+    GtkWidget *entry_alias;
+    GtkWidget *label_generating_account;
+    GtkWidget *spinner_generating_account;
+    GtkWidget *button_account_creation_next;
+    GtkWidget *account_creation_2;
+    GtkWidget *entry_hash;
+    GtkWidget *button_account_creation_done;
+
+    QMetaObject::Connection hash_updated;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
@@ -457,6 +474,160 @@
     }
 }
 
+static gboolean
+has_ring_account()
+{
+    /* check if a Ring account exists */
+    int a_count = AccountModel::instance()->rowCount();
+    for (int i = 0; i < a_count; ++i) {
+        QModelIndex idx = AccountModel::instance()->index(i, 0);
+        QVariant protocol = idx.data(static_cast<int>(Account::Role::Proto));
+        if ((Account::Protocol)protocol.toUInt() == Account::Protocol::RING)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+create_ring_account(RingMainWindow *win)
+{
+    g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
+    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
+
+    /* create account and set UPnP enabled, as its not by default in the daemon */
+    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
+    Account *account = AccountModel::instance()->add(alias, Account::Protocol::RING);
+    account->setUpnpEnabled(TRUE);
+
+    /* wait for hash to be generated to show the next view */
+    priv->hash_updated = QObject::connect(
+        account,
+        &Account::changed,
+        [=] (Account *a) {
+            QString hash = a->username();
+            if (!hash.isEmpty()) {
+                /* set the hash */
+                gtk_entry_set_text(GTK_ENTRY(priv->entry_hash), hash.toUtf8().constData());
+
+                /* show the next accont creation view */
+                gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
+                gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_2_VIEW_NAME);
+
+                /* select the hash text */
+                gtk_widget_grab_focus(priv->entry_hash);
+                gtk_editable_select_region(GTK_EDITABLE(priv->entry_hash), 0, -1);
+            }
+        }
+    );
+
+    account->performAction(Account::EditAction::SAVE);
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
+{
+    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
+
+    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (strlen(alias) > 0) {
+        /* enable "next" button */
+        gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
+    } else {
+        /* disable "next" button, as we require an alias */
+        gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
+    }
+}
+
+static void
+account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
+{
+    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
+
+    /* show/hide relevant widgets */
+    gtk_widget_hide(priv->label_enter_alias);
+    gtk_widget_hide(priv->entry_alias);
+    gtk_widget_hide(priv->button_account_creation_next);
+    gtk_widget_show(priv->label_generating_account);
+    gtk_widget_show(priv->spinner_generating_account);
+
+    /* now create account after a short timeout so that the the save doesn't
+     * happen freeze the client before the widget changes happen;
+     * the timeout function should then display the next step in account creation
+     */
+    g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_ring_account, win, NULL);
+}
+
+static void
+account_creation_done_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
+{
+    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
+
+    QObject::disconnect(priv->hash_updated);
+
+    /* show the call view */
+    gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
+    gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
+
+    /* show the search bar and settings */
+    gtk_widget_show(priv->hbox_search);
+    gtk_widget_show(priv->ring_settings);
+
+}
+
+static void
+entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
+{
+    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
+
+    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (strlen(alias) > 0)
+        gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
+}
+
+static void
+show_account_creation(RingMainWindow *win)
+{
+    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
+
+    gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
+                        priv->account_creation_1,
+                        CREATE_ACCOUNT_1_VIEW_NAME);
+
+    gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
+                        priv->account_creation_2,
+                        CREATE_ACCOUNT_2_VIEW_NAME);
+
+    /* hide search bar and settings until account creation is complete */
+    gtk_widget_hide(priv->hbox_search);
+    gtk_widget_hide(priv->ring_settings);
+
+    /* set ring logo */
+    GError *error = NULL;
+    GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
+                                                                  -1, 75, TRUE, &error);
+    if (logo_ring == NULL) {
+        g_debug("Could not load logo: %s", error->message);
+        g_error_free(error);
+    } else
+        gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
+
+    /* style of alias and hash entry; give them a larger font */
+    gtk_widget_override_font(priv->entry_alias, pango_font_description_from_string("15"));
+    gtk_widget_override_font(priv->entry_hash, pango_font_description_from_string("monospace 15"));
+
+    /* connect signals */
+    g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
+    g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
+    g_signal_connect(priv->button_account_creation_done, "clicked", G_CALLBACK(account_creation_done_clicked), win);
+    g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
+    g_signal_connect_swapped(priv->entry_hash, "activate", G_CALLBACK(gtk_button_clicked), priv->button_account_creation_done);
+
+    gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_1_VIEW_NAME);
+}
+
 static void
 ring_main_window_init(RingMainWindow *win)
 {
@@ -498,7 +669,13 @@
                         priv->vbox_call_view,
                         CALL_VIEW_NAME);
 
-    gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
+    if (has_ring_account()) {
+        /* user has ring account, so show the call view right away */
+        gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
+    } else {
+        /* user has to create the ring account */
+        show_account_creation(win);
+    }
 
     /* init the settings views */
     priv->account_settings_view = account_view_new();
@@ -719,6 +896,19 @@
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_video_settings);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
+
+    /* account creation */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_1);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_enter_alias);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
+
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_2);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_hash);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_done);
 }
 
 GtkWidget *