Project 'savoirfairelinux/ring-client-gnome' was moved to 'savoirfairelinux/jami-client-gnome'. Please update any links and bookmarks that may still have the old path.
Select Git revision
accountcreationwizard.cpp
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
accountcreationwizard.cpp 40.79 KiB
/*
* Copyright (C) 2016-2019 Savoir-faire Linux Inc.
* Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
* Author: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com>
*
* 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// GTK+ related
#include <glib/gi18n.h>
#include <gtk/gtk.h>
// LRC
#include "api/newaccountmodel.h"
// Jami Client
#include "utils/models.h"
#include "avatarmanipulation.h"
#include "accountcreationwizard.h"
#include "usernameregistrationbox.h"
#include "utils/files.h"
struct _AccountCreationWizard
{
GtkScrolledWindow parent;
};
struct _AccountCreationWizardClass
{
GtkScrolledWindowClass parent_class;
};
typedef struct _AccountCreationWizardPrivate AccountCreationWizardPrivate;
enum class Mode {
ADD_LOCAL,
IMPORT_FROM_DEVICE,
IMPORT_FROM_BACKUP,
CONNECT_TO_MANAGER
};
struct _AccountCreationWizardPrivate
{
AccountInfoPointer const *accountInfo_ = nullptr;
gchar* accountId;
gchar* username;
gchar* password;
gchar* avatar;
GtkWidget *stack_account_creation;
gboolean username_available;
/* choose_account_type_vbox */
GtkWidget *choose_account_type_vbox;
GtkWidget *choose_account_type_ring_logo;
GtkWidget *button_new_account;
GtkWidget *button_import_from_device;
GtkWidget *button_import_from_backup;
GtkWidget *button_show_advanced;
GtkWidget *button_connect_account_manager;
GtkWidget *button_new_sip_account;
/* existing account */
GtkWidget *existing_account;
GtkWidget *existing_account_label;
GtkWidget *row_archive;
GtkWidget *row_pin;
GtkWidget *button_pin_info;
GtkWidget *row_pin_label;
GtkWidget *button_import_from_next;
GtkWidget *button_import_from_previous;
GtkWidget *entry_existing_account_pin;
GtkWidget *entry_existing_account_archive;
GtkWidget *entry_existing_account_password;
GtkWidget *retrieving_account;
/* account creation */
GtkWidget *account_creation;
GtkWidget *entry_username;
GtkWidget *entry_password;
GtkWidget *entry_password_confirm;
GtkWidget *label_password_error;
GtkWidget *button_account_creation_next;
GtkWidget *button_account_creation_previous;
GtkWidget *box_avatarselection;
GtkWidget *avatar_manipulation;
GtkWidget *box_username_entry;
GtkWidget *switch_register;
GtkWidget *username_registration_box;
GtkWidget *entry_display_name;
/* generating_account_spinner */
GtkWidget *vbox_generating_account_spinner;
/* registering_username_spinner */
GtkWidget *vbox_registering_username_spinner;
/* backup */
GtkWidget *info_backup;
GtkWidget *button_never_show_again;
GtkWidget *button_export_account;
GtkWidget *button_skip_backup;
GtkWidget *button_backup_info;
GtkWidget *row_backup_label;
/* error_view */
GtkWidget *error_view;
GtkWidget *label_error_view;
GtkWidget *button_error_view_ok;
/* connect to account_manager */
GtkWidget *connect_to_account_manager;
GtkWidget *entry_account_manager_username;
GtkWidget *entry_account_manager_password;
GtkWidget *entry_account_manager_uri;
GtkWidget *button_account_manager_connect_previous;
GtkWidget *button_account_manager_connect_next;
lrc::api::AVModel* avModel_;
lrc::api::NewAccountModel* accountModel_;
Mode mode;
GSettings *settings;
};
G_DEFINE_TYPE_WITH_PRIVATE(AccountCreationWizard, account_creation_wizard, GTK_TYPE_SCROLLED_WINDOW);
#define ACCOUNT_CREATION_WIZARD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizardPrivate))
/* signals */
enum {
ACCOUNT_CREATION_LOCK,
ACCOUNT_CREATION_UNLOCK,
ACCOUNT_CREATION_COMPLETED,
LAST_SIGNAL
};
static guint account_creation_wizard_signals[LAST_SIGNAL] = { 0 };
static void
account_creation_wizard_dispose(GObject *object)
{
// make sure preview is stopped and destroyed
account_creation_wizard_show_preview(ACCOUNT_CREATION_WIZARD(object), FALSE);
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(object);
g_clear_object(&priv->settings);
G_OBJECT_CLASS(account_creation_wizard_parent_class)->dispose(object);
}
static void
account_creation_wizard_init(AccountCreationWizard *view)
{
gtk_widget_init_template(GTK_WIDGET(view));
auto *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
priv->settings = g_settings_new_full(get_settings_schema(), NULL, NULL);
g_settings_bind(priv->settings, "never-show-backup-again",
priv->button_never_show_again, "active",
G_SETTINGS_BIND_DEFAULT);
}
static void
account_creation_wizard_class_init(AccountCreationWizardClass *klass)
{
G_OBJECT_CLASS(klass)->dispose = account_creation_wizard_dispose;
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
"/net/jami/JamiGnome/accountcreationwizard.ui");
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, stack_account_creation);
/* choose_account_type_vbox */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, choose_account_type_vbox);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, choose_account_type_ring_logo);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_new_account);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_import_from_device);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_import_from_backup);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_show_advanced);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_connect_account_manager);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_new_sip_account);
/* existing account */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, existing_account);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, existing_account_label);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, row_archive);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, row_pin);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_pin_info);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, row_pin_label);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_import_from_next);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_import_from_previous);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_pin);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_archive);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_password);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, retrieving_account);
/* account creation */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, account_creation);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_password);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_password_confirm);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_creation_next);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_creation_previous);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, box_avatarselection);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_password_error);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, box_username_entry);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, switch_register);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_display_name);
/* generating_account_spinner */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, vbox_generating_account_spinner);
/* registering_username_spinner */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, vbox_registering_username_spinner);
/* info backup */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, info_backup);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_never_show_again);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_export_account);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, row_backup_label);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_backup_info);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_skip_backup);
/* error view */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, error_view);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_error_view);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_error_view_ok);
/* connect to account_manager */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, connect_to_account_manager);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_account_manager_username);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_account_manager_password);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_account_manager_uri);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_manager_connect_previous);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_manager_connect_next);
/* add signals */
account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED] = g_signal_new("account-creation-completed",
G_TYPE_FROM_CLASS(klass),
(GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
0,
nullptr,
nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
account_creation_wizard_signals[ACCOUNT_CREATION_LOCK] = g_signal_new("account-creation-lock",
G_TYPE_FROM_CLASS(klass),
(GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
0,
nullptr,
nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
account_creation_wizard_signals[ACCOUNT_CREATION_UNLOCK] = g_signal_new("account-creation-unlock",
G_TYPE_FROM_CLASS(klass),
(GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
0,
nullptr,
nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
void
account_creation_show_error_view(AccountCreationWizard *view, const std::string& id)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
if (priv->accountId && id == priv->accountId) {
switch (priv->mode) {
case Mode::ADD_LOCAL:
gtk_label_set_text(GTK_LABEL(priv->label_error_view),
_("An error occured during the account creation."));
break;
case Mode::IMPORT_FROM_DEVICE:
gtk_label_set_text(GTK_LABEL(priv->label_error_view),
_("Cannot retrieve any account, please verify your PIN and your password."));
break;
case Mode::IMPORT_FROM_BACKUP:
gtk_label_set_text(GTK_LABEL(priv->label_error_view),
_("Cannot retrieve any account, please verify your archive and your password."));
break;
case Mode::CONNECT_TO_MANAGER:
gtk_label_set_text(GTK_LABEL(priv->label_error_view),
_("Cannot connect to provided account manager. Please check your credentials."));
break;
}
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->error_view);
}
}
static gboolean
connect_to_manager(AccountCreationWizard *view, gchar* username, gchar* password, gchar* managerUri)
{
g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(view), G_SOURCE_REMOVE);
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_LOCK], 0);
std::string accountId = lrc::api::NewAccountModel::connectToAccountManager(
username? username : "",
password? password : "",
managerUri? managerUri : "");
priv->accountId = g_strdup(accountId.c_str());
// NOTE: NewAccountModel::accountAdded will be triggered here and will call account_creation_wizard_account_added
g_object_ref(view); // ref so its not destroyed too early
return G_SOURCE_REMOVE;
}
static gboolean
create_account(AccountCreationWizard *view,
gchar *display_name,
gchar *username,
gchar *password,
gchar *pin,
gchar *archivePath)
{
g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(view), G_SOURCE_REMOVE);
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_LOCK], 0);
priv->username = g_strdup(username);
priv->password = g_strdup(password);
std::string accountId = lrc::api::NewAccountModel::createNewAccount(
lrc::api::profile::Type::RING,
display_name? display_name : "",
archivePath? archivePath : "",
password? password : "",
pin? pin : "");
priv->accountId = g_strdup(accountId.c_str());
priv->avatar = g_strdup(avatar_manipulation_get_temporary(AVATAR_MANIPULATION(priv->avatar_manipulation)));
// NOTE: NewAccountModel::accountAdded will be triggered here and will call account_creation_wizard_account_added
g_object_ref(view); // ref so its not destroyed too early
return G_SOURCE_REMOVE;
}
void
account_creation_wizard_account_added(AccountCreationWizard *view, const std::string& id)
{
g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(view));
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
if (id != priv->accountId) {
// Not for this account. Abort
return;
}
// Register username
if (priv->username) {
priv->accountModel_->registerName(priv->accountId, priv->password, priv->username);
}
// Set avatar if any.
if (priv->avatar) {
try {
priv->accountModel_->setAvatar(priv->accountId, priv->avatar);
} catch (std::out_of_range&) {
g_warning("Can't set avatar for unknown account");
}
}
if (!g_settings_get_boolean(priv->settings, "never-show-backup-again") && priv->mode == Mode::ADD_LOCAL) {
gtk_button_set_label(GTK_BUTTON(priv->button_export_account), _("Export account"));
gtk_widget_show_all(priv->info_backup);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->info_backup);
} else {
g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
}
g_object_unref(view);
}
static gboolean
create_new_account(AccountCreationWizard *win)
{
g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
/* Tuleap: #1441
* if the user did not validate the avatar area selection, we still take that as the image
* for their avatar; otherwise many users end up with no avatar by default
* TODO: improve avatar creation process to not need this fix
*/
avatar_manipulation_wizard_completed(AVATAR_MANIPULATION(priv->avatar_manipulation));
gchar *display_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_display_name)));
gchar *username = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_username)));
gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_password)));
gtk_entry_set_text(GTK_ENTRY(priv->entry_password), "");
gtk_entry_set_text(GTK_ENTRY(priv->entry_password_confirm), "");
auto status = create_account(win, display_name, username, password, nullptr, nullptr);
// Now that we've use the preview to generate the avatar, we can safely close it. Don't
// assume owner will do it for us, this might not always be the case
account_creation_wizard_show_preview(win, FALSE);
g_free(display_name);
g_free(password);
g_free(username);
return status;
}
static gboolean
connect_to_account_manager(AccountCreationWizard *win)
{
g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
gchar *username = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_username)));
gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_password)));
gchar *managerUri = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_uri)));
auto status = connect_to_manager(win, username, password, managerUri);
g_free(username);
g_free(password);
g_free(managerUri);
return status;
}
static gboolean
create_existing_ring_account(AccountCreationWizard *win)
{
g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_password)));
gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_password), "");
gchar *pin = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_pin)));
if (pin and strlen(pin) > 0) {
gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_pin), "");
}
gchar *archive = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive)));
if (archive) {
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive), nullptr);
}
auto status = create_account(win, NULL, NULL, password, pin, archive);
g_free(archive);
g_free(password);
g_free(pin);
return status;
}
static void
show_generating_account_spinner(AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->vbox_generating_account_spinner);
}
static void
account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *win)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
/* Check for correct password */
const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
if (g_strcmp0(password, password_confirm) != 0)
{
gtk_label_set_text(GTK_LABEL(priv->label_password_error), _("Passwords don't match"));
return;
}
show_generating_account_spinner(win);
/* 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_new_account, win, NULL);
}
static void
connect_account_manager_next_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *win)
{
show_generating_account_spinner(win);
/* 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)connect_to_account_manager, win, NULL);
}
static void
show_retrieving_account(AccountCreationWizard *win)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->retrieving_account);
}
static void
existing_account_next_clicked(AccountCreationWizard *win)
{
show_retrieving_account(win);
/* 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_existing_ring_account, win, NULL);
}
static void
show_choose_account_type(AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
/* Make sure other stack elements are hidden, otherwise their width will prevent
the window to be correctly resized at this stage. */
gtk_widget_hide(priv->account_creation);
gtk_widget_hide(priv->existing_account);
gtk_widget_hide(priv->connect_to_account_manager);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->choose_account_type_vbox);
}
static void
show_pin_label(AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
gtk_widget_set_visible(priv->row_pin_label, !gtk_widget_is_visible(priv->row_pin_label));
}
static void
show_backup_label(AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
gtk_widget_set_visible(priv->row_backup_label, !gtk_widget_is_visible(priv->row_backup_label));
}
static void
error_ok_clicked(AccountCreationWizard *view)
{
g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_UNLOCK], 0);
show_choose_account_type(view);
}
static void
skip_backup_clicked(AccountCreationWizard *view)
{
g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
}
static void
account_creation_previous_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
{
// make sure to stop preview before going back to choose account type
account_creation_wizard_show_preview(view, FALSE);
show_choose_account_type(view);
}
static void
connect_account_manager_previous_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
{
show_choose_account_type(view);
}
static void
show_import_from_device(AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
gtk_label_set_text(GTK_LABEL(priv->existing_account_label), _("Import from device"));
gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_pin), "");
gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_password), "");
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive), nullptr);
gtk_widget_show_all(priv->existing_account);
gtk_widget_hide(priv->row_archive);
gtk_widget_hide(priv->row_pin_label);
gtk_widget_hide(priv->row_backup_label);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->existing_account);
priv->mode = Mode::IMPORT_FROM_DEVICE;
}
static void
show_import_from_backup(AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
gtk_label_set_text(GTK_LABEL(priv->existing_account_label), _("Import from backup"));
gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_pin), "");
gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_password), "");
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive), nullptr);
gtk_widget_show_all(priv->existing_account);
gtk_widget_hide(priv->row_pin);
gtk_widget_hide(priv->row_pin_label);
gtk_widget_hide(priv->row_backup_label);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->existing_account);
priv->mode = Mode::IMPORT_FROM_BACKUP;
}
static void
show_connect_account_manager(AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
gtk_widget_show(priv->connect_to_account_manager);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->connect_to_account_manager);
priv->mode = Mode::CONNECT_TO_MANAGER;
}
static void
show_advanced(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
gtk_widget_set_visible(GTK_WIDGET(priv->button_connect_account_manager), !gtk_widget_is_visible(GTK_WIDGET(priv->button_connect_account_manager)));
gtk_widget_set_visible(GTK_WIDGET(priv->button_new_sip_account), !gtk_widget_is_visible(GTK_WIDGET(priv->button_new_sip_account)));
}
static void
create_new_sip_account(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
{
lrc::api::NewAccountModel::createNewAccount(lrc::api::profile::Type::SIP, "SIP");
g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
g_object_unref(view);
}
static void
entries_existing_account_changed(G_GNUC_UNUSED GtkEntry *entry, AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
const gchar *pin = gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_pin));
gchar *archive_path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->entry_existing_account_archive));
bool hasPin = pin and strlen(pin) > 0;
bool hasArchive = archive_path and strlen(archive_path) > 0;
gtk_widget_set_sensitive(
priv->entry_existing_account_pin,
(not hasArchive)
);
gtk_widget_set_sensitive(
priv->entry_existing_account_archive,
(not hasPin)
);
gtk_widget_set_sensitive(
priv->button_import_from_next,
(hasArchive || hasPin)
);
g_free(archive_path);
}
static void
entries_new_account_changed(AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
const gchar *username = gtk_entry_get_text(GTK_ENTRY(priv->entry_username));
const gboolean sign_up_blockchain = gtk_switch_get_active(GTK_SWITCH(priv->switch_register));
if (
(sign_up_blockchain && strlen(username) > 0 && priv->username_available) || // we are signing up, username is set and avaialble
!sign_up_blockchain // We are not signing up
)
{
gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
}
else
{
gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
}
}
static void
entries_connect_account_manager_changed(AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
const std::string username = gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_username));
const std::string managerUri = gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_uri));
const std::string password = gtk_entry_get_text(GTK_ENTRY(priv->entry_account_manager_password));
gtk_widget_set_sensitive(priv->button_account_manager_connect_next, (!username.empty() && !managerUri.empty() && !password.empty()));
}
static void
sign_up_blockchain_switched(GtkSwitch* switch_btn, GParamSpec*, AccountCreationWizard *view)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
gboolean sign_up_blockchain = gtk_switch_get_active(switch_btn);
username_registration_box_set_use_blockchain(
USERNAME_REGISTRATION_BOX(priv->username_registration_box),
sign_up_blockchain
);
gtk_widget_set_sensitive(priv->entry_username, sign_up_blockchain);
if (!sign_up_blockchain)
{
gtk_entry_set_text(GTK_ENTRY(priv->entry_username), "");
}
/* Unchecking blockchain signup when there is an empty username should
* result in activating the next button.
*/
entries_new_account_changed(view);
}
static void
username_availability_changed(AccountCreationWizard *view, gboolean username_available)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
priv->username_available = username_available;
entries_new_account_changed(view);
}
// Export file
static void
choose_export_file(AccountCreationWizard *view)
{
g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(view));
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
// Get preferred path
GtkWidget* dialog;
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
gint res;
gchar* filename = nullptr;
dialog = gtk_file_chooser_dialog_new(_("Save File"),
GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))),
action,
_("_Cancel"),
GTK_RESPONSE_CANCEL,
_("_Save"),
GTK_RESPONSE_ACCEPT,
nullptr);
const auto& accountInfo = priv->accountModel_->getAccountInfo(priv->accountId);
std::string alias = accountInfo.profileInfo.alias;
std::string uri = alias.empty()? "export.gz" : alias + ".gz";
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), uri.c_str());
res = gtk_dialog_run(GTK_DIALOG(dialog));
if (res == GTK_RESPONSE_ACCEPT) {
auto chooser = GTK_FILE_CHOOSER(dialog);
filename = gtk_file_chooser_get_filename(chooser);
}
gtk_widget_destroy(dialog);
if (!filename) return;
std::string password = {};
auto prop = priv->accountModel_->getAccountConfig(priv->accountId);
if (prop.archiveHasPassword) {
auto* top_window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
auto* password_dialog = gtk_message_dialog_new(top_window,
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
_("You need to enter your password to export the full archive."));
gtk_window_set_title(GTK_WINDOW(password_dialog), _("Enter password to export archive"));
gtk_dialog_set_default_response(GTK_DIALOG(password_dialog), GTK_RESPONSE_OK);
auto* message_area = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(password_dialog));
auto* password_entry = gtk_entry_new();
gtk_entry_set_visibility(GTK_ENTRY(password_entry), false);
gtk_entry_set_invisible_char(GTK_ENTRY(password_entry), '*');
gtk_box_pack_start(GTK_BOX(message_area), password_entry, true, true, 0);
gtk_widget_show_all(password_dialog);
auto res = gtk_dialog_run(GTK_DIALOG(password_dialog));
if (res == GTK_RESPONSE_OK) {
password = gtk_entry_get_text(GTK_ENTRY(password_entry));
} else {
gtk_widget_destroy(password_dialog);
return;
}
gtk_widget_destroy(password_dialog);
}
// export account
auto success = priv->accountModel_->exportToFile(priv->accountId, filename, password);
std::string label = success? _("Account exported!") : _("Export account failure.");
gtk_button_set_label(GTK_BUTTON(priv->button_export_account), label.c_str());
g_free(filename);
if (success) {
g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
}
}
static void
build_creation_wizard_view(AccountCreationWizard *view)
{
g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(view));
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
/* set ring logo */
GError *error = NULL;
GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/net/jami/JamiGnome/jami-logo-blue",
-1, 50, TRUE, &error);
if (logo_ring == NULL) {
g_debug("Could not load logo: %s", error->message);
g_clear_error(&error);
} else
gtk_image_set_from_pixbuf(GTK_IMAGE(priv->choose_account_type_ring_logo), logo_ring);
/* create the username_registration_box */
priv->username_registration_box = username_registration_box_new_empty(false);
gtk_box_pack_end(GTK_BOX(priv->box_username_entry), GTK_WIDGET(priv->username_registration_box), false, false, 0);
gtk_widget_show(priv->username_registration_box);
priv->entry_username = GTK_WIDGET(
username_registration_box_get_entry(
USERNAME_REGISTRATION_BOX(priv->username_registration_box)
)
);
/* use the real name / username of the logged in user as the default */
const char* real_name = g_get_real_name();
const char* user_name = g_get_user_name();
g_debug("real_name = %s",real_name);
g_debug("user_name = %s",user_name);
/* check first if the real name was determined */
if (g_strcmp0 (real_name,"Unknown") != 0)
gtk_entry_set_text(GTK_ENTRY(priv->entry_display_name), real_name);
else
gtk_entry_set_text(GTK_ENTRY(priv->entry_display_name), user_name);
/* choose_account_type signals */
g_signal_connect_swapped(priv->button_new_account, "clicked", G_CALLBACK(account_creation_wizard_show_preview), view);
g_signal_connect_swapped(priv->button_import_from_device, "clicked", G_CALLBACK(show_import_from_device), view);
g_signal_connect_swapped(priv->button_import_from_backup, "clicked", G_CALLBACK(show_import_from_backup), view);
g_signal_connect(priv->button_show_advanced, "clicked", G_CALLBACK(show_advanced), view);
g_signal_connect_swapped(priv->button_connect_account_manager, "clicked", G_CALLBACK(show_connect_account_manager), view);
g_signal_connect(priv->button_new_sip_account, "clicked", G_CALLBACK(create_new_sip_account), view);
/* account_creation signals */
g_signal_connect_swapped(priv->entry_username, "changed", G_CALLBACK(entries_new_account_changed), view);
g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), view);
g_signal_connect(priv->button_account_creation_previous, "clicked", G_CALLBACK(account_creation_previous_clicked), view);
g_signal_connect_swapped(priv->entry_password, "changed", G_CALLBACK(entries_new_account_changed), view);
g_signal_connect_swapped(priv->entry_password_confirm, "changed", G_CALLBACK(entries_new_account_changed), view);
g_signal_connect_swapped(priv->entry_display_name, "changed", G_CALLBACK(entries_new_account_changed), view);
g_signal_connect(priv->switch_register, "notify::active", G_CALLBACK(sign_up_blockchain_switched), view);
g_signal_connect_swapped(priv->username_registration_box, "username-availability-changed", G_CALLBACK(username_availability_changed), view);
/* existing_account signals */
g_signal_connect_swapped(priv->button_import_from_previous, "clicked", G_CALLBACK(show_choose_account_type), view);
g_signal_connect_swapped(priv->button_pin_info, "clicked", G_CALLBACK(show_pin_label), view);
g_signal_connect_swapped(priv->button_import_from_next, "clicked", G_CALLBACK(existing_account_next_clicked), view);
g_signal_connect(priv->entry_existing_account_pin, "changed", G_CALLBACK(entries_existing_account_changed), view);
g_signal_connect(priv->entry_existing_account_archive, "file-set", G_CALLBACK(entries_existing_account_changed), view);
g_signal_connect(priv->entry_existing_account_password, "changed", G_CALLBACK(entries_existing_account_changed), view);
/* Backup */
g_signal_connect_swapped(priv->button_export_account, "clicked", G_CALLBACK(choose_export_file), view);
g_signal_connect_swapped(priv->button_skip_backup, "clicked", G_CALLBACK(skip_backup_clicked), view);
g_signal_connect_swapped(priv->button_backup_info, "clicked", G_CALLBACK(show_backup_label), view);
/* error_view signals */
g_signal_connect_swapped(priv->button_error_view_ok, "clicked", G_CALLBACK(error_ok_clicked), view);
/* Connect to account_manager */
g_signal_connect_swapped(priv->entry_account_manager_username, "changed", G_CALLBACK(entries_connect_account_manager_changed), view);
g_signal_connect_swapped(priv->entry_account_manager_password, "changed", G_CALLBACK(entries_connect_account_manager_changed), view);
g_signal_connect_swapped(priv->entry_account_manager_uri, "changed", G_CALLBACK(entries_connect_account_manager_changed), view);
g_signal_connect(priv->button_account_manager_connect_previous, "clicked", G_CALLBACK(connect_account_manager_previous_clicked), view);
g_signal_connect(priv->button_account_manager_connect_next, "clicked", G_CALLBACK(connect_account_manager_next_clicked), view);
show_choose_account_type(view);
gtk_button_set_relief(GTK_BUTTON(priv->button_show_advanced), GTK_RELIEF_NONE);
auto provider = gtk_css_provider_new();
gtk_css_provider_load_from_data(provider,
".black { color: grey; font-size: 0.8em; }\
.transparent-button { margin-left: 10px; border: 0; background-color: rgba(0,0,0,0); margin-right: 0; padding-right: 0;}\
.infos-button { margin: 0; border: 0; background-color: rgba(0,0,0,0); padding: 0; box-shadow: 0;}",
-1, nullptr
);
gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
GtkWidget *
account_creation_wizard_new(lrc::api::AVModel& avModel, lrc::api::NewAccountModel& accountModel)
{
gpointer view = g_object_new(ACCOUNT_CREATION_WIZARD_TYPE, NULL);
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
priv->avModel_ = &avModel;
priv->accountModel_ = &accountModel;
build_creation_wizard_view(ACCOUNT_CREATION_WIZARD(view));
return (GtkWidget *)view;
}
void
account_creation_wizard_show_preview(AccountCreationWizard *win, gboolean show_preview)
{
auto* priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
if (priv->account_creation) {
if (show_preview)
gtk_widget_show(priv->account_creation);
else
gtk_widget_hide(priv->account_creation);
}
priv->mode = Mode::ADD_LOCAL;
/* Similarily to general settings view, we construct and destroy the avatar manipulation widget
each time the profile is made visible / hidden. While not the most elegant solution, this
allows us to run the preview if and only if it is displayed, and always stop it when hidden. */
if (show_preview && !priv->avatar_manipulation) {
priv->avatar_manipulation = avatar_manipulation_new_from_wizard(priv->avModel_);
gtk_box_pack_start(GTK_BOX(priv->box_avatarselection), priv->avatar_manipulation, true, true, 0);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->account_creation);
} else if (priv->avatar_manipulation) {
/* make sure the AvatarManipulation widget is destroyed so the VideoWidget inside of it is too;
* NOTE: destorying its parent (box_avatarselection) first will cause a mystery 'BadDrawable'
* crash due to X error */
gtk_container_remove(GTK_CONTAINER(priv->box_avatarselection), priv->avatar_manipulation);
priv->avatar_manipulation = nullptr;
}
}