From 5beb0b84d075a49d1f9190dcd6d21e6b758e52a8 Mon Sep 17 00:00:00 2001 From: Gaelle Braud Date: Mon, 24 Jun 2024 16:26:28 +0200 Subject: [PATCH] register --- Linphone/core/App.cpp | 14 +- Linphone/core/CMakeLists.txt | 1 + Linphone/core/login/LoginPage.cpp | 17 +- Linphone/core/phone-number/PhoneNumber.cpp | 7 + Linphone/core/phone-number/PhoneNumber.hpp | 3 +- .../core/phone-number/PhoneNumberList.cpp | 18 +- .../core/phone-number/PhoneNumberList.hpp | 2 + .../core/phone-number/PhoneNumberProxy.cpp | 10 +- .../core/phone-number/PhoneNumberProxy.hpp | 11 +- Linphone/core/register/RegisterPage.cpp | 107 ++++++++++ Linphone/core/register/RegisterPage.hpp | 68 ++++++ Linphone/data/config/linphonerc-factory | 1 + Linphone/model/CMakeLists.txt | 2 + Linphone/model/account/AccountManager.cpp | 174 ++++++++++++++- Linphone/model/account/AccountManager.hpp | 23 +- .../account/AccountManagerServicesModel.cpp | 101 +++++++++ .../account/AccountManagerServicesModel.hpp | 72 +++++++ .../AccountManagerServicesRequestModel.cpp | 72 +++++++ .../AccountManagerServicesRequestModel.hpp | 70 +++++++ Linphone/model/core/CoreModel.hpp | 1 + Linphone/model/logger/LoggerModel.cpp | 6 +- Linphone/model/setting/SettingsModel.cpp | 140 ++++++------- Linphone/tool/Constants.hpp | 2 +- Linphone/tool/LinphoneEnums.hpp | 23 ++ Linphone/tool/Utils.cpp | 5 + Linphone/tool/Utils.hpp | 1 + Linphone/view/App/AppWindow.qml | 4 +- Linphone/view/App/Layout/LoginLayout.qml | 27 +-- Linphone/view/App/Main.qml | 27 ++- Linphone/view/Item/DigitInput.qml | 1 + Linphone/view/Item/ErrorText.qml | 12 +- Linphone/view/Item/Form/LoginForm.qml | 4 +- Linphone/view/Item/LoadingPopup.qml | 9 + Linphone/view/Item/PhoneNumberComboBox.qml | 10 +- Linphone/view/Item/PhoneNumberInput.qml | 100 +++++---- Linphone/view/Item/TextField.qml | 2 +- Linphone/view/Layout/FormItemLayout.qml | 27 +-- Linphone/view/Page/Login/LoginPage.qml | 1 + .../view/Page/Login/RegisterCheckingPage.qml | 16 +- Linphone/view/Page/Login/RegisterPage.qml | 198 +++++++++++------- Linphone/view/Page/Login/SIPLoginPage.qml | 1 + 41 files changed, 1119 insertions(+), 271 deletions(-) create mode 100644 Linphone/core/register/RegisterPage.cpp create mode 100644 Linphone/core/register/RegisterPage.hpp create mode 100644 Linphone/model/account/AccountManagerServicesModel.cpp create mode 100644 Linphone/model/account/AccountManagerServicesModel.hpp create mode 100644 Linphone/model/account/AccountManagerServicesRequestModel.cpp create mode 100644 Linphone/model/account/AccountManagerServicesRequestModel.hpp diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 0ae4276c9..5dcd8a778 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -58,6 +58,7 @@ #include "core/participant/ParticipantProxy.hpp" #include "core/phone-number/PhoneNumber.hpp" #include "core/phone-number/PhoneNumberProxy.hpp" +#include "core/register/RegisterPage.hpp" #include "core/screen/ScreenList.hpp" #include "core/screen/ScreenProxy.hpp" #include "core/search/MagicSearchProxy.hpp" @@ -294,8 +295,17 @@ void App::initCore() { void App::initCppInterfaces() { qmlRegisterSingletonType( - Constants::MainQmlUri, 1, 0, "LoginPageCpp", - [](QQmlEngine *engine, QJSEngine *) -> QObject * { return new LoginPage(engine); }); + Constants::MainQmlUri, 1, 0, "LoginPageCpp", [](QQmlEngine *engine, QJSEngine *) -> QObject * { + static auto loginPage = new LoginPage(engine); + App::getInstance()->mEngine->setObjectOwnership(loginPage, QQmlEngine::CppOwnership); + return loginPage; + }); + qmlRegisterSingletonType( + Constants::MainQmlUri, 1, 0, "RegisterPageCpp", [](QQmlEngine *engine, QJSEngine *) -> QObject * { + static RegisterPage *registerPage = new RegisterPage(); + App::getInstance()->mEngine->setObjectOwnership(registerPage, QQmlEngine::CppOwnership); + return registerPage; + }); qmlRegisterSingletonType( "ConstantsCpp", 1, 0, "ConstantsCpp", [](QQmlEngine *engine, QJSEngine *) -> QObject * { return new Constants(engine); }); diff --git a/Linphone/core/CMakeLists.txt b/Linphone/core/CMakeLists.txt index a906494ea..9543c892c 100644 --- a/Linphone/core/CMakeLists.txt +++ b/Linphone/core/CMakeLists.txt @@ -25,6 +25,7 @@ list(APPEND _LINPHONEAPP_SOURCES core/phone-number/PhoneNumber.cpp core/phone-number/PhoneNumberList.cpp core/phone-number/PhoneNumberProxy.cpp + core/register/RegisterPage.cpp core/search/MagicSearchList.cpp core/search/MagicSearchProxy.cpp diff --git a/Linphone/core/login/LoginPage.cpp b/Linphone/core/login/LoginPage.cpp index f759c178e..c6c9e35e8 100644 --- a/Linphone/core/login/LoginPage.cpp +++ b/Linphone/core/login/LoginPage.cpp @@ -71,14 +71,27 @@ void LoginPage::login(const QString &username, const QString &password) { switch (state) { case linphone::RegistrationState::Failed: { emit accountManager->errorMessageChanged(*error); - accountManager->deleteLater(); + if (accountManager) { + accountManager->deleteLater(); + accountManager = nullptr; + } break; } case linphone::RegistrationState::Ok: { emit accountManager->errorMessageChanged(""); + if (accountManager) { + accountManager->deleteLater(); + accountManager = nullptr; + } + break; + } + case linphone::RegistrationState::Cleared: { + if (accountManager) { + accountManager->deleteLater(); + accountManager = nullptr; + } break; } - case linphone::RegistrationState::Cleared: case linphone::RegistrationState::None: case linphone::RegistrationState::Progress: case linphone::RegistrationState::Refreshing: diff --git a/Linphone/core/phone-number/PhoneNumber.cpp b/Linphone/core/phone-number/PhoneNumber.cpp index d555b778f..57e5c572f 100644 --- a/Linphone/core/phone-number/PhoneNumber.cpp +++ b/Linphone/core/phone-number/PhoneNumber.cpp @@ -19,10 +19,17 @@ */ #include "PhoneNumber.hpp" +#include "core/App.hpp" #include "tool/Utils.hpp" #include DEFINE_ABSTRACT_OBJECT(PhoneNumber) +QSharedPointer PhoneNumber::create(const std::shared_ptr &dialPlan) { + auto sharedPointer = QSharedPointer(new PhoneNumber(dialPlan), &QObject::deleteLater); + sharedPointer->moveToThread(App::getInstance()->thread()); + return sharedPointer; +} + PhoneNumber::PhoneNumber(const std::shared_ptr &dialPlan) : QObject(nullptr) { // Should be call from model Thread mustBeInLinphoneThread(getClassName()); diff --git a/Linphone/core/phone-number/PhoneNumber.hpp b/Linphone/core/phone-number/PhoneNumber.hpp index 4a74bd0bd..4e04e5acc 100644 --- a/Linphone/core/phone-number/PhoneNumber.hpp +++ b/Linphone/core/phone-number/PhoneNumber.hpp @@ -36,7 +36,7 @@ class PhoneNumber : public QObject, public AbstractObject { Q_PROPERTY(QString country MEMBER mCountry CONSTANT) public: - // Should be call from model Thread. Will be automatically in App thread after initialization + static QSharedPointer create(const std::shared_ptr &dialPlan); PhoneNumber(const std::shared_ptr &dialPlan); ~PhoneNumber(); @@ -47,6 +47,7 @@ public: QString mInternationalCallPrefix; QString mCountry; +private: DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/phone-number/PhoneNumberList.cpp b/Linphone/core/phone-number/PhoneNumberList.cpp index 7e89ff873..a910dc8e4 100644 --- a/Linphone/core/phone-number/PhoneNumberList.cpp +++ b/Linphone/core/phone-number/PhoneNumberList.cpp @@ -28,22 +28,28 @@ DEFINE_ABSTRACT_OBJECT(PhoneNumberList) +QSharedPointer PhoneNumberList::create() { + auto model = QSharedPointer(new PhoneNumberList(), &QObject::deleteLater); + model->moveToThread(App::getInstance()->thread()); + return model; +} + PhoneNumberList::PhoneNumberList(QObject *parent) : ListProxy(parent) { mustBeInMainThread(getClassName()); App::postModelAsync([=]() { // Model thread. auto dialPlans = linphone::Factory::get()->getDialPlans(); - QList> numbers; - QVector results; + QList> *numbers = new QList>(); for (auto it : dialPlans) { - auto numberModel = QSharedPointer::create(it); - numberModel->moveToThread(this->thread()); - numbers.push_back(numberModel); + auto numberModel = PhoneNumber::create(it); + numbers->push_back(numberModel); } // Invoke for adding stuffs in caller thread QMetaObject::invokeMethod(this, [this, numbers]() { mustBeInMainThread(this->log().arg(Q_FUNC_INFO)); - add(numbers); + resetData(); + add(*numbers); + delete numbers; }); }); } diff --git a/Linphone/core/phone-number/PhoneNumberList.hpp b/Linphone/core/phone-number/PhoneNumberList.hpp index 91f8436d3..3306b3548 100644 --- a/Linphone/core/phone-number/PhoneNumberList.hpp +++ b/Linphone/core/phone-number/PhoneNumberList.hpp @@ -29,9 +29,11 @@ class PhoneNumberList : public ListProxy, public AbstractObject { Q_OBJECT public: + static QSharedPointer create(); PhoneNumberList(QObject *parent = Q_NULLPTR); ~PhoneNumberList(); +private: DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/phone-number/PhoneNumberProxy.cpp b/Linphone/core/phone-number/PhoneNumberProxy.cpp index 0eecc9968..1efc784fb 100644 --- a/Linphone/core/phone-number/PhoneNumberProxy.cpp +++ b/Linphone/core/phone-number/PhoneNumberProxy.cpp @@ -20,13 +20,19 @@ #include "PhoneNumberProxy.hpp" #include "PhoneNumber.hpp" -#include "PhoneNumberList.hpp" + +DEFINE_ABSTRACT_OBJECT(PhoneNumberProxy) PhoneNumberProxy::PhoneNumberProxy(QObject *parent) : SortFilterProxy(parent) { - setSourceModel(new PhoneNumberList(this)); + mPhoneNumberList = PhoneNumberList::create(); + setSourceModel(mPhoneNumberList.get()); sort(0); } +PhoneNumberProxy::~PhoneNumberProxy() { + setSourceModel(nullptr); +} + QString PhoneNumberProxy::getFilterText() const { return mFilterText; } diff --git a/Linphone/core/phone-number/PhoneNumberProxy.hpp b/Linphone/core/phone-number/PhoneNumberProxy.hpp index 27c38c112..0c99a8d7f 100644 --- a/Linphone/core/phone-number/PhoneNumberProxy.hpp +++ b/Linphone/core/phone-number/PhoneNumberProxy.hpp @@ -22,21 +22,24 @@ #define PHONE_NUMBER_PROXY_H_ #include "../proxy/SortFilterProxy.hpp" +#include "PhoneNumberList.hpp" +#include "tool/AbstractObject.hpp" // ============================================================================= -class PhoneNumberProxy : public SortFilterProxy { +class PhoneNumberProxy : public SortFilterProxy, public AbstractObject { Q_OBJECT Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged) public: PhoneNumberProxy(QObject *parent = Q_NULLPTR); + ~PhoneNumberProxy(); QString getFilterText() const; void setFilterText(const QString &filter); - Q_INVOKABLE int findIndexByCountryCallingCode(const QString& countryCallingCode); + Q_INVOKABLE int findIndexByCountryCallingCode(const QString &countryCallingCode); signals: void filterTextChanged(); @@ -46,6 +49,10 @@ protected: virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; QString mFilterText; + QSharedPointer mPhoneNumberList; + +private: + DECLARE_ABSTRACT_OBJECT }; #endif diff --git a/Linphone/core/register/RegisterPage.cpp b/Linphone/core/register/RegisterPage.cpp new file mode 100644 index 000000000..8ce457a44 --- /dev/null +++ b/Linphone/core/register/RegisterPage.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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, see . + */ + +#include "RegisterPage.hpp" +#include + +#include "core/App.hpp" + +#include "model/account/AccountManager.hpp" + +DEFINE_ABSTRACT_OBJECT(RegisterPage) + +RegisterPage::RegisterPage(QObject *parent) : QObject(parent) { + mustBeInMainThread(getClassName()); +} + +RegisterPage::~RegisterPage() { + mustBeInMainThread("~" + getClassName()); +} + +void RegisterPage::registerNewAccount(const QString &username, + const QString &password, + const QString &email, + const QString &phoneNumber) { + App::postModelAsync([=]() { + // Create on Model thread. + // registrationFailed(error); }); + AccountManager::RegisterType registerType; + QString address; + if (email.isEmpty()) { + registerType = AccountManager::RegisterType::PhoneNumber; + address = phoneNumber; + } else { + registerType = AccountManager::RegisterType::Email; + address = email; + } + auto accountManager = new AccountManager(); + connect(accountManager, &AccountManager::newAccountCreationSucceed, this, + [this, registerType, address, accountManager](const QString &sipAddress) mutable { + App::postCoreAsync([this, registerType, address, sipAddress, accountManager]() { + emit newAccountCreationSucceed(registerType == AccountManager::RegisterType::Email, address, + sipAddress); + }); + if (accountManager) { + accountManager->deleteLater(); + accountManager = nullptr; + } + }); + connect(accountManager, &AccountManager::registerNewAccountFailed, this, + [this, accountManager](const QString &errorMessage) mutable { + App::postCoreAsync( + [this, errorMessage, accountManager]() { emit registerNewAccountFailed(errorMessage); }); + if (accountManager) { + accountManager->deleteLater(); + accountManager = nullptr; + } + }); + connect(accountManager, &AccountManager::errorInField, this, + [this, accountManager](const QString &field, const QString &errorMessage) mutable { + App::postCoreAsync([this, field, errorMessage]() { emit errorInField(field, errorMessage); }); + if (accountManager) { + accountManager->deleteLater(); + accountManager = nullptr; + } + }); + connect(accountManager, &AccountManager::tokenConversionSucceed, this, + [this, accountManager]() { App::postCoreAsync([this]() { emit tokenConversionSucceed(); }); }); + accountManager->registerNewAccount(username, password, registerType, address); + }); +} + +void RegisterPage::linkNewAccountUsingCode(const QString &code, + bool registerWithEmail, + const QString &sipIdentityAddress) { + App::postModelAsync([=]() { + auto accountManager = new AccountManager(); + connect(accountManager, &AccountManager::linkingNewAccountWithCodeSucceed, this, [this, accountManager]() { + App::postCoreAsync([this]() { emit linkingNewAccountWithCodeSucceed(); }); + accountManager->deleteLater(); + }); + connect(accountManager, &AccountManager::linkingNewAccountWithCodeFailed, this, + [this, accountManager](const QString &errorMessage) { + App::postCoreAsync([this, errorMessage]() { emit linkingNewAccountWithCodeFailed(errorMessage); }); + accountManager->deleteLater(); + }); + accountManager->linkNewAccountUsingCode( + code, registerWithEmail ? AccountManager::RegisterType::Email : AccountManager::RegisterType::PhoneNumber, + sipIdentityAddress); + }); +} \ No newline at end of file diff --git a/Linphone/core/register/RegisterPage.hpp b/Linphone/core/register/RegisterPage.hpp new file mode 100644 index 000000000..8543fbe9b --- /dev/null +++ b/Linphone/core/register/RegisterPage.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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, see . + */ + +#ifndef REGISTERPAGE_H_ +#define REGISTERPAGE_H_ + +#include "tool/AbstractObject.hpp" +#include "tool/thread/SafeConnection.hpp" +#include +#include + +class AccountManager; +class RegisterPage : public QObject, public AbstractObject { + Q_OBJECT + +public: + RegisterPage(QObject *parent = nullptr); + ~RegisterPage(); + + // Q_PROPERTY(linphone::RegistrationState registrationState READ getRegistrationState NOTIFY + // registrationStateChanged) Q_PROPERTY(QString errorMessage READ getErrorMessage NOTIFY errorMessageChanged) + + Q_INVOKABLE void registerNewAccount(const QString &username, + const QString &password, + const QString &email, + const QString &phoneNumber); + Q_INVOKABLE void + linkNewAccountUsingCode(const QString &code, bool registerWithEmail, const QString &sipIdentityAddress); + +signals: + void registrationFailed(const QString &errorMessage); + void errorMessageChanged(); + void newAccountCreationSucceed(bool withEmail, // false if creation with phone number + const QString &address, + const QString &sipIdentityAddress); + void registerNewAccountFailed(const QString &error); + void errorInField(const QString &field, const QString &error); + void tokenConversionSucceed(); + void linkingNewAccountWithCodeSucceed(); + void linkingNewAccountWithCodeFailed(const QString &error); + +private: + linphone::RegistrationState mRegistrationState = linphone::RegistrationState::None; + QSharedPointer> mAccountManagerConnection; + std::shared_ptr mAccountManager; + QString mErrorMessage; + + DECLARE_ABSTRACT_OBJECT +}; + +#endif \ No newline at end of file diff --git a/Linphone/data/config/linphonerc-factory b/Linphone/data/config/linphonerc-factory index 697e1b59e..77fa78ae3 100644 --- a/Linphone/data/config/linphonerc-factory +++ b/Linphone/data/config/linphonerc-factory @@ -3,6 +3,7 @@ backend=1 # 1 means FlexiAPI, 0 is XMLRPC url=https://subscribe.linphone.org/api/ # replace above URL by https://staging-subscribe.linphone.org/api/ for testing +# url=https://flexisip-staging-master.linphone.org/api/ [alerts] alerts_enabled=1 diff --git a/Linphone/model/CMakeLists.txt b/Linphone/model/CMakeLists.txt index cfb08ff55..d8fde253b 100644 --- a/Linphone/model/CMakeLists.txt +++ b/Linphone/model/CMakeLists.txt @@ -1,6 +1,8 @@ list(APPEND _LINPHONEAPP_SOURCES model/account/AccountModel.cpp model/account/AccountManager.cpp + model/account/AccountManagerServicesModel.cpp + model/account/AccountManagerServicesRequestModel.cpp model/call/CallModel.cpp diff --git a/Linphone/model/account/AccountManager.cpp b/Linphone/model/account/AccountManager.cpp index 3e7aed757..2e0621404 100644 --- a/Linphone/model/account/AccountManager.cpp +++ b/Linphone/model/account/AccountManager.cpp @@ -21,10 +21,16 @@ #include "AccountManager.hpp" #include +#include +#include +#include +#include #include +#include #include "core/path/Paths.hpp" #include "model/core/CoreModel.hpp" +#include "model/tool/ToolModel.hpp" #include "tool/Utils.hpp" DEFINE_ABSTRACT_OBJECT(AccountManager) @@ -93,6 +99,172 @@ bool AccountManager::login(QString username, QString password, QString *errorMes return true; } +void AccountManager::registerNewAccount(const QString &username, + const QString &password, + RegisterType type, + const QString ®isterAddress) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + if (!mAccountManagerServicesModel) { + auto core = CoreModel::getInstance()->getCore(); + auto ams = core->createAccountManagerServices(); + mAccountManagerServicesModel = Utils::makeQObject_ptr(ams); + } + connect( + mAccountManagerServicesModel.get(), &AccountManagerServicesModel::requestSuccessfull, this, + [this, username, password, type, registerAddress]( + const std::shared_ptr &request, const std::string &data) { + if (request->getType() == linphone::AccountManagerServicesRequest::Type::AccountCreationRequestToken) { + QString verifyTokenUrl = Utils::coreStringToAppString(data); + qDebug() << "[AccountManager] request token succeed" << verifyTokenUrl; + + QDesktopServices::openUrl(verifyTokenUrl); + auto creationToken = verifyTokenUrl.mid(verifyTokenUrl.lastIndexOf("/") + 1); + + // QNetworkRequest req; + timer.setSingleShot(true); + timer.setInterval(2000); + QObject::connect(&timer, &QTimer::timeout, this, [this, creationToken]() { + mAccountManagerServicesModel->convertCreationRequestTokenIntoCreationToken( + Utils::appStringToCoreString(creationToken)); + }); + timer.start(); + // req.setUrl(QUrl(verifyTokenUrl)); + + } else if (request->getType() == linphone::AccountManagerServicesRequest::Type:: + AccountCreationTokenFromAccountCreationRequestToken) { + qDebug() << "[AccountManager] request token conversion succeed" << data; + emit tokenConversionSucceed(); + timer.stop(); + mAccountManagerServicesModel->createAccountUsingToken(Utils::appStringToCoreString(username), + Utils::appStringToCoreString(password), data); + + } else if (request->getType() == linphone::AccountManagerServicesRequest::Type::CreateAccountUsingToken) { + auto core = CoreModel::getInstance()->getCore(); + auto factory = linphone::Factory::get(); + mCreatedSipAddress = Utils::coreStringToAppString(data); + auto createdSipIdentityAddress = ToolModel::interpretUrl(mCreatedSipAddress); + core->addAuthInfo(factory->createAuthInfo(Utils::appStringToCoreString(username), // Username. + "", // User ID. + Utils::appStringToCoreString(password), // Password. + "", // HA1. + "", // Realm. + createdSipIdentityAddress->getDomain() // Domain. + )); + if (type == RegisterType::Email) { + qDebug() << "[AccountManager] creation succeed, email verification" << registerAddress; + mAccountManagerServicesModel->linkEmailByEmail( + ToolModel::interpretUrl(Utils::coreStringToAppString(data)), + Utils::appStringToCoreString(registerAddress)); + } else { + qDebug() << "[AccountManager] creation succeed, sms verification" << registerAddress; + mAccountManagerServicesModel->linkPhoneNumberBySms( + ToolModel::interpretUrl(Utils::coreStringToAppString(data)), + Utils::appStringToCoreString(registerAddress)); + } + } else if (request->getType() == + linphone::AccountManagerServicesRequest::Type::SendEmailLinkingCodeByEmail) { + qDebug() << "[AccountManager] send email succeed, link account using code"; + emit newAccountCreationSucceed(mCreatedSipAddress, type, registerAddress); + mCreatedSipAddress.clear(); + } else if (request->getType() == + linphone::AccountManagerServicesRequest::Type::SendPhoneNumberLinkingCodeBySms) { + qDebug() << "[AccountManager] send phone number succeed, link account using code"; + emit newAccountCreationSucceed(mCreatedSipAddress, type, registerAddress); + mCreatedSipAddress.clear(); + } + }); + connect( + mAccountManagerServicesModel.get(), &AccountManagerServicesModel::requestError, this, + [this](const std::shared_ptr &request, int statusCode, + const std::string &errorMessage, const std::shared_ptr ¶meterErrors) { + if (request->getType() == linphone::AccountManagerServicesRequest::Type::AccountCreationRequestToken) { + qDebug() << "[AccountManager] error creating request token :" << errorMessage; + emit registerNewAccountFailed(Utils::coreStringToAppString(errorMessage)); + } else if (request->getType() == linphone::AccountManagerServicesRequest::Type:: + AccountCreationTokenFromAccountCreationRequestToken) { + qDebug() << "[AccountManager] error converting token into creation token :" << errorMessage; + if (parameterErrors) { + timer.stop(); + emit registerNewAccountFailed(Utils::coreStringToAppString(errorMessage)); + } else { + timer.start(); + } + } else if (request->getType() == linphone::AccountManagerServicesRequest::Type::CreateAccountUsingToken) { + qDebug() << "[AccountManager] error creating account :" << errorMessage; + if (parameterErrors) { + for (const std::string &key : parameterErrors->getKeys()) { + emit errorInField(Utils::coreStringToAppString(key), + Utils::coreStringToAppString(errorMessage)); + } + } else { + emit registerNewAccountFailed(Utils::coreStringToAppString(errorMessage)); + } + } else if (request->getType() == + linphone::AccountManagerServicesRequest::Type::SendEmailLinkingCodeByEmail) { + qDebug() << "[AccountManager] error sending code to email" << errorMessage; + if (parameterErrors) { + for (const std::string &key : parameterErrors->getKeys()) { + emit errorInField(Utils::coreStringToAppString(key), + Utils::coreStringToAppString(errorMessage)); + } + } else { + emit registerNewAccountFailed(Utils::coreStringToAppString(errorMessage)); + } + } else if (request->getType() == + linphone::AccountManagerServicesRequest::Type::SendPhoneNumberLinkingCodeBySms) { + qDebug() << "[AccountManager] error sending code to phone number" << errorMessage; + if (parameterErrors) { + for (const std::string &key : parameterErrors->getKeys()) { + emit errorInField(Utils::coreStringToAppString(key), + Utils::coreStringToAppString(errorMessage)); + } + } else { + emit registerNewAccountFailed(Utils::coreStringToAppString(errorMessage)); + } + } + }); + mAccountManagerServicesModel->requestToken(); +} + +void AccountManager::linkNewAccountUsingCode(const QString &code, + RegisterType registerType, + const QString &sipAddress) { + auto sipIdentityAddress = ToolModel::interpretUrl(sipAddress); + if (!mAccountManagerServicesModel) { + auto core = CoreModel::getInstance()->getCore(); + auto ams = core->createAccountManagerServices(); + mAccountManagerServicesModel = Utils::makeQObject_ptr(ams); + } + connect( + mAccountManagerServicesModel.get(), &AccountManagerServicesModel::requestSuccessfull, this, + [this](const std::shared_ptr &request, const std::string &data) { + if (request->getType() == linphone::AccountManagerServicesRequest::Type::LinkEmailUsingCode) { + qDebug() << "[AccountManager] link email to account succeed" << data; + emit linkingNewAccountWithCodeSucceed(); + } else if (request->getType() == linphone::AccountManagerServicesRequest::Type::LinkPhoneNumberUsingCode) { + qDebug() << "[AccountManager] link phone number to account succeed" << data; + emit linkingNewAccountWithCodeSucceed(); + } + }); + connect( + mAccountManagerServicesModel.get(), &AccountManagerServicesModel::requestError, this, + [this](const std::shared_ptr &request, int statusCode, + const std::string &errorMessage, const std::shared_ptr ¶meterErrors) { + if (request->getType() == linphone::AccountManagerServicesRequest::Type::LinkEmailUsingCode) { + qDebug() << "[AccountManager] error linking email to account" << errorMessage; + } else if (request->getType() == linphone::AccountManagerServicesRequest::Type::LinkPhoneNumberUsingCode) { + qDebug() << "[AccountManager] error linking phone number to account" << errorMessage; + } + emit linkingNewAccountWithCodeFailed(Utils::coreStringToAppString(errorMessage)); + }); + if (registerType == RegisterType::Email) + mAccountManagerServicesModel->linkEmailToAccountUsingCode(sipIdentityAddress, + Utils::appStringToCoreString(code)); + else + mAccountManagerServicesModel->linkPhoneNumberToAccountUsingCode(sipIdentityAddress, + Utils::appStringToCoreString(code)); +} + void AccountManager::onRegistrationStateChanged(const std::shared_ptr &account, linphone::RegistrationState state, const std::string &message) { @@ -112,4 +284,4 @@ void AccountManager::onRegistrationStateChanged(const std::shared_ptr +#include #include +#include "AccountManagerServicesModel.hpp" +#include "AccountManagerServicesRequestModel.hpp" #include "AccountModel.hpp" #include "tool/AbstractObject.hpp" @@ -34,18 +37,36 @@ public: ~AccountManager(); bool login(QString username, QString password, QString *errorMessage = nullptr); - std::shared_ptr createAccount(const QString &assistantFile); + enum RegisterType { PhoneNumber = 0, Email = 1 }; + void registerNewAccount(const QString &username, + const QString &password, + RegisterType type, + const QString ®isterAddress); + + void linkNewAccountUsingCode(const QString &code, RegisterType registerType, const QString &sipAddress); + void onRegistrationStateChanged(const std::shared_ptr &account, linphone::RegistrationState state, const std::string &message); + signals: void registrationStateChanged(linphone::RegistrationState state); void errorMessageChanged(const QString &errorMessage); + void newAccountCreationSucceed(QString sipAddress, RegisterType registerType, const QString ®isterAddress); + void registerNewAccountFailed(const QString &error); + void tokenConversionSucceed(); + void errorInField(const QString &field, const QString &error); + void linkingNewAccountWithCodeSucceed(); + void linkingNewAccountWithCodeFailed(const QString &error); private: std::shared_ptr mAccountModel; + std::shared_ptr mAccountManagerServicesModel; + QTimer timer; + QString mCreatedSipAddress; + // std::shared_ptr mCreatedSipIdentityAddress; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/model/account/AccountManagerServicesModel.cpp b/Linphone/model/account/AccountManagerServicesModel.cpp new file mode 100644 index 000000000..6eb8a8fc2 --- /dev/null +++ b/Linphone/model/account/AccountManagerServicesModel.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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, see . + */ + +#include "AccountManagerServicesModel.hpp" + +#include "core/path/Paths.hpp" +#include "model/core/CoreModel.hpp" +#include "tool/Utils.hpp" +#include "tool/providers/AvatarProvider.hpp" +#include +#include + +DEFINE_ABSTRACT_OBJECT(AccountManagerServicesModel) + +AccountManagerServicesModel::AccountManagerServicesModel( + const std::shared_ptr &accountManagerServices, QObject *parent) + : mAccountManagerServices(accountManagerServices) { + mustBeInLinphoneThread(getClassName()); +} + +AccountManagerServicesModel::~AccountManagerServicesModel() { + mustBeInLinphoneThread("~" + getClassName()); +} + +void AccountManagerServicesModel::setRequestAndSubmit( + const std::shared_ptr &request) { + if (mRequest) { + disconnect(mRequest.get(), &AccountManagerServicesRequestModel::requestSuccessfull, this, nullptr); + disconnect(mRequest.get(), &AccountManagerServicesRequestModel::requestError, this, nullptr); + disconnect(mRequest.get(), &AccountManagerServicesRequestModel::devicesListFetched, this, nullptr); + mRequest = nullptr; + } + mRequest = Utils::makeQObject_ptr(request); + mRequest->setSelf(mRequest); + connect(mRequest.get(), &AccountManagerServicesRequestModel::requestSuccessfull, this, + &AccountManagerServicesModel::requestSuccessfull); + connect(mRequest.get(), &AccountManagerServicesRequestModel::requestError, this, + &AccountManagerServicesModel::requestError); + connect(mRequest.get(), &AccountManagerServicesRequestModel::devicesListFetched, this, + &AccountManagerServicesModel::devicesListFetched); + mRequest->submit(); +} + +void AccountManagerServicesModel::requestToken() { + auto req = mAccountManagerServices->createGetAccountCreationRequestTokenRequest(); + setRequestAndSubmit(req); +} + +void AccountManagerServicesModel::convertCreationRequestTokenIntoCreationToken(const std::string &token) { + auto req = mAccountManagerServices->createGetAccountCreationTokenFromRequestTokenRequest(token); + setRequestAndSubmit(req); +} + +void AccountManagerServicesModel::createAccountUsingToken(const std::string &username, + const std::string &password, + const std::string &token, + const std::string &algorithm) { + auto req = mAccountManagerServices->createNewAccountUsingTokenRequest(username, password, algorithm, token); + setRequestAndSubmit(req); +} + +void AccountManagerServicesModel::linkPhoneNumberBySms(const std::shared_ptr &sipIdentityAddress, + const std::string &phoneNumber) { + auto req = mAccountManagerServices->createSendPhoneNumberLinkingCodeBySmsRequest(sipIdentityAddress, phoneNumber); + setRequestAndSubmit(req); +} + +void AccountManagerServicesModel::linkEmailByEmail(const std::shared_ptr &sipIdentityAddress, + const std::string &emailAddress) { + auto req = mAccountManagerServices->createSendEmailLinkingCodeByEmailRequest(sipIdentityAddress, emailAddress); + setRequestAndSubmit(req); +} + +void AccountManagerServicesModel::linkPhoneNumberToAccountUsingCode( + const std::shared_ptr &sipIdentityAddress, const std::string &code) { + auto req = mAccountManagerServices->createLinkPhoneNumberToAccountUsingCodeRequest(sipIdentityAddress, code); + setRequestAndSubmit(req); +} + +void AccountManagerServicesModel::linkEmailToAccountUsingCode( + const std::shared_ptr &sipIdentityAddress, const std::string &code) { + auto req = mAccountManagerServices->createLinkEmailToAccountUsingCodeRequest(sipIdentityAddress, code); + setRequestAndSubmit(req); +} \ No newline at end of file diff --git a/Linphone/model/account/AccountManagerServicesModel.hpp b/Linphone/model/account/AccountManagerServicesModel.hpp new file mode 100644 index 000000000..6a5b27b96 --- /dev/null +++ b/Linphone/model/account/AccountManagerServicesModel.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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, see . + */ + +#ifndef ACCOUNT_MANAGER_SERVICES_MODEL_H_ +#define ACCOUNT_MANAGER_SERVICES_MODEL_H_ + +#include "AccountManagerServicesRequestModel.hpp" +#include "model/listener/Listener.hpp" +#include "tool/AbstractObject.hpp" + +#include +#include + +class AccountManagerServicesModel : public QObject, public AbstractObject { + Q_OBJECT +public: + AccountManagerServicesModel(const std::shared_ptr &accountManagerServices, + QObject *parent = nullptr); + ~AccountManagerServicesModel(); + + void requestToken(); + void convertCreationRequestTokenIntoCreationToken(const std::string &token); + void createAccountUsingToken(const std::string &username, + const std::string &password, + const std::string &token, + const std::string &algorithm = "SHA-256"); + void linkPhoneNumberBySms(const std::shared_ptr &sipIdentityAddress, + const std::string &phoneNumber); + void linkEmailByEmail(const std::shared_ptr &sipIdentityAddress, + const std::string &emailAddress); + + void linkPhoneNumberToAccountUsingCode(const std::shared_ptr &sipIdentityAddress, + const std::string &code); + void linkEmailToAccountUsingCode(const std::shared_ptr &sipIdentityAddress, + const std::string &code); + + void setRequestAndSubmit(const std::shared_ptr &request); + +signals: + void requestSuccessfull(const std::shared_ptr &request, + const std::string &data); + void requestError(const std::shared_ptr &request, + int statusCode, + const std::string &errorMessage, + const std::shared_ptr ¶meterErrors); + void devicesListFetched(const std::shared_ptr &request, + const std::list> &devicesList); + +private: + DECLARE_ABSTRACT_OBJECT + std::shared_ptr mAccountManagerServices; + std::shared_ptr mRequest; +}; + +#endif diff --git a/Linphone/model/account/AccountManagerServicesRequestModel.cpp b/Linphone/model/account/AccountManagerServicesRequestModel.cpp new file mode 100644 index 000000000..1b8669101 --- /dev/null +++ b/Linphone/model/account/AccountManagerServicesRequestModel.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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, see . + */ + +#include "AccountManagerServicesRequestModel.hpp" + +#include "core/path/Paths.hpp" +#include "model/core/CoreModel.hpp" +#include "tool/Utils.hpp" +#include "tool/providers/AvatarProvider.hpp" +#include +#include + +DEFINE_ABSTRACT_OBJECT(AccountManagerServicesRequestModel) + +AccountManagerServicesRequestModel::AccountManagerServicesRequestModel( + const std::shared_ptr &accountManagerServicesRequest, QObject *parent) + : ::Listener( + accountManagerServicesRequest, parent) { + mustBeInLinphoneThread(getClassName()); +} + +AccountManagerServicesRequestModel::~AccountManagerServicesRequestModel() { + mustBeInLinphoneThread("~" + getClassName()); +} + +void AccountManagerServicesRequestModel::submit() { + mMonitor->submit(); +} + +linphone::AccountManagerServicesRequest::Type AccountManagerServicesRequestModel::getType() const { + return mMonitor->getType(); +} + +//-------------------------------------------------------------------------------- +// LINPHONE +//-------------------------------------------------------------------------------- + +void AccountManagerServicesRequestModel::onRequestSuccessful( + const std::shared_ptr &request, const std::string &data) { + emit requestSuccessfull(request, data); +} + +void AccountManagerServicesRequestModel::onRequestError( + const std::shared_ptr &request, + int statusCode, + const std::string &errorMessage, + const std::shared_ptr ¶meterErrors) { + emit requestError(request, statusCode, errorMessage, parameterErrors); +} + +void AccountManagerServicesRequestModel::onDevicesListFetched( + const std::shared_ptr &request, + const std::list> &devicesList) { + emit devicesListFetched(request, devicesList); +} diff --git a/Linphone/model/account/AccountManagerServicesRequestModel.hpp b/Linphone/model/account/AccountManagerServicesRequestModel.hpp new file mode 100644 index 000000000..7ba9fa365 --- /dev/null +++ b/Linphone/model/account/AccountManagerServicesRequestModel.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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, see . + */ + +#ifndef ACCOUNT_MANAGER_SERVICES_REQUEST_MODEL_H_ +#define ACCOUNT_MANAGER_SERVICES_REQUEST_MODEL_H_ + +#include "model/listener/Listener.hpp" +#include "tool/AbstractObject.hpp" + +#include +#include + +class AccountManagerServicesRequestModel + : public ::Listener, + public linphone::AccountManagerServicesRequestListener, + public AbstractObject { + Q_OBJECT +public: + AccountManagerServicesRequestModel( + const std::shared_ptr &accountManagerServicesRequest, + QObject *parent = nullptr); + ~AccountManagerServicesRequestModel(); + + void submit(); + linphone::AccountManagerServicesRequest::Type getType() const; + +signals: + void requestSuccessfull(const std::shared_ptr &request, + const std::string &data); + void requestError(const std::shared_ptr &request, + int statusCode, + const std::string &errorMessage, + const std::shared_ptr ¶meterErrors); + void devicesListFetched(const std::shared_ptr &request, + const std::list> &devicesList); + +private: + DECLARE_ABSTRACT_OBJECT + + //-------------------------------------------------------------------------------- + // LINPHONE + //-------------------------------------------------------------------------------- + virtual void onRequestSuccessful(const std::shared_ptr &request, + const std::string &data) override; + virtual void onRequestError(const std::shared_ptr &request, + int statusCode, + const std::string &errorMessage, + const std::shared_ptr ¶meterErrors) override; + virtual void onDevicesListFetched(const std::shared_ptr &request, + const std::list> &devicesList) override; +}; + +#endif diff --git a/Linphone/model/core/CoreModel.hpp b/Linphone/model/core/CoreModel.hpp index e039d9e8c..3fe589aa1 100644 --- a/Linphone/model/core/CoreModel.hpp +++ b/Linphone/model/core/CoreModel.hpp @@ -28,6 +28,7 @@ #include #include +#include "model/account/AccountManager.hpp" #include "model/cli/CliModel.hpp" #include "model/listener/Listener.hpp" #include "model/logger/LoggerModel.hpp" diff --git a/Linphone/model/logger/LoggerModel.cpp b/Linphone/model/logger/LoggerModel.cpp index cf6f9c7b8..d651a4967 100644 --- a/Linphone/model/logger/LoggerModel.cpp +++ b/Linphone/model/logger/LoggerModel.cpp @@ -28,9 +28,9 @@ #include "LoggerListener.hpp" #include "LoggerModel.hpp" +#include "model/setting/SettingsModel.hpp" #include "tool/Constants.hpp" #include "tool/Utils.hpp" -#include "model/setting/SettingsModel.hpp" #include "core/logger/QtLogger.hpp" // ----------------------------------------------------------------------------- @@ -127,7 +127,9 @@ void LoggerModel::applyConfig(const std::shared_ptr &config) { const QString folder = SettingsModel::getLogsFolder(config); linphone::Core::setLogCollectionPath(Utils::appStringToCoreString(folder)); enableFullLogs(SettingsModel::getFullLogsEnabled(config)); - enable(SettingsModel::getLogsEnabled(config)); + // TODO : uncomment when it is possible to change the config from settings + // enable(SettingsModel::getLogsEnabled(config)); + enable(true); } void LoggerModel::init() { diff --git a/Linphone/model/setting/SettingsModel.cpp b/Linphone/model/setting/SettingsModel.cpp index 173764646..272e8e2c1 100644 --- a/Linphone/model/setting/SettingsModel.cpp +++ b/Linphone/model/setting/SettingsModel.cpp @@ -19,11 +19,10 @@ */ #include "SettingsModel.hpp" -#include "model/core/CoreModel.hpp" -#include "tool/Utils.hpp" -#include "model/tool/ToolModel.hpp" #include "core/path/Paths.hpp" - +#include "model/core/CoreModel.hpp" +#include "model/tool/ToolModel.hpp" +#include "tool/Utils.hpp" // ============================================================================= @@ -63,18 +62,14 @@ QStringList SettingsModel::getVideoDevices() const { return result; } -QString SettingsModel::getVideoDevice () const { +QString SettingsModel::getVideoDevice() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - return Utils::coreStringToAppString( - CoreModel::getInstance()->getCore()->getVideoDevice() - ); + return Utils::coreStringToAppString(CoreModel::getInstance()->getCore()->getVideoDevice()); } -void SettingsModel::setVideoDevice (const QString &device) { +void SettingsModel::setVideoDevice(const QString &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - CoreModel::getInstance()->getCore()->setVideoDevice( - Utils::appStringToCoreString(device) - ); + CoreModel::getInstance()->getCore()->setVideoDevice(Utils::appStringToCoreString(device)); emit videoDeviceChanged(device); } @@ -94,8 +89,8 @@ void SettingsModel::resetCaptureGraph() { } void SettingsModel::createCaptureGraph() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - mSimpleCaptureGraph = - new MediastreamerUtils::SimpleCaptureGraph(Utils::appStringToCoreString(getCaptureDevice()), Utils::appStringToCoreString(getPlaybackDevice())); + mSimpleCaptureGraph = new MediastreamerUtils::SimpleCaptureGraph(Utils::appStringToCoreString(getCaptureDevice()), + Utils::appStringToCoreString(getPlaybackDevice())); mSimpleCaptureGraph->start(); emit captureGraphRunningChanged(getCaptureGraphRunning()); } @@ -135,7 +130,7 @@ void SettingsModel::deleteCaptureGraph() { mSimpleCaptureGraph = nullptr; } } -//Force a call on the 'detect' method of all audio filters, updating new or removed devices +// Force a call on the 'detect' method of all audio filters, updating new or removed devices void SettingsModel::accessCallSettings() { // Audio mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); @@ -148,7 +143,8 @@ void SettingsModel::accessCallSettings() { emit playbackGainChanged(getPlaybackGain()); emit captureGainChanged(getCaptureGain()); - // Media cards must not be used twice (capture card + call) else we will get latencies issues and bad echo calibrations in call. + // Media cards must not be used twice (capture card + call) else we will get latencies issues and bad echo + // calibrations in call. if (!getIsInCall()) { qDebug() << "Starting capture graph from accessing audio panel"; startCaptureGraph(); @@ -193,8 +189,7 @@ void SettingsModel::setPlaybackGain(float gain) { if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { mSimpleCaptureGraph->setPlaybackGain(gain); } - if((int)(oldGain*1000) != (int)(gain*1000)) - emit playbackGainChanged(gain); + if ((int)(oldGain * 1000) != (int)(gain * 1000)) emit playbackGainChanged(gain); } float SettingsModel::getCaptureGain() const { @@ -210,11 +205,10 @@ void SettingsModel::setCaptureGain(float gain) { if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { mSimpleCaptureGraph->setCaptureGain(gain); } - if((int)(oldGain *1000) != (int)(gain *1000)) - emit captureGainChanged(gain); + if ((int)(oldGain * 1000) != (int)(gain * 1000)) emit captureGainChanged(gain); } -QStringList SettingsModel::getCaptureDevices () const { +QStringList SettingsModel::getCaptureDevices() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); shared_ptr core = CoreModel::getInstance()->getCore(); QStringList list; @@ -226,7 +220,7 @@ QStringList SettingsModel::getCaptureDevices () const { return list; } -QStringList SettingsModel::getPlaybackDevices () const { +QStringList SettingsModel::getPlaybackDevices() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); shared_ptr core = CoreModel::getInstance()->getCore(); QStringList list; @@ -241,68 +235,64 @@ QStringList SettingsModel::getPlaybackDevices () const { // ----------------------------------------------------------------------------- -QString SettingsModel::getCaptureDevice () const { +QString SettingsModel::getCaptureDevice() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto audioDevice = CoreModel::getInstance()->getCore()->getInputAudioDevice(); - return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreModel::getInstance()->getCore()->getCaptureDevice()); + return Utils::coreStringToAppString(audioDevice ? audioDevice->getId() + : CoreModel::getInstance()->getCore()->getCaptureDevice()); } -void SettingsModel::setCaptureDevice (const QString &device) { +void SettingsModel::setCaptureDevice(const QString &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); std::string devId = Utils::appStringToCoreString(device); auto list = CoreModel::getInstance()->getCore()->getExtendedAudioDevices(); - auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) { - return audioItem->getId() == devId; - }); - if(audioDevice != list.cend()){ + auto audioDevice = + find_if(list.cbegin(), list.cend(), + [&](const std::shared_ptr &audioItem) { return audioItem->getId() == devId; }); + if (audioDevice != list.cend()) { CoreModel::getInstance()->getCore()->setCaptureDevice(devId); CoreModel::getInstance()->getCore()->setInputAudioDevice(*audioDevice); emit captureDeviceChanged(device); resetCaptureGraph(); - }else - qWarning() << "Cannot set Capture device. The ID cannot be matched with an existant device : " << device; + } else qWarning() << "Cannot set Capture device. The ID cannot be matched with an existant device : " << device; } // ----------------------------------------------------------------------------- -QString SettingsModel::getPlaybackDevice () const { +QString SettingsModel::getPlaybackDevice() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto audioDevice = CoreModel::getInstance()->getCore()->getOutputAudioDevice(); - return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreModel::getInstance()->getCore()->getPlaybackDevice()); + return Utils::coreStringToAppString(audioDevice ? audioDevice->getId() + : CoreModel::getInstance()->getCore()->getPlaybackDevice()); } -void SettingsModel::setPlaybackDevice (const QString &device) { +void SettingsModel::setPlaybackDevice(const QString &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); std::string devId = Utils::appStringToCoreString(device); auto list = CoreModel::getInstance()->getCore()->getExtendedAudioDevices(); - auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) { - return audioItem->getId() == devId; - }); - if(audioDevice != list.cend()){ + auto audioDevice = + find_if(list.cbegin(), list.cend(), + [&](const std::shared_ptr &audioItem) { return audioItem->getId() == devId; }); + if (audioDevice != list.cend()) { CoreModel::getInstance()->getCore()->setPlaybackDevice(devId); CoreModel::getInstance()->getCore()->setOutputAudioDevice(*audioDevice); emit playbackDeviceChanged(device); resetCaptureGraph(); - }else - qWarning() << "Cannot set Playback device. The ID cannot be matched with an existant device : " << device; + } else qWarning() << "Cannot set Playback device. The ID cannot be matched with an existant device : " << device; } // ----------------------------------------------------------------------------- -QString SettingsModel::getRingerDevice () const { +QString SettingsModel::getRingerDevice() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - return Utils::coreStringToAppString( - CoreModel::getInstance()->getCore()->getRingerDevice() - ); + return Utils::coreStringToAppString(CoreModel::getInstance()->getCore()->getRingerDevice()); } -void SettingsModel::setRingerDevice (const QString &device) { +void SettingsModel::setRingerDevice(const QString &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - CoreModel::getInstance()->getCore()->setRingerDevice( - Utils::appStringToCoreString(device) - ); + CoreModel::getInstance()->getCore()->setRingerDevice(Utils::appStringToCoreString(device)); emit ringerDeviceChanged(device); } @@ -310,7 +300,7 @@ void SettingsModel::setRingerDevice (const QString &device) { bool SettingsModel::getVideoEnabled() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - return CoreModel::getInstance()->getCore()->videoEnabled(); + return CoreModel::getInstance()->getCore()->videoEnabled(); } void SettingsModel::setVideoEnabled(const bool enabled) { @@ -323,33 +313,33 @@ void SettingsModel::setVideoEnabled(const bool enabled) { // ----------------------------------------------------------------------------- -bool SettingsModel::getEchoCancellationEnabled () const { +bool SettingsModel::getEchoCancellationEnabled() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return CoreModel::getInstance()->getCore()->echoCancellationEnabled(); } -void SettingsModel::setEchoCancellationEnabled (bool status) { +void SettingsModel::setEchoCancellationEnabled(bool status) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); CoreModel::getInstance()->getCore()->enableEchoCancellation(status); emit echoCancellationEnabledChanged(status); } -void SettingsModel::startEchoCancellerCalibration(){ +void SettingsModel::startEchoCancellerCalibration() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); CoreModel::getInstance()->getCore()->startEchoCancellerCalibration(); } -int SettingsModel::getEchoCancellationCalibration()const { +int SettingsModel::getEchoCancellationCalibration() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return CoreModel::getInstance()->getCore()->getEchoCancellationCalibration(); } -bool SettingsModel::getAutomaticallyRecordCallsEnabled () const { +bool SettingsModel::getAutomaticallyRecordCallsEnabled() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return !!mConfig->getInt(UiSection, "automatically_record_calls", 0); } -void SettingsModel::setAutomaticallyRecordCallsEnabled (bool enabled) { +void SettingsModel::setAutomaticallyRecordCallsEnabled(bool enabled) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mConfig->setInt(UiSection, "automatically_record_calls", enabled); emit automaticallyRecordCallsEnabledChanged(enabled); @@ -359,12 +349,12 @@ void SettingsModel::setAutomaticallyRecordCallsEnabled (bool enabled) { // VFS. // ============================================================================= -bool SettingsModel::getVfsEnabled () const { +bool SettingsModel::getVfsEnabled() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return !!mConfig->getInt(UiSection, "vfs_enabled", 0); } -void SettingsModel::setVfsEnabled (bool enabled) { +void SettingsModel::setVfsEnabled(bool enabled) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mConfig->setInt(UiSection, "vfs_enabled", enabled); emit vfsEnabledChanged(enabled); @@ -374,69 +364,67 @@ void SettingsModel::setVfsEnabled (bool enabled) { // Logs. // ============================================================================= -bool SettingsModel::getLogsEnabled () const { +bool SettingsModel::getLogsEnabled() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return getLogsEnabled(mConfig); } -void SettingsModel::setLogsEnabled (bool status) { +void SettingsModel::setLogsEnabled(bool status) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mConfig->setInt(UiSection, "logs_enabled", status); CoreModel::getInstance()->getLogger()->enable(status); emit logsEnabledChanged(status); } -bool SettingsModel::getFullLogsEnabled () const { +bool SettingsModel::getFullLogsEnabled() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return getFullLogsEnabled(mConfig); } -void SettingsModel::setFullLogsEnabled (bool status) { +void SettingsModel::setFullLogsEnabled(bool status) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mConfig->setInt(UiSection, "full_logs_enabled", status); CoreModel::getInstance()->getLogger()->enableFullLogs(status); emit fullLogsEnabledChanged(status); } -bool SettingsModel::getLogsEnabled (const shared_ptr &config) { +bool SettingsModel::getLogsEnabled(const shared_ptr &config) { mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); return config ? config->getInt(UiSection, "logs_enabled", false) : true; } -bool SettingsModel::getFullLogsEnabled (const shared_ptr &config) { +bool SettingsModel::getFullLogsEnabled(const shared_ptr &config) { mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); return config ? config->getInt(UiSection, "full_logs_enabled", false) : false; } -QString SettingsModel::getLogsFolder () const { +QString SettingsModel::getLogsFolder() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return getLogsFolder(mConfig); } -QString SettingsModel::getLogsFolder (const shared_ptr &config) { +QString SettingsModel::getLogsFolder(const shared_ptr &config) { mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); - return config - ? Utils::coreStringToAppString(config->getString(UiSection, "logs_folder", Utils::appStringToCoreString(Paths::getLogsDirPath()))) - : Paths::getLogsDirPath(); + return config ? Utils::coreStringToAppString(config->getString( + UiSection, "logs_folder", Utils::appStringToCoreString(Paths::getLogsDirPath()))) + : Paths::getLogsDirPath(); } -void SettingsModel::cleanLogs () const { +void SettingsModel::cleanLogs() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); CoreModel::getInstance()->getCore()->resetLogCollection(); } -void SettingsModel::sendLogs () const { +void SettingsModel::sendLogs() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto core = CoreModel::getInstance()->getCore(); qInfo() << QStringLiteral("Send logs to: `%1` from `%2`.") - .arg(Utils::coreStringToAppString(core->getLogCollectionUploadServerUrl())) - .arg(Utils::coreStringToAppString(core->getLogCollectionPath())); + .arg(Utils::coreStringToAppString(core->getLogCollectionUploadServerUrl())) + .arg(Utils::coreStringToAppString(core->getLogCollectionPath())); core->uploadLogCollection(); } -QString SettingsModel::getLogsEmail () const { +QString SettingsModel::getLogsEmail() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - return Utils::coreStringToAppString( - mConfig->getString(UiSection, "logs_email", Constants::DefaultLogsEmail) - ); + return Utils::coreStringToAppString(mConfig->getString(UiSection, "logs_email", Constants::DefaultLogsEmail)); } diff --git a/Linphone/tool/Constants.hpp b/Linphone/tool/Constants.hpp index 074d8e17f..0db40fc30 100644 --- a/Linphone/tool/Constants.hpp +++ b/Linphone/tool/Constants.hpp @@ -64,7 +64,7 @@ public: static constexpr char DownloadUrl[] = "https://www.linphone.org/technical-corner/linphone"; static constexpr char VersionCheckReleaseUrl[] = "https://linphone.org/releases"; static constexpr char VersionCheckNightlyUrl[] = "https://linphone.org/snapshots"; - static constexpr char PasswordRecoveryUrl[] = "https://subscribe.linphone.org/login/email"; + static constexpr char PasswordRecoveryUrl[] = "https://subscribe.linphone.org/recovery/email"; static constexpr char CguUrl[] = "https://www.linphone.org/general-terms"; static constexpr char PrivatePolicyUrl[] = "https://www.linphone.org/privacy-policy"; static constexpr char ContactUrl[] = "https://www.linphone.org/contact"; diff --git a/Linphone/tool/LinphoneEnums.hpp b/Linphone/tool/LinphoneEnums.hpp index 7e1e1b91c..cddb2f198 100644 --- a/Linphone/tool/LinphoneEnums.hpp +++ b/Linphone/tool/LinphoneEnums.hpp @@ -323,6 +323,29 @@ LinphoneEnums::TransportType fromLinphone(const linphone::TransportType &type); QString toString(const LinphoneEnums::TransportType &type); void fromString(const QString &transportType, LinphoneEnums::TransportType *transport); +enum class AccountManagerServicesRequestType { + SendAccountCreationTokenByPush = int(linphone::AccountManagerServicesRequest::Type::SendAccountCreationTokenByPush), + AccountCreationRequestToken = int(linphone::AccountManagerServicesRequest::Type::AccountCreationRequestToken), + AccountCreationTokenFromAccountCreationRequestToken = + int(linphone::AccountManagerServicesRequest::Type::AccountCreationTokenFromAccountCreationRequestToken), + CreateAccountUsingToken = int(linphone::AccountManagerServicesRequest::Type::CreateAccountUsingToken), + SendPhoneNumberLinkingCodeBySms = + int(linphone::AccountManagerServicesRequest::Type::SendPhoneNumberLinkingCodeBySms), + LinkPhoneNumberUsingCode = int(linphone::AccountManagerServicesRequest::Type::LinkPhoneNumberUsingCode), + SendEmailLinkingCodeByEmail = int(linphone::AccountManagerServicesRequest::Type::SendEmailLinkingCodeByEmail), + LinkEmailUsingCode = int(linphone::AccountManagerServicesRequest::Type::LinkEmailUsingCode), + GetDevicesList = int(linphone::AccountManagerServicesRequest::Type::GetDevicesList), + DeleteDevice = int(linphone::AccountManagerServicesRequest::Type::DeleteDevice), + GetCreationTokenAsAdmin = int(linphone::AccountManagerServicesRequest::Type::GetCreationTokenAsAdmin), + GetAccountInfoAsAdmin = int(linphone::AccountManagerServicesRequest::Type::GetAccountInfoAsAdmin), + DeleteAccountAsAdmin = int(linphone::AccountManagerServicesRequest::Type::DeleteAccountAsAdmin) +}; +Q_ENUM_NS(AccountManagerServicesRequestType) + +// linphone::AccountManagerServicesRequest::Type toLinphone(const LinphoneEnums::AccountManagerServicesRequestType +// &type); LinphoneEnums::AccountManagerServicesRequestType fromLinphone(const +// linphone::AccountManagerServicesRequest::Type &type); + enum VideoSourceScreenSharingType { VideoSourceScreenSharingTypeArea = int(linphone::VideoSourceScreenSharingType::Area), VideoSourceScreenSharingTypeDisplay = int(linphone::VideoSourceScreenSharingType::Display), diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index 196602dbd..ce2fff99d 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -374,6 +374,11 @@ bool Utils::copyToClipboard(const QString &text) { return !clipboardText.isEmpty(); } +QString Utils::getClipboardText() { + QClipboard *clipboard = QApplication::clipboard(); + return clipboard->text(); +} + QString Utils::getApplicationProduct() { // Note: Keep '-' as a separator between application name and application type return QString(APPLICATION_NAME "-Desktop").remove(' ') + "/" + QCoreApplication::applicationVersion(); diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index 23feaad70..6280e9727 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -85,6 +85,7 @@ public: Q_INVOKABLE static QStringList generateSecurityLettersArray(int arraySize, int correctIndex, QString correctCode); Q_INVOKABLE static int getRandomIndex(int size); Q_INVOKABLE static bool copyToClipboard(const QString &text); + Q_INVOKABLE static QString getClipboardText(); Q_INVOKABLE static QString toDateString(QDateTime date, const QString &format = ""); Q_INVOKABLE static QString toDateString(QDate date, const QString &format = ""); Q_INVOKABLE static QString toDateDayString(const QDateTime &date); diff --git a/Linphone/view/App/AppWindow.qml b/Linphone/view/App/AppWindow.qml index 5b85a9df2..8ae00c16e 100644 --- a/Linphone/view/App/AppWindow.qml +++ b/Linphone/view/App/AppWindow.qml @@ -38,8 +38,10 @@ ApplicationWindow { infoPopup.open() infoPopup.closePopup.connect(removeFromPopupLayout) } - function showLoadingPopup(text) { + function showLoadingPopup(text, cancelButtonVisible) { + if (cancelButtonVisible == undefined) cancelButtonVisible = false loadingPopup.text = text + loadingPopup.cancelButtonVisible = cancelButtonVisible loadingPopup.open() } function closeLoadingPopup() { diff --git a/Linphone/view/App/Layout/LoginLayout.qml b/Linphone/view/App/Layout/LoginLayout.qml index 1943896af..4701b6844 100644 --- a/Linphone/view/App/Layout/LoginLayout.qml +++ b/Linphone/view/App/Layout/LoginLayout.qml @@ -15,10 +15,12 @@ Rectangle { color: DefaultStyle.grey_0 ColumnLayout { // anchors.leftMargin: 119 * DefaultStyle.dp - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: bottomMountains.top + id: contentLayout + // anchors.top: parent.top + // anchors.left: parent.left + // anchors.right: parent.right + anchors.fill: parent + // anchors.bottom: bottomMountains.top spacing: 0 RowLayout { Layout.fillWidth: true @@ -63,17 +65,16 @@ Rectangle { id: centerLayout Layout.fillHeight: true Layout.fillWidth: true + z: 1 + } + Image { + id: bottomMountains + source: AppIcons.belledonne + fillMode: Image.Stretch + Layout.fillWidth: true + Layout.preferredHeight: 108 * DefaultStyle.dp } } - Image { - id: bottomMountains - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - height: 108 * DefaultStyle.dp - source: AppIcons.belledonne - fillMode: Image.Stretch - } } diff --git a/Linphone/view/App/Main.qml b/Linphone/view/App/Main.qml index 51be45d9b..b3f6f3f86 100644 --- a/Linphone/view/App/Main.qml +++ b/Linphone/view/App/Main.qml @@ -92,8 +92,17 @@ AppWindow { id: registerPage RegisterPage { onReturnToLogin: mainWindowStackView.replace(loginPage) - onRegisterCalled: (countryCode, phoneNumber, email) => { - mainWindowStackView.push(checkingPage, {"phoneNumber": phoneNumber, "email": email}) + onBrowserValidationRequested: mainWindow.showLoadingPopup(qsTr("Veuillez valider le captcha sur la page web"), true) + Connections { + target: RegisterPageCpp + onNewAccountCreationSucceed: (withEmail, address, sipIdentityAddress) => { + mainWindowStackView.push(checkingPage, {"registerWithEmail": withEmail, "address": address, "sipIdentityAddress": sipIdentityAddress}) + } + onRegisterNewAccountFailed: (errorMessage) => { + mainWindow.showInformationPopup(qsTr("Erreur lors de la création"), errorMessage, false) + mainWindow.closeLoadingPopup() + } + onTokenConversionSucceed: mainWindow.closeLoadingPopup() } } } @@ -101,6 +110,20 @@ AppWindow { id: checkingPage RegisterCheckingPage { onReturnToRegister: mainWindowStackView.pop() + onSendCode: (code) => { + RegisterPageCpp.linkNewAccountUsingCode(code, registerWithEmail, sipIdentityAddress) + } + Connections { + target: RegisterPageCpp + onLinkingNewAccountWithCodeSucceed: { + mainWindowStackView.replace(loginPage) + mainWindow.showInformationPopup(qsTr("Compte créé"), qsTr("Le compte a été créé avec succès. Vous pouvez maintenant vous connecter"), true) + } + onLinkingNewAccountWithCodeFailed: (errorMessage) => { + if (errorMessage.length === 0) errorMessage = qsTr("Erreur dans le code de validation") + mainWindow.showInformationPopup(qsTr("Erreur"), errorMessage, false) + } + } } } Component { diff --git a/Linphone/view/Item/DigitInput.qml b/Linphone/view/Item/DigitInput.qml index fa2df97a4..b5722852c 100644 --- a/Linphone/view/Item/DigitInput.qml +++ b/Linphone/view/Item/DigitInput.qml @@ -12,6 +12,7 @@ Control.TextField { height: inputSize horizontalAlignment: TextInput.AlignHCenter verticalAlignment: TextInput.AlignVCenter + overwriteMode: true // just reserve the space for the background placeholderText: "0" diff --git a/Linphone/view/Item/ErrorText.qml b/Linphone/view/Item/ErrorText.qml index e9cc696d1..b32cf99b6 100644 --- a/Linphone/view/Item/ErrorText.qml +++ b/Linphone/view/Item/ErrorText.qml @@ -8,12 +8,6 @@ Text { id: mainItem color: DefaultStyle.danger_500main opacity: 0 - function displayText() { - mainItem.state = "Visible" - } - function hideText() { - mainItem.state = "Invisible" - } font { pixelSize: 12 * DefaultStyle.dp weight: 300 * DefaultStyle.dp @@ -25,7 +19,7 @@ Text { }, State{ name:"Invisible" - PropertyChanges{target: mainItem; opacity: 0.0} + PropertyChanges{target: mainItem; opacity: 0.0; text: ""} } ] transitions: [ @@ -34,7 +28,7 @@ Text { to: "Invisible" NumberAnimation { property: "opacity" - duration: 1000 + duration: 500 } } ] @@ -51,6 +45,8 @@ Text { onTextChanged: { if (mainItem.text.length > 0) { mainItem.state = "Visible" + } else { + mainItem.state = "Invisible" } } } diff --git a/Linphone/view/Item/Form/LoginForm.qml b/Linphone/view/Item/Form/LoginForm.qml index 20e701907..383133d5e 100644 --- a/Linphone/view/Item/Form/LoginForm.qml +++ b/Linphone/view/Item/Form/LoginForm.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls as Control import Linphone - +import ConstantsCpp 1.0 ColumnLayout { id: mainItem @@ -148,7 +148,7 @@ ColumnLayout { weight: 600 * DefaultStyle.dp } } - onClicked: console.debug("[LoginForm]User: forgotten password button clicked") + onClicked: Qt.openUrlExternally(ConstantsCpp.PasswordRecoveryUrl) } } diff --git a/Linphone/view/Item/LoadingPopup.qml b/Linphone/view/Item/LoadingPopup.qml index e24053d7a..527462034 100644 --- a/Linphone/view/Item/LoadingPopup.qml +++ b/Linphone/view/Item/LoadingPopup.qml @@ -6,6 +6,7 @@ import Linphone Popup { id: mainItem property string text + property bool cancelButtonVisible: false modal: true closePolicy: Control.Popup.NoAutoClose anchors.centerIn: parent @@ -15,6 +16,8 @@ Popup { // onAboutToShow: width = contentText.implicitWidth contentItem: ColumnLayout { spacing: 15 * DefaultStyle.dp + // width: childrenRect.width + // height: childrenRect.height BusyIndicator{ Layout.alignment: Qt.AlignHCenter Layout.preferredWidth: 33 * DefaultStyle.dp @@ -28,5 +31,11 @@ Popup { text: mainItem.text font.pixelSize: 14 * DefaultStyle.dp } + Button { + visible: mainItem.cancelButtonVisible + Layout.alignment: Qt.AlignHCenter + text: qsTr("Annuler") + onClicked: mainItem.close() + } } } \ No newline at end of file diff --git a/Linphone/view/Item/PhoneNumberComboBox.qml b/Linphone/view/Item/PhoneNumberComboBox.qml index aa3aefa52..f3443bf19 100644 --- a/Linphone/view/Item/PhoneNumberComboBox.qml +++ b/Linphone/view/Item/PhoneNumberComboBox.qml @@ -7,7 +7,7 @@ import Linphone ColumnLayout { id: mainItem property string label: "" - readonly property string currentText: combobox.model.getAt(combobox.currentIndex) ? combobox.model.getAt(combobox.currentIndex).countryCallingCode : "" + readonly property string currentText: combobox.model.getAt(combobox.currentIndex) ? "+" + combobox.model.getAt(combobox.currentIndex).countryCallingCode : "" property string defaultCallingCode: "" property bool enableBackgroundColors: false @@ -101,7 +101,7 @@ ColumnLayout { id: listPopup y: combobox.height - 1 width: 311 * DefaultStyle.dp - height: 198 * DefaultStyle.dp + height: 250 * DefaultStyle.dp contentItem: ListView { id: listView @@ -109,7 +109,9 @@ ColumnLayout { anchors.fill: parent model: PhoneNumberProxy{} currentIndex: combobox.highlightedIndex >= 0 ? combobox.highlightedIndex : 0 - highlightFollowsCurrentItem: true + keyNavigationEnabled: true + keyNavigationWraps: true + maximumFlickVelocity: 1500 highlight: Rectangle { anchors.left: parent.left anchors.right: parent.right @@ -177,7 +179,7 @@ ColumnLayout { color: DefaultStyle.main2_500main visible: parent.containsMouse } - onPressed: { + onClicked: { combobox.currentIndex = index listPopup.close() } diff --git a/Linphone/view/Item/PhoneNumberInput.qml b/Linphone/view/Item/PhoneNumberInput.qml index afa4227e4..967046f0d 100644 --- a/Linphone/view/Item/PhoneNumberInput.qml +++ b/Linphone/view/Item/PhoneNumberInput.qml @@ -7,13 +7,15 @@ ColumnLayout { id: mainItem property string label: "" - property string errorMessage: "" + property alias errorMessage: errorText.text property string placeholderText : "" property bool mandatory: false + property bool enableErrorText: true property int textInputWidth: width property string initialPhoneNumber readonly property string phoneNumber: textField.text readonly property string countryCode: combobox.currentText + property string defaultCallingCode Text { visible: label.length > 0 @@ -26,52 +28,60 @@ ColumnLayout { } } - Rectangle { - Layout.preferredWidth: mainItem.textInputWidth - Layout.preferredHeight: 49 * DefaultStyle.dp - radius: 63 * DefaultStyle.dp - color: DefaultStyle.grey_100 - border.color: mainItem.errorMessage.length > 0 - ? DefaultStyle.danger_500main - : (textField.hasActiveFocus || combobox.hasActiveFocus) - ? DefaultStyle.main1_500_main - : DefaultStyle.grey_200 - RowLayout { - anchors.fill: parent - PhoneNumberComboBox { - id: combobox - implicitWidth: 110 * DefaultStyle.dp - } - Rectangle { - Layout.preferredWidth: 1 * DefaultStyle.dp - Layout.fillHeight: true - Layout.topMargin: 10 * DefaultStyle.dp - Layout.bottomMargin: 10 * DefaultStyle.dp - color: DefaultStyle.main2_600 - } - TextField { - id: textField - Layout.fillWidth: true - placeholderText: mainItem.placeholderText - background: Item{} - initialText: initialPhoneNumber - validator: RegularExpressionValidator{ regularExpression: /[0-9]+/} + Item { + Layout.preferredWidth: contentBackground.width + Layout.preferredHeight: contentBackground.height + Rectangle { + id: contentBackground + width: mainItem.textInputWidth + height: 49 * DefaultStyle.dp + radius: 63 * DefaultStyle.dp + color: DefaultStyle.grey_100 + border.color: mainItem.errorMessage.length > 0 + ? DefaultStyle.danger_500main + : (textField.hasActiveFocus || combobox.hasActiveFocus) + ? DefaultStyle.main1_500_main + : DefaultStyle.grey_200 + RowLayout { + anchors.fill: parent + PhoneNumberComboBox { + id: combobox + implicitWidth: 110 * DefaultStyle.dp + defaultCallingCode: mainItem.defaultCallingCode + } + Rectangle { + Layout.preferredWidth: 1 * DefaultStyle.dp + Layout.fillHeight: true + Layout.topMargin: 10 * DefaultStyle.dp + Layout.bottomMargin: 10 * DefaultStyle.dp + color: DefaultStyle.main2_600 + } + TextField { + id: textField + Layout.fillWidth: true + placeholderText: mainItem.placeholderText + background: Item{} + initialText: initialPhoneNumber + validator: RegularExpressionValidator{ regularExpression: /[0-9]+/} + } } } - } - - Text { - visible: mainItem.errorMessage.length > 0 - verticalAlignment: Text.AlignVCenter - text: mainItem.errorMessage - color: DefaultStyle.danger_500main - elide: Text.ElideRight - wrapMode: Text.Wrap - font { - pixelSize: 13 * DefaultStyle.dp - family: DefaultStyle.defaultFont - bold: true + ErrorText { + id: errorText + anchors.top: contentBackground.bottom + // visible: mainItem.enableErrorText + text: mainItem.errorMessage + color: DefaultStyle.danger_500main + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + wrapMode: Text.Wrap + font { + pixelSize: 13 * DefaultStyle.dp + family: DefaultStyle.defaultFont + bold: true + } + Layout.preferredWidth: mainItem.textInputWidth + // Layout.preferredWidth: implicitWidth } - Layout.preferredWidth: mainItem.textInputWidth } } diff --git a/Linphone/view/Item/TextField.qml b/Linphone/view/Item/TextField.qml index dc195637a..903f558c0 100644 --- a/Linphone/view/Item/TextField.qml +++ b/Linphone/view/Item/TextField.qml @@ -86,7 +86,7 @@ Control.TextField { } } Keys.onPressed: (event) => { - if (event.jey == Qt.Key_Control) mainItem.controlIsDown = true + if (event.key == Qt.Key_Control) mainItem.controlIsDown = true if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { enterPressed() if (mainItem.controlIsDown) { diff --git a/Linphone/view/Layout/FormItemLayout.qml b/Linphone/view/Layout/FormItemLayout.qml index cd7b453b7..6a3933ff4 100644 --- a/Linphone/view/Layout/FormItemLayout.qml +++ b/Linphone/view/Layout/FormItemLayout.qml @@ -10,7 +10,7 @@ ColumnLayout { property string label: "" property bool mandatory: false - property string errorMessage: "" + property alias errorMessage: errorText.text property bool enableErrorText: false property bool errorTextVisible: errorText.opacity > 0 spacing: 5 * DefaultStyle.dp @@ -32,16 +32,19 @@ ColumnLayout { } Item { - id: contentItem - Layout.preferredHeight: childrenRect.height - Layout.preferredWidth: childrenRect.width + Layout.preferredHeight: contentItem.height + Layout.preferredWidth: contentItem.width + Item { + id: contentItem + height: childrenRect.height + width: childrenRect.width + } + ErrorText { + id: errorText + anchors.top: contentItem.bottom + color: DefaultStyle.danger_500main + Layout.preferredWidth: implicitWidth + } } - ErrorText { - id: errorText - visible: mainItem.enableErrorText - text: mainItem.errorMessage - color: DefaultStyle.main2_600 - Layout.preferredWidth: implicitWidth - } -} +} \ No newline at end of file diff --git a/Linphone/view/Page/Login/LoginPage.qml b/Linphone/view/Page/Login/LoginPage.qml index 5aea98a53..e4c3f8b9c 100644 --- a/Linphone/view/Page/Login/LoginPage.qml +++ b/Linphone/view/Page/Login/LoginPage.qml @@ -104,6 +104,7 @@ LoginLayout { } }, Image { + z: -1 anchors.top: parent.top anchors.right: parent.right anchors.topMargin: 129 * DefaultStyle.dp diff --git a/Linphone/view/Page/Login/RegisterCheckingPage.qml b/Linphone/view/Page/Login/RegisterCheckingPage.qml index eca089874..e0dd6d317 100644 --- a/Linphone/view/Page/Login/RegisterCheckingPage.qml +++ b/Linphone/view/Page/Login/RegisterCheckingPage.qml @@ -6,9 +6,11 @@ import Linphone LoginLayout { id: mainItem signal returnToRegister() - property string phoneNumber - property string email - + signal sendCode(string code) + property bool registerWithEmail + property string address + property string sipIdentityAddress + property string code titleContent: [ RowLayout { spacing: 21 * DefaultStyle.dp @@ -34,7 +36,7 @@ LoginLayout { Text { wrapMode: Text.NoWrap text: { - var completeString = (mainItem.email.length > 0) ? qsTr("email") : qsTr("numéro") + var completeString = mainItem.registerWithEmail ? qsTr("email") : qsTr("numéro") text = qsTr("Inscription | Confirmer votre ") + completeString } font { @@ -64,7 +66,7 @@ LoginLayout { } color: DefaultStyle.main2_700 text: { - var completeString = (mainItem.email.length > 0) ? ("email \"" + mainItem.email + "\"") : ("phone number \"" + mainItem.phoneNumber + "\"") + var completeString = mainItem.registerWithEmail ? ("email \"") : ("phone number \"") + address + "\"" text = "We have sent a verification code on your " + completeString + "
Please enter the verification code below:" } } @@ -78,10 +80,12 @@ LoginLayout { Layout.preferredHeight: height onTextEdited: { if (text.length > 0 ) { + mainItem.code = mainItem.code.slice(0, index) + text + mainItem.code.slice(index) if (index < 3) nextItemInFocusChain(true).forceActiveFocus() else { - // TODO : validate() + mainItem.sendCode(mainItem.code) + mainItem.code = "" } } else { if (index > 0) diff --git a/Linphone/view/Page/Login/RegisterPage.qml b/Linphone/view/Page/Login/RegisterPage.qml index 41176d757..9785cdb8f 100644 --- a/Linphone/view/Page/Login/RegisterPage.qml +++ b/Linphone/view/Page/Login/RegisterPage.qml @@ -2,15 +2,27 @@ import QtQuick 2.15 import QtQuick.Layouts 1.3 import QtQuick.Controls as Control import Linphone +import UtilsCpp 1.0 +import ConstantsCpp 1.0 LoginLayout { id: mainItem signal returnToLogin() - signal registerCalled(countryCode: string, phoneNumber: string, email: string) + signal browserValidationRequested() readonly property string countryCode: phoneNumberInput.countryCode readonly property string phoneNumber: phoneNumberInput.phoneNumber readonly property string email: emailInput.text + Connections { + target: RegisterPageCpp + onErrorInField: (field, errorMessage) => { + if (field == "username") usernameItem.errorMessage = errorMessage + else if (field == "password") pwdItem.errorMessage = errorMessage + else if (field == "phone") phoneNumberInput.errorMessage = errorMessage + else if (field == "email") emailItem.errorMessage = errorMessage + } + } + titleContent: [ RowLayout { spacing: 21 * DefaultStyle.dp @@ -80,11 +92,13 @@ LoginLayout { RowLayout { spacing: 16 * DefaultStyle.dp FormItemLayout { + id: usernameItem label: qsTr("Username") mandatory: true contentItem: TextField { id: usernameInput Layout.preferredWidth: 346 * DefaultStyle.dp + backgroundBorderColor: usernameItem.errorMessage.length > 0 ? DefaultStyle.danger_500main : DefaultStyle.grey_200 } } RowLayout { @@ -109,17 +123,22 @@ LoginLayout { Layout.fillWidth: true PhoneNumberInput { id: phoneNumberInput - label: qsTr("Phone number") + property string completePhoneNumber: countryCode + phoneNumber + label: qsTr("Numéro de téléphone") mandatory: true placeholderText: "Phone number" + defaultCallingCode: "33" Layout.preferredWidth: 346 * DefaultStyle.dp } FormItemLayout { + id: emailItem label: qsTr("Email") mandatory: true + enableErrorText: true contentItem: TextField { id: emailInput Layout.preferredWidth: 346 * DefaultStyle.dp + backgroundBorderColor: emailItem.errorMessage.length > 0 ? DefaultStyle.danger_500main : DefaultStyle.grey_200 } } } @@ -128,130 +147,147 @@ LoginLayout { ColumnLayout { spacing: 5 * DefaultStyle.dp FormItemLayout { - label: qsTr("Password") + id: passwordItem + label: qsTr("Mot de passe") mandatory: true + enableErrorText: true contentItem: TextField { id: pwdInput hidden: true Layout.preferredWidth: 346 * DefaultStyle.dp - } - } - Text { - text: qsTr("The password must contain 6 characters minimum") - font { - pixelSize: 12 * DefaultStyle.dp - weight: 300 * DefaultStyle.dp + backgroundBorderColor: passwordItem.errorMessage.length > 0 ? DefaultStyle.danger_500main : DefaultStyle.grey_200 } } } ColumnLayout { spacing: 5 * DefaultStyle.dp FormItemLayout { - label: qsTr("Confirm password") + label: qsTr("Confirmation mot de passe") mandatory: true + enableErrorText: true contentItem: TextField { id: confirmPwdInput hidden: true Layout.preferredWidth: 346 * DefaultStyle.dp - } - } - Text { - text: qsTr("The password must contain 6 characters minimum") - font { - pixelSize: 12 * DefaultStyle.dp - weight: 300 * DefaultStyle.dp + backgroundBorderColor: passwordItem.errorMessage.length > 0 ? DefaultStyle.danger_500main : DefaultStyle.grey_200 } } } } } - ColumnLayout { - spacing: 18 * DefaultStyle.dp + // ColumnLayout { + // spacing: 18 * DefaultStyle.dp + // RowLayout { + // spacing: 10 * DefaultStyle.dp + // CheckBox { + // id: subscribeToNewsletterCheckBox + // } + // Text { + // text: qsTr("Je souhaite souscrire à la newletter Linphone.") + // font { + // pixelSize: 14 * DefaultStyle.dp + // weight: 400 * DefaultStyle.dp + // } + // MouseArea { + // anchors.fill: parent + // onClicked: subscribeToNewsletterCheckBox.toggle() + // } + // } + // } + RowLayout { + spacing: 10 * DefaultStyle.dp + CheckBox { + id: termsCheckBox + } RowLayout { - spacing: 10 * DefaultStyle.dp - CheckBox { + spacing: 0 + Layout.fillWidth: true + Text { + text: qsTr("J'accepte les ") + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp + } + MouseArea { + anchors.fill: parent + onClicked: termsCheckBox.toggle() + } } Text { - text: qsTr("I would like to suscribe to the newsletter") + font { + underline: true + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp + } + text: qsTr("conditions d’utilisation") + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + onClicked: Qt.openUrlExternally(ConstantsCpp.CguUrl) + } + } + Text { + text: qsTr(" et la ") font { pixelSize: 14 * DefaultStyle.dp weight: 400 * DefaultStyle.dp } } - } - RowLayout { - spacing: 10 * DefaultStyle.dp - CheckBox { - id: termsCheckBox - } - RowLayout { - spacing: 0 - Layout.fillWidth: true - Text { - // Layout.preferredWidth: 450 * DefaultStyle.dp - text: qsTr("I accept the Terms and Conditions: ") - font { - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp - } + Text { + font { + underline: true + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp } - Text { - // Layout.preferredWidth: 450 * DefaultStyle.dp - font { - underline: true - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp - } - text: qsTr("Read the Terms and Conditions.") - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor - onClicked: console.log("TODO : display terms and conditions") - } - } - Text { - // Layout.preferredWidth: 450 * DefaultStyle.dp - text: qsTr("I accept the Privacy policy: ") - font { - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp - } - } - Text { - // Layout.preferredWidth: 450 * DefaultStyle.dp - font { - underline: true - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp - } - text: qsTr("Read the Privacy policy.") - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor - onClicked: console.log("TODO : display privacy policy") - } + text: qsTr("politique de confidentialité.") + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + onClicked: Qt.openUrlExternally(ConstantsCpp.PrivatePolicyUrl) } } } } + // } Button { - // enabled: termsCheckBox.checked && usernameInput.text.length != 0 && pwdInput.text.length != 0 && confirmPwdInput.text.length != 0 - // && (phoneNumberInput.phoneNumber.length != 0 || emailInput.text.length != 0) + enabled: termsCheckBox.checked leftPadding: 20 * DefaultStyle.dp rightPadding: 20 * DefaultStyle.dp topPadding: 11 * DefaultStyle.dp bottomPadding: 11 * DefaultStyle.dp - text: qsTr("Register") + text: qsTr("Créer") onClicked:{ - console.log("[RegisterPage] User: Call register with phone number", phoneNumberInput.phoneNumber) - mainItem.registerCalled(phoneNumberInput.countryCode, phoneNumberInput.phoneNumber, emailInput.text) + if (usernameInput.text.length === 0) { + console.log("ERROR username") + usernameItem.errorMessage = qsTr("Veuillez entrer un nom d'utilisateur") + } else if (pwdInput.text.length === 0) { + console.log("ERROR password") + passwordItem.errorMessage = qsTr("Veuillez entrer un mot de passe") + } else if (pwdInput.text != confirmPwdInput.text) { + console.log("ERROR confirm pwd") + passwordItem.errorMessage = qsTr("Les mots de passe sont différents") + } else if (bar.currentIndex === 0 && phoneNumberInput.phoneNumber.length === 0) { + console.log("ERROR phone number") + phoneNumberInput.errorMessage = qsTr("Veuillez entrer un numéro de téléphone") + } else if (bar.currentIndex === 1 && emailInput.text.length === 0) { + console.log("ERROR email") + emailItem.errorMessage = qsTr("Veuillez entrer un email") + } else { + console.log("[RegisterPage] User: Call register") + mainItem.browserValidationRequested() + if (bar.currentIndex === 0) + RegisterPageCpp.registerNewAccount(usernameInput.text, pwdInput.text, "", phoneNumberInput.completePhoneNumber) + else + RegisterPageCpp.registerNewAccount(usernameInput.text, pwdInput.text, emailInput.text, "") + } } } } }, Image { + z: -1 anchors.top: parent.top anchors.right: parent.right anchors.topMargin: 129 * DefaultStyle.dp diff --git a/Linphone/view/Page/Login/SIPLoginPage.qml b/Linphone/view/Page/Login/SIPLoginPage.qml index 68075f5ee..c0ceb122f 100644 --- a/Linphone/view/Page/Login/SIPLoginPage.qml +++ b/Linphone/view/Page/Login/SIPLoginPage.qml @@ -229,6 +229,7 @@ LoginLayout { clip: true }, Image { + z: -1 anchors.top: parent.top anchors.right: parent.right anchors.topMargin: 129 * DefaultStyle.dp