From a710558c6bc726de2bca8e38b6aab34a35eb98ba Mon Sep 17 00:00:00 2001 From: Stefan Date: Mon, 4 Jul 2022 23:14:13 +0200 Subject: [PATCH] chore(CPP): foundation for user onboarding Contains minimal account creation and login Considerations: - migrated status-go wrapper and login code from the fix/cpp-structure (241eec) - Minimal refactoring and changes at the moment. Expect further refactoring follow up to reach the desired state. - Fix missing keychain initialization - Fix accounts DB initialization call done by startup -> Controller.openedAccounts -> status-go.OpenAccounts calls - Small refactoring and todos for other steps - fix SignalsManager - fix async access to dereferenced status-go memory from SignalsManager - fix SignalsManager not starting when registering - finish dev end to end test for create account and login - small improvements and added TODOs for future work - add onboarding test helpers and start messaging test - Refactoring towards Login UI integration Closes: #5909 Closes: #6028 --- .gitignore | 6 - CMakeLists.txt | 1 + app/CMakeLists.txt | 18 +- app/qml/CMakeLists.txt | 15 - app/qml/Status/Application/CMakeLists.txt | 12 - .../Application/Navigation/CMakeLists.txt | 3 +- app/src/main.cpp | 2 +- ...e.linux-cpp => Jenkinsfile.linux-cpp.todo} | 5 +- ci/cpp/Dockerfile-linux | 2 +- conanfile.py | 2 +- libs/ApplicationCore/CMakeLists.txt | 54 +++ .../src/ApplicationCore/Conversions.cpp | 15 + .../src/ApplicationCore/Conversions.h | 12 + .../src/ApplicationCore/UserConfiguration.cpp | 47 ++ .../src/ApplicationCore/UserConfiguration.h | 34 ++ libs/Assets/CMakeLists.txt | 9 +- libs/CMakeLists.txt | 10 +- libs/Core/CMakeLists.txt | 40 -- libs/Core/res/CMakeLists.txt | 4 - libs/Core/src/Core/CMakeLists.txt | 5 - libs/Helpers/CMakeLists.txt | 29 +- libs/Helpers/src/CMakeLists.txt | 8 - libs/Helpers/src/Helpers/CMakeLists.txt | 7 - libs/Helpers/src/Helpers/Singleton.h | 27 ++ libs/Helpers/src/Helpers/helpers.h | 2 +- libs/Helpers/src/Helpers/logs.cpp | 2 +- libs/Onboarding/CMakeLists.txt | 42 +- .../Status/Onboarding/ConfirmPasswordPage.qml | 85 ++++ .../Status/Onboarding/CreatePasswordPage.qml | 68 +++ .../qml/Status/Onboarding/LoginView.qml | 27 ++ .../qml/Status/Onboarding/OnboardingView.qml | 60 ++- .../Onboarding/SetUserNameAndPicturePage.qml | 59 +++ .../Status/Onboarding/SetupNewProfileView.qml | 86 ++++ .../qml/Status/Onboarding/TempTextInput.qml | 18 + .../qml/Status/Onboarding/WelcomeView.qml | 86 ++++ .../Onboarding/base/OnboardingPageBase.qml | 40 ++ .../base/SetupNewProfilePageBase.qml | 21 + libs/{Core => Onboarding}/src/CMakeLists.txt | 7 +- .../src/Onboarding/Accounts/AccountDto.h | 55 +++ .../Onboarding/Accounts/AccountsService.cpp | 419 ++++++++++++++++++ .../src/Onboarding/Accounts/AccountsService.h | 95 ++++ .../Accounts/AccountsServiceInterface.h | 51 +++ .../Onboarding/Accounts/GeneratedAccountDto.h | 141 ++++++ libs/Onboarding/src/Onboarding/CMakeLists.txt | 26 ++ .../src/Onboarding/Common/Constants.h | 48 ++ libs/Onboarding/src/Onboarding/Common/Json.h | 32 ++ .../src/Onboarding/Common/SigningPhrases.h | 56 +++ .../src/Onboarding/NewAccountController.cpp | 138 ++++++ .../src/Onboarding/NewAccountController.h | 84 ++++ .../src/Onboarding/OnboardingController.cpp | 77 ++++ .../src/Onboarding/OnboardingController.h | 75 ++++ .../src/Onboarding/OnboardingModule.cpp | 65 +++ .../src/Onboarding/OnboardingModule.h | 62 +++ libs/Onboarding/src/Onboarding/TODO.md | 9 + .../Onboarding/src/Onboarding/UserAccount.cpp | 37 ++ libs/Onboarding/src/Onboarding/UserAccount.h | 38 ++ .../src/Onboarding/UserAccountsModel.cpp | 44 ++ .../src/Onboarding/UserAccountsModel.h | 36 ++ libs/Onboarding/tests/CMakeLists.txt | 54 +++ .../OnboardingTestHelpers/CMakeLists.txt | 50 +++ .../tests/OnboardingTestHelpers/Constants.h | 15 + .../ScopedTestAccount.cpp | 119 +++++ .../OnboardingTestHelpers/ScopedTestAccount.h | 52 +++ libs/Onboarding/tests/ServiceMock.h | 37 ++ .../Onboarding/tests/qml_tests/CMakeLists.txt | 33 ++ libs/Onboarding/tests/qml_tests/main.cpp | 3 + .../qml_tests/tst_OnboardingController.qml | 86 ++++ libs/Onboarding/tests/test_AccountService.cpp | 60 +++ .../tests/test_OnboardingController.cpp | 55 +++ .../tests/test_OnboardingModule.cpp | 170 +++++++ libs/StatusGoQt/CMakeLists.txt | 27 +- .../src/StatusGo/Accounts}/Accounts.cpp | 169 +++++-- .../src/StatusGo/Accounts}/Accounts.h | 13 +- libs/StatusGoQt/src/StatusGo/CMakeLists.txt | 22 + libs/StatusGoQt/src/StatusGo/General.cpp | 37 ++ libs/StatusGoQt/src/StatusGo/General.h | 12 + .../src/StatusGo/Messenger/Service.cpp | 22 + .../src/StatusGo/Messenger/Service.h | 12 + .../src/StatusGo/SignalsManager.cpp | 138 ++++++ libs/StatusGoQt/src/StatusGo/SignalsManager.h | 70 +++ libs/StatusGoQt/src/StatusGo/Types.h | 43 ++ libs/StatusGoQt/src/StatusGo/Utils.cpp | 48 ++ libs/StatusGoQt/src/StatusGo/Utils.h | 116 +++++ libs/StatusQ/CMakeLists.txt | 3 +- .../Navigation/NavigationBarButton.qml | 2 +- libs/StatusQ/qml/Status/Core/Theme/Utils.qml | 2 +- libs/StatusQ/tests/CMakeLists.txt | 5 +- libs/StatusQ/tests/TestHelpers/CMakeLists.txt | 5 +- .../tests/TestHelpers/IOTestHelpers.cpp | 14 +- .../StatusQ/tests/TestHelpers/IOTestHelpers.h | 4 +- .../tests/TestHelpers/MonitorQtOutput.h | 2 + resources/CMakeLists.txt | 31 ++ src-cpp-structure/CMakeLists.txt | 27 -- src-cpp-structure/CMakeSettings.json | 28 -- src-cpp-structure/cmake/app-linux.cmake | 25 -- src-cpp-structure/cmake/app-mac.cmake | 31 -- src-cpp-structure/cmake/app-win.cmake | 25 -- src-cpp-structure/cmake/conan.cmake | 27 -- src-cpp-structure/cmake/project-config.cmake | 19 - src-cpp-structure/cmake/services-linux.cmake | 10 - src-cpp-structure/cmake/services-mac.cmake | 22 - src-cpp-structure/cmake/services-win.cmake | 10 - src-cpp-structure/cmake/translation.cmake | 36 -- src-cpp-structure/conan-debug-profile | 4 - src-cpp-structure/conan-release-profile | 4 - src-cpp-structure/conanfile.py | 10 - .../projects/App/Boot/AppController.cpp | 136 ------ .../projects/App/Boot/AppController.h | 12 - .../projects/App/Boot/AppWindow.cpp | 57 --- .../projects/App/Boot/AppWindow.h | 50 --- .../projects/App/Boot/AppWindow.mm | 37 -- src-cpp-structure/projects/App/Boot/DI.h | 18 - src-cpp-structure/projects/App/Boot/main.cpp | 6 - src-cpp-structure/projects/App/CMakeLists.txt | 40 -- .../projects/App/Common/Constants.h | 14 - src-cpp-structure/projects/App/Common/Utils.h | 67 --- .../projects/App/Core/Engine.cpp | 60 --- src-cpp-structure/projects/App/Core/Engine.h | 25 -- .../projects/App/Core/GlobalEvents.cpp | 18 - .../projects/App/Core/GlobalEvents.h | 26 -- .../projects/App/Core/SignalsManager.cpp | 96 ---- .../projects/App/Core/SignalsManager.h | 36 -- .../projects/App/Core/SingleInstance.cpp | 64 --- .../projects/App/Core/SingleInstance.h | 31 -- .../App/Core/StatusSyntaxHighlighter.cpp | 72 --- .../App/Core/StatusSyntaxHighlighter.h | 53 --- .../App/Global/LocalAccountSettings.cpp | 47 -- .../App/Global/LocalAccountSettings.h | 45 -- .../projects/App/Global/LocalAppSettings.cpp | 50 --- .../projects/App/Global/LocalAppSettings.h | 42 -- .../projects/App/Global/SettingsProperties.h | 45 -- .../projects/App/Modules/Module.cpp | 26 -- .../projects/App/Modules/Module.h | 27 -- .../projects/App/Modules/ModuleBuilder.cpp | 15 - .../projects/App/Modules/ModuleBuilder.h | 21 - .../projects/App/Modules/ModuleInterface.h | 12 - .../App/Modules/SharedModels/SectionItem.cpp | 151 ------- .../App/Modules/SharedModels/SectionItem.h | 113 ----- .../App/Modules/SharedModels/SectionModel.cpp | 123 ----- .../App/Modules/SharedModels/SectionModel.h | 55 --- .../App/Modules/Startup/Controller.cpp | 67 --- .../projects/App/Modules/Startup/Controller.h | 31 -- .../App/Modules/Startup/ControllerInterface.h | 22 - .../App/Modules/Startup/Login/Controller.cpp | 98 ---- .../App/Modules/Startup/Login/Controller.h | 42 -- .../Startup/Login/ControllerInterface.h | 28 -- .../App/Modules/Startup/Login/Item.cpp | 34 -- .../projects/App/Modules/Startup/Login/Item.h | 26 -- .../App/Modules/Startup/Login/Model.cpp | 73 --- .../App/Modules/Startup/Login/Model.h | 31 -- .../App/Modules/Startup/Login/Module.cpp | 96 ---- .../App/Modules/Startup/Login/Module.h | 44 -- .../Modules/Startup/Login/ModuleBuilder.cpp | 27 -- .../App/Modules/Startup/Login/ModuleBuilder.h | 24 - .../Modules/Startup/Login/ModuleInterface.h | 19 - .../Modules/Startup/Login/SelectedAccount.cpp | 38 -- .../Modules/Startup/Login/SelectedAccount.h | 33 -- .../App/Modules/Startup/Login/View.cpp | 77 ---- .../projects/App/Modules/Startup/Login/View.h | 47 -- .../App/Modules/Startup/Login/ViewInterface.h | 34 -- .../projects/App/Modules/Startup/Module.cpp | 93 ---- .../projects/App/Modules/Startup/Module.h | 63 --- .../App/Modules/Startup/ModuleBuilder.cpp | 30 -- .../App/Modules/Startup/ModuleBuilder.h | 27 -- .../App/Modules/Startup/ModuleInterface.h | 21 - .../Modules/Startup/Onboarding/Controller.cpp | 72 --- .../Modules/Startup/Onboarding/Controller.h | 33 -- .../Startup/Onboarding/ControllerInterface.h | 30 -- .../App/Modules/Startup/Onboarding/Item.cpp | 38 -- .../App/Modules/Startup/Onboarding/Item.h | 26 -- .../App/Modules/Startup/Onboarding/Model.cpp | 63 --- .../App/Modules/Startup/Onboarding/Model.h | 30 -- .../App/Modules/Startup/Onboarding/Module.cpp | 83 ---- .../App/Modules/Startup/Onboarding/Module.h | 46 -- .../Startup/Onboarding/ModuleBuilder.cpp | 25 -- .../Startup/Onboarding/ModuleBuilder.h | 21 - .../Startup/Onboarding/ModuleInterface.h | 19 - .../App/Modules/Startup/Onboarding/View.cpp | 90 ---- .../App/Modules/Startup/Onboarding/View.h | 49 -- .../Startup/Onboarding/ViewInterface.h | 35 -- .../projects/App/Modules/Startup/View.cpp | 52 --- .../projects/App/Modules/Startup/View.h | 37 -- .../App/Modules/Startup/ViewInterface.h | 32 -- .../projects/Backend/CMakeLists.txt | 27 -- .../Backend/include/StatusBackend/Types.h | 37 -- .../Backend/include/StatusBackend/Utils.h | 125 ------ .../include/StatusBackend/WalletAccounts.h | 24 - .../projects/Backend/src/WalletAccounts.cpp | 106 ----- src-cpp-structure/projects/CMakeLists.txt | 6 - .../projects/Services/CMakeLists.txt | 37 -- .../StatusServices/Accounts/AccountDto.h | 86 ---- .../Accounts/GeneratedAccountDto.h | 116 ----- .../include/StatusServices/Accounts/Service.h | 74 ---- .../Accounts/ServiceInterface.h | 42 -- .../StatusServices/Accounts/ServiceMock.h | 29 -- .../include/StatusServices/AccountsService | 5 - .../include/StatusServices/Common/Constants.h | 45 -- .../include/StatusServices/Common/Json.h | 29 -- .../include/StatusServices/Common/Logger.h | 45 -- .../StatusServices/Common/SigningPhrases.h | 53 --- .../include/StatusServices/CommonService | 6 - .../include/StatusServices/Keychain/Service.h | 30 -- .../Keychain/ServiceInterface.h | 32 -- .../include/StatusServices/KeychainService | 3 - .../StatusServices/WalletAccounts/Service.h | 35 -- .../WalletAccounts/ServiceInterface.h | 31 -- .../WalletAccounts/WalletAccountDto.h | 57 --- .../StatusServices/WalletAccountsService | 4 - .../Services/src/Accounts/Service.cpp | 403 ----------------- .../projects/Services/src/Keychain/Keychain.h | 37 -- .../Services/src/Keychain/Keychain.mm | 162 ------- .../Services/src/Keychain/KeychainManager.cpp | 57 --- .../Services/src/Keychain/KeychainManager.h | 95 ---- .../Services/src/Keychain/KeychainManager.mm | 74 ---- .../src/Keychain/LocalAuthentication.h | 30 -- .../src/Keychain/LocalAuthentication.mm | 152 ------- .../Services/src/Keychain/Service.cpp | 47 -- .../Services/src/WalletAccounts/Service.cpp | 108 ----- .../projects/ServicesTest/CMakeLists.txt | 32 -- .../ServicesTest/src/AccountsService.cpp | 75 ---- .../projects/ServicesTest/src/Constants.h | 11 - .../src/StatusTestEnvironment.cpp | 24 - .../ServicesTest/src/StatusTestEnvironment.h | 21 - .../projects/ServicesTest/src/Utils.h | 59 --- .../projects/ServicesTest/src/main.cpp | 21 - test/CMakeLists.txt | 1 + test/libs/CMakeLists.txt | 3 + test/libs/StatusGoQt/CMakeLists.txt | 39 ++ test/libs/StatusGoQt/test_accounts.cpp | 34 ++ test/libs/StatusGoQt/test_messaging.cpp | 42 ++ ui/generate-rcc.go | 4 - ui/main.qml | 5 +- vendor/conan-configs/linux.ini | 2 - 233 files changed, 3987 insertions(+), 6598 deletions(-) delete mode 100644 app/qml/CMakeLists.txt delete mode 100644 app/qml/Status/Application/CMakeLists.txt rename ci/{Jenkinsfile.linux-cpp => Jenkinsfile.linux-cpp.todo} (85%) create mode 100644 libs/ApplicationCore/CMakeLists.txt create mode 100644 libs/ApplicationCore/src/ApplicationCore/Conversions.cpp create mode 100644 libs/ApplicationCore/src/ApplicationCore/Conversions.h create mode 100644 libs/ApplicationCore/src/ApplicationCore/UserConfiguration.cpp create mode 100644 libs/ApplicationCore/src/ApplicationCore/UserConfiguration.h delete mode 100644 libs/Core/CMakeLists.txt delete mode 100644 libs/Core/res/CMakeLists.txt delete mode 100644 libs/Core/src/Core/CMakeLists.txt delete mode 100644 libs/Helpers/src/CMakeLists.txt delete mode 100644 libs/Helpers/src/Helpers/CMakeLists.txt create mode 100644 libs/Helpers/src/Helpers/Singleton.h create mode 100644 libs/Onboarding/qml/Status/Onboarding/ConfirmPasswordPage.qml create mode 100644 libs/Onboarding/qml/Status/Onboarding/CreatePasswordPage.qml create mode 100644 libs/Onboarding/qml/Status/Onboarding/LoginView.qml create mode 100644 libs/Onboarding/qml/Status/Onboarding/SetUserNameAndPicturePage.qml create mode 100644 libs/Onboarding/qml/Status/Onboarding/SetupNewProfileView.qml create mode 100644 libs/Onboarding/qml/Status/Onboarding/TempTextInput.qml create mode 100644 libs/Onboarding/qml/Status/Onboarding/WelcomeView.qml create mode 100644 libs/Onboarding/qml/Status/Onboarding/base/OnboardingPageBase.qml create mode 100644 libs/Onboarding/qml/Status/Onboarding/base/SetupNewProfilePageBase.qml rename libs/{Core => Onboarding}/src/CMakeLists.txt (67%) create mode 100644 libs/Onboarding/src/Onboarding/Accounts/AccountDto.h create mode 100644 libs/Onboarding/src/Onboarding/Accounts/AccountsService.cpp create mode 100644 libs/Onboarding/src/Onboarding/Accounts/AccountsService.h create mode 100644 libs/Onboarding/src/Onboarding/Accounts/AccountsServiceInterface.h create mode 100644 libs/Onboarding/src/Onboarding/Accounts/GeneratedAccountDto.h create mode 100644 libs/Onboarding/src/Onboarding/CMakeLists.txt create mode 100644 libs/Onboarding/src/Onboarding/Common/Constants.h create mode 100644 libs/Onboarding/src/Onboarding/Common/Json.h create mode 100644 libs/Onboarding/src/Onboarding/Common/SigningPhrases.h create mode 100644 libs/Onboarding/src/Onboarding/NewAccountController.cpp create mode 100644 libs/Onboarding/src/Onboarding/NewAccountController.h create mode 100644 libs/Onboarding/src/Onboarding/OnboardingController.cpp create mode 100644 libs/Onboarding/src/Onboarding/OnboardingController.h create mode 100644 libs/Onboarding/src/Onboarding/OnboardingModule.cpp create mode 100644 libs/Onboarding/src/Onboarding/OnboardingModule.h create mode 100644 libs/Onboarding/src/Onboarding/TODO.md create mode 100644 libs/Onboarding/src/Onboarding/UserAccount.cpp create mode 100644 libs/Onboarding/src/Onboarding/UserAccount.h create mode 100644 libs/Onboarding/src/Onboarding/UserAccountsModel.cpp create mode 100644 libs/Onboarding/src/Onboarding/UserAccountsModel.h create mode 100644 libs/Onboarding/tests/CMakeLists.txt create mode 100644 libs/Onboarding/tests/OnboardingTestHelpers/CMakeLists.txt create mode 100644 libs/Onboarding/tests/OnboardingTestHelpers/Constants.h create mode 100644 libs/Onboarding/tests/OnboardingTestHelpers/ScopedTestAccount.cpp create mode 100644 libs/Onboarding/tests/OnboardingTestHelpers/ScopedTestAccount.h create mode 100644 libs/Onboarding/tests/ServiceMock.h create mode 100644 libs/Onboarding/tests/qml_tests/CMakeLists.txt create mode 100644 libs/Onboarding/tests/qml_tests/main.cpp create mode 100644 libs/Onboarding/tests/qml_tests/tst_OnboardingController.qml create mode 100644 libs/Onboarding/tests/test_AccountService.cpp create mode 100644 libs/Onboarding/tests/test_OnboardingController.cpp create mode 100644 libs/Onboarding/tests/test_OnboardingModule.cpp rename {src-cpp-structure/projects/Backend/src => libs/StatusGoQt/src/StatusGo/Accounts}/Accounts.cpp (51%) rename {src-cpp-structure/projects/Backend/include/StatusBackend => libs/StatusGoQt/src/StatusGo/Accounts}/Accounts.h (55%) create mode 100644 libs/StatusGoQt/src/StatusGo/CMakeLists.txt create mode 100644 libs/StatusGoQt/src/StatusGo/General.cpp create mode 100644 libs/StatusGoQt/src/StatusGo/General.h create mode 100644 libs/StatusGoQt/src/StatusGo/Messenger/Service.cpp create mode 100644 libs/StatusGoQt/src/StatusGo/Messenger/Service.h create mode 100644 libs/StatusGoQt/src/StatusGo/SignalsManager.cpp create mode 100644 libs/StatusGoQt/src/StatusGo/SignalsManager.h create mode 100644 libs/StatusGoQt/src/StatusGo/Types.h create mode 100644 libs/StatusGoQt/src/StatusGo/Utils.cpp create mode 100644 libs/StatusGoQt/src/StatusGo/Utils.h create mode 100644 resources/CMakeLists.txt delete mode 100644 src-cpp-structure/CMakeLists.txt delete mode 100644 src-cpp-structure/CMakeSettings.json delete mode 100644 src-cpp-structure/cmake/app-linux.cmake delete mode 100644 src-cpp-structure/cmake/app-mac.cmake delete mode 100644 src-cpp-structure/cmake/app-win.cmake delete mode 100644 src-cpp-structure/cmake/conan.cmake delete mode 100644 src-cpp-structure/cmake/project-config.cmake delete mode 100644 src-cpp-structure/cmake/services-linux.cmake delete mode 100644 src-cpp-structure/cmake/services-mac.cmake delete mode 100644 src-cpp-structure/cmake/services-win.cmake delete mode 100644 src-cpp-structure/cmake/translation.cmake delete mode 100644 src-cpp-structure/conan-debug-profile delete mode 100644 src-cpp-structure/conan-release-profile delete mode 100644 src-cpp-structure/conanfile.py delete mode 100644 src-cpp-structure/projects/App/Boot/AppController.cpp delete mode 100644 src-cpp-structure/projects/App/Boot/AppController.h delete mode 100644 src-cpp-structure/projects/App/Boot/AppWindow.cpp delete mode 100644 src-cpp-structure/projects/App/Boot/AppWindow.h delete mode 100644 src-cpp-structure/projects/App/Boot/AppWindow.mm delete mode 100644 src-cpp-structure/projects/App/Boot/DI.h delete mode 100644 src-cpp-structure/projects/App/Boot/main.cpp delete mode 100644 src-cpp-structure/projects/App/CMakeLists.txt delete mode 100644 src-cpp-structure/projects/App/Common/Constants.h delete mode 100644 src-cpp-structure/projects/App/Common/Utils.h delete mode 100644 src-cpp-structure/projects/App/Core/Engine.cpp delete mode 100644 src-cpp-structure/projects/App/Core/Engine.h delete mode 100644 src-cpp-structure/projects/App/Core/GlobalEvents.cpp delete mode 100644 src-cpp-structure/projects/App/Core/GlobalEvents.h delete mode 100644 src-cpp-structure/projects/App/Core/SignalsManager.cpp delete mode 100644 src-cpp-structure/projects/App/Core/SignalsManager.h delete mode 100644 src-cpp-structure/projects/App/Core/SingleInstance.cpp delete mode 100644 src-cpp-structure/projects/App/Core/SingleInstance.h delete mode 100644 src-cpp-structure/projects/App/Core/StatusSyntaxHighlighter.cpp delete mode 100644 src-cpp-structure/projects/App/Core/StatusSyntaxHighlighter.h delete mode 100644 src-cpp-structure/projects/App/Global/LocalAccountSettings.cpp delete mode 100644 src-cpp-structure/projects/App/Global/LocalAccountSettings.h delete mode 100644 src-cpp-structure/projects/App/Global/LocalAppSettings.cpp delete mode 100644 src-cpp-structure/projects/App/Global/LocalAppSettings.h delete mode 100644 src-cpp-structure/projects/App/Global/SettingsProperties.h delete mode 100644 src-cpp-structure/projects/App/Modules/Module.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Module.h delete mode 100644 src-cpp-structure/projects/App/Modules/ModuleBuilder.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/ModuleBuilder.h delete mode 100644 src-cpp-structure/projects/App/Modules/ModuleInterface.h delete mode 100644 src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.h delete mode 100644 src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Controller.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Controller.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/ControllerInterface.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/Controller.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/Controller.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/ControllerInterface.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/Item.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/Item.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/Model.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/Model.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/Module.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/Module.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/ModuleInterface.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/View.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/View.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Login/ViewInterface.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Module.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Module.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/ModuleInterface.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/ControllerInterface.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleInterface.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/Onboarding/ViewInterface.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/View.cpp delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/View.h delete mode 100644 src-cpp-structure/projects/App/Modules/Startup/ViewInterface.h delete mode 100644 src-cpp-structure/projects/Backend/CMakeLists.txt delete mode 100644 src-cpp-structure/projects/Backend/include/StatusBackend/Types.h delete mode 100644 src-cpp-structure/projects/Backend/include/StatusBackend/Utils.h delete mode 100644 src-cpp-structure/projects/Backend/include/StatusBackend/WalletAccounts.h delete mode 100644 src-cpp-structure/projects/Backend/src/WalletAccounts.cpp delete mode 100644 src-cpp-structure/projects/CMakeLists.txt delete mode 100644 src-cpp-structure/projects/Services/CMakeLists.txt delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Accounts/AccountDto.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Accounts/GeneratedAccountDto.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Accounts/Service.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Accounts/ServiceInterface.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Accounts/ServiceMock.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/AccountsService delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Common/Constants.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Common/Json.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Common/Logger.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Common/SigningPhrases.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/CommonService delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Keychain/Service.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/Keychain/ServiceInterface.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/KeychainService delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/Service.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/ServiceInterface.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/WalletAccountDto.h delete mode 100644 src-cpp-structure/projects/Services/include/StatusServices/WalletAccountsService delete mode 100644 src-cpp-structure/projects/Services/src/Accounts/Service.cpp delete mode 100644 src-cpp-structure/projects/Services/src/Keychain/Keychain.h delete mode 100644 src-cpp-structure/projects/Services/src/Keychain/Keychain.mm delete mode 100644 src-cpp-structure/projects/Services/src/Keychain/KeychainManager.cpp delete mode 100644 src-cpp-structure/projects/Services/src/Keychain/KeychainManager.h delete mode 100644 src-cpp-structure/projects/Services/src/Keychain/KeychainManager.mm delete mode 100644 src-cpp-structure/projects/Services/src/Keychain/LocalAuthentication.h delete mode 100644 src-cpp-structure/projects/Services/src/Keychain/LocalAuthentication.mm delete mode 100644 src-cpp-structure/projects/Services/src/Keychain/Service.cpp delete mode 100644 src-cpp-structure/projects/Services/src/WalletAccounts/Service.cpp delete mode 100644 src-cpp-structure/projects/ServicesTest/CMakeLists.txt delete mode 100644 src-cpp-structure/projects/ServicesTest/src/AccountsService.cpp delete mode 100644 src-cpp-structure/projects/ServicesTest/src/Constants.h delete mode 100644 src-cpp-structure/projects/ServicesTest/src/StatusTestEnvironment.cpp delete mode 100644 src-cpp-structure/projects/ServicesTest/src/StatusTestEnvironment.h delete mode 100644 src-cpp-structure/projects/ServicesTest/src/Utils.h delete mode 100644 src-cpp-structure/projects/ServicesTest/src/main.cpp create mode 100644 test/libs/CMakeLists.txt create mode 100644 test/libs/StatusGoQt/CMakeLists.txt create mode 100644 test/libs/StatusGoQt/test_accounts.cpp create mode 100644 test/libs/StatusGoQt/test_messaging.cpp diff --git a/.gitignore b/.gitignore index 9fb72ceb9..716b8fd1d 100644 --- a/.gitignore +++ b/.gitignore @@ -90,9 +90,3 @@ build/ *.exe *.out *.app - -# Cpp App -*build-src-cpp-structure* -src-cpp-structure/projects/App/translations.qrc -src-cpp-structure/projects/App/i18n/* -src-cpp-structure/projects/App/translations/* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fb1531b1..3d200641b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,5 +31,6 @@ add_subdirectory(libs) add_subdirectory(app) add_subdirectory(test) # TODO: temporary not to duplicate resources until we switch to c++ app then it can be refactored +add_subdirectory(resources) add_subdirectory(ui/imports/assets) add_subdirectory(ui/fonts) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 1acc38b15..79d239680 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_AUTORCC On) find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick REQUIRED) qt6_standard_project_setup() -qt6_add_executable(${PROJECT_NAME} "") +qt6_add_executable(${PROJECT_NAME}) # TODO: Fix temporarly workaround until we make qt6_target_qml_sources work # Adds qml files as /Status/Application/qml/.../ @@ -45,7 +45,6 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE BUILD_PROJECT_APPLICATION_NAM target_compile_definitions(${PROJECT_NAME} PRIVATE BUILD_BINARY_DIR=${CMAKE_BINARY_DIR}) target_compile_definitions(${PROJECT_NAME} PRIVATE BUILD_SOURCE_DIR=${CMAKE_SOURCE_DIR}) -add_subdirectory(qml) add_subdirectory(qml/Status/Application/Navigation) add_subdirectory(src) add_subdirectory(res) @@ -63,14 +62,15 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Quick - StatusQ_Application_Navigation + Status::Application::Navigation # TODO: Use Status:: namespace - #Core - Helpers - Onboarding - Assets - StatusQ + Status::ApplicationCore + Status::Helpers + Status::Onboarding + Status::Assets + Status::StatusQ + Status::StatusGoQt ) # QtCreator needs this @@ -81,4 +81,4 @@ install( TARGETS ${PROJECT_NAME} RUNTIME -) \ No newline at end of file +) diff --git a/app/qml/CMakeLists.txt b/app/qml/CMakeLists.txt deleted file mode 100644 index 5427c466c..000000000 --- a/app/qml/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Note that the automatic plugin generation is only possible if the module does not do anything besides registering the types. -# If it needs to do something more advanced like registering an image provider in initializeEngine, you still need to manually write the plugin. qt6_add_qml_module has support for this with NO_GENERATE_PLUGIN_SOURCE. - -target_sources(${PROJECT_NAME} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt -) - -add_subdirectory(Status/Application) - -# Qt 6.3 fails calling qt6_target_qml_sources -#qt6_target_qml_sources(${PROJECT_NAME} -# QML_FILES -# main.qml -#) diff --git a/app/qml/Status/Application/CMakeLists.txt b/app/qml/Status/Application/CMakeLists.txt deleted file mode 100644 index 72161748f..000000000 --- a/app/qml/Status/Application/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -target_sources(${PROJECT_NAME} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt -) - -# Qt 6.3 fails calling qt6_target_qml_sources -#qt6_target_qml_sources(${PROJECT_NAME} -# QML_FILES -# StatusWindow.qml -# StatusContentView.qml -# TODO -#) diff --git a/app/qml/Status/Application/Navigation/CMakeLists.txt b/app/qml/Status/Application/Navigation/CMakeLists.txt index 3070cf775..7f98709d7 100644 --- a/app/qml/Status/Application/Navigation/CMakeLists.txt +++ b/app/qml/Status/Application/Navigation/CMakeLists.txt @@ -1,5 +1,5 @@ # Controls specialized on user workflows -project(StatusQ_Application_Navigation) +project(Status_Application_Navigation) set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) @@ -23,3 +23,4 @@ qt6_add_qml_module(${PROJECT_NAME} OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Status/Application/Navigation ) +add_library(Status::Application::Navigation ALIAS Status_Application_Navigation) diff --git a/app/src/main.cpp b/app/src/main.cpp index df39f86eb..42ee38a84 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -21,7 +21,7 @@ int main(int argc, char *argv[]) QGuiApplication app(argc, argv); setApplicationInformation(app); - + QTranslator translator; const QStringList uiLanguages = QLocale::system().uiLanguages(); for (const QString &locale : uiLanguages) { diff --git a/ci/Jenkinsfile.linux-cpp b/ci/Jenkinsfile.linux-cpp.todo similarity index 85% rename from ci/Jenkinsfile.linux-cpp rename to ci/Jenkinsfile.linux-cpp.todo index 26df37145..01feddd6f 100644 --- a/ci/Jenkinsfile.linux-cpp +++ b/ci/Jenkinsfile.linux-cpp.todo @@ -44,9 +44,8 @@ pipeline { stage('CMake Build') { steps { sh "conan install ${env.WORKSPACE}/ --profile=${env.WORKSPACE}/vendor/conan-configs/linux.ini -s build_type=Release --build=missing -if=${env.WORKSPACE}/build/conan -of=${env.WORKSPACE}/build" - // TODO: switch CMAKE_PREFIX_PATH with "-DCMAKE_TOOLCHAIN_FILE=${env.WORKSPACE}/build/conan/conan_toolchain.cmake" after fixing error "c++: error: unrecognized command line option '-stdlib=libc++'" - sh "qt-cmake ${env.WORKSPACE} -G Ninja -B ${env.WORKSPACE}/build -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=${env.WORKSPACE}/build/conan/conan_home/.conan/data/gtest/1.11.0/_/_/package/521ce6d2b56041e08ea425948717819429cfbc29/" - sh "cmake --build ${env.WORKSPACE}/build" + // TODO: This fails compiling status-go with Jenkins user but not when run with docker's user. Fix go installation to work for all users or build docker with jenkin's + sh "conan build ${env.WORKSPACE} --build-folder=${env.WORKSPACE}/build/conan" } } diff --git a/ci/cpp/Dockerfile-linux b/ci/cpp/Dockerfile-linux index 81bea5cfc..0799b2ad5 100644 --- a/ci/cpp/Dockerfile-linux +++ b/ci/cpp/Dockerfile-linux @@ -2,7 +2,7 @@ FROM stateoftheartio/qt6:6.3-gcc-aqt RUN export DEBIAN_FRONTEND=noninteractive \ && sudo apt update -yq \ - && sudo apt install -yq libgl-dev libvulkan-dev libxcb*-dev libxkbcommon-x11-dev python3-pip gcc-10 + && sudo apt install -yq libgl-dev libvulkan-dev libxcb*-dev libxkbcommon-x11-dev python3-pip gcc-10 golang-go RUN sudo pip install conan diff --git a/conanfile.py b/conanfile.py index 3b1297f00..5ef44c66b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -5,7 +5,7 @@ class StatusDesktop(ConanFile): name = "status-desktop" settings = "os", "compiler", "build_type", "arch" - requires = "gtest/1.11.0" #"fruit/3.6.0", + requires = "gtest/1.11.0", "nlohmann_json/3.10.5" # "fruit/3.6.0", # cmake_find_package and cmake_find_package_multi should be substituted with CMakeDeps # as soon as Conan 2.0 is released and all conan-center packages are adapted diff --git a/libs/ApplicationCore/CMakeLists.txt b/libs/ApplicationCore/CMakeLists.txt new file mode 100644 index 000000000..bd656c722 --- /dev/null +++ b/libs/ApplicationCore/CMakeLists.txt @@ -0,0 +1,54 @@ +# Base library. Expect most of the module libraries to depend on it +# +cmake_minimum_required(VERSION 3.21) + +project(ApplicationCore + VERSION 0.1.0 + LANGUAGES CXX) + +set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) + +find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml REQUIRED) +qt6_standard_project_setup() + +qt6_add_qml_module(ApplicationCore + URI Status.ApplicationCore + VERSION 1.0 +) +add_library(Status::ApplicationCore ALIAS ApplicationCore) + +target_link_libraries(ApplicationCore + PRIVATE + Qt6::Quick + Qt6::Qml +) + +install( + TARGETS + ApplicationCore + RUNTIME +) + +target_include_directories(ApplicationCore + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/ApplicationCore + # TODO: Workaround to QML_ELEMENT Qt6 + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/src/ApplicationCore + + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src +) + +target_sources(ApplicationCore + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/ApplicationCore/Conversions.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/ApplicationCore/Conversions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ApplicationCore/UserConfiguration.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/ApplicationCore/UserConfiguration.cpp +) + +# QtCreator needs this +set(QML_IMPORT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/qml;${QML_IMPORT_PATH} CACHE STRING "For QtCreator" FORCE) +list(REMOVE_DUPLICATES QML_IMPORT_PATH) + diff --git a/libs/ApplicationCore/src/ApplicationCore/Conversions.cpp b/libs/ApplicationCore/src/ApplicationCore/Conversions.cpp new file mode 100644 index 000000000..f7d0caad5 --- /dev/null +++ b/libs/ApplicationCore/src/ApplicationCore/Conversions.cpp @@ -0,0 +1,15 @@ +#include "Conversions.h" + +namespace fs = std::filesystem; + +namespace Status { + +QString toString(const fs::path &path) { + return QString::fromStdString(path.string()); +} + +fs::path toPath(const QString &pathStr) { + return fs::path(pathStr.toStdString()); +} + +} diff --git a/libs/ApplicationCore/src/ApplicationCore/Conversions.h b/libs/ApplicationCore/src/ApplicationCore/Conversions.h new file mode 100644 index 000000000..a76fa8bb5 --- /dev/null +++ b/libs/ApplicationCore/src/ApplicationCore/Conversions.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include + +namespace Status { + +QString toString(const std::filesystem::path& path); +std::filesystem::path toPath(const QString& pathStr); + +} diff --git a/libs/ApplicationCore/src/ApplicationCore/UserConfiguration.cpp b/libs/ApplicationCore/src/ApplicationCore/UserConfiguration.cpp new file mode 100644 index 000000000..b08b0ad40 --- /dev/null +++ b/libs/ApplicationCore/src/ApplicationCore/UserConfiguration.cpp @@ -0,0 +1,47 @@ +#include "UserConfiguration.h" + +#include "Conversions.h" + +#include + +namespace fs = std::filesystem; + +namespace Status::ApplicationCore { + +namespace { + /// `status-go` data location + constexpr auto dataSubfolder = "data"; +} + +UserConfiguration::UserConfiguration(QObject *parent) + : QObject{parent} +{ + generateReleaseConfiguration(); +} + +const QString UserConfiguration::qmlUserDataFolder() const +{ + return toString(m_userDataFolder.string()); +} + +const fs::path &UserConfiguration::userDataFolder() const +{ + return m_userDataFolder; +} + +void UserConfiguration::setUserDataFolder(const QString &newUserDataFolder) +{ + auto newVal = Status::toPath(newUserDataFolder); + if (m_userDataFolder.compare(newVal) == 0) + return; + m_userDataFolder = newVal; + emit userDataFolderChanged(); +} + +void UserConfiguration::generateReleaseConfiguration() +{ + m_userDataFolder = toPath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))/dataSubfolder; + emit userDataFolderChanged(); +} + +} diff --git a/libs/ApplicationCore/src/ApplicationCore/UserConfiguration.h b/libs/ApplicationCore/src/ApplicationCore/UserConfiguration.h new file mode 100644 index 000000000..e7e65c1a4 --- /dev/null +++ b/libs/ApplicationCore/src/ApplicationCore/UserConfiguration.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include + +namespace Status::ApplicationCore { + +namespace fs = std::filesystem; + +class UserConfiguration: public QObject +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QString userDataFolder READ qmlUserDataFolder WRITE setUserDataFolder NOTIFY userDataFolderChanged) +public: + explicit UserConfiguration(QObject *parent = nullptr); + + const QString qmlUserDataFolder() const; + const fs::path &userDataFolder() const; + void setUserDataFolder(const QString &newUserDataFolder); + +signals: + void userDataFolderChanged(); + +private: + void generateReleaseConfiguration(); + + fs::path m_userDataFolder; +}; + +} diff --git a/libs/Assets/CMakeLists.txt b/libs/Assets/CMakeLists.txt index e8baff919..5b61a04fc 100644 --- a/libs/Assets/CMakeLists.txt +++ b/libs/Assets/CMakeLists.txt @@ -16,7 +16,7 @@ set_source_files_properties(qml/Status/Assets/Resources.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE ) -qt6_add_qml_module(${PROJECT_NAME} +qt6_add_qml_module(Assets URI Status.Assets VERSION 1.0 @@ -27,8 +27,9 @@ qt6_add_qml_module(${PROJECT_NAME} # Required to suppress "qmllint may not work" warning OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Status/Assets/ ) +add_library(Status::Assets ALIAS Assets) -target_link_libraries(${PROJECT_NAME} +target_link_libraries(Assets PRIVATE Qt6::Qml @@ -43,6 +44,6 @@ list(REMOVE_DUPLICATES QML_IMPORT_PATH) install( TARGETS - ${PROJECT_NAME} + Assets RUNTIME -) \ No newline at end of file +) diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 3ea3bc0c9..323f1768e 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -1,6 +1,6 @@ -add_subdirectory(Helpers) -# TODO: enable after adding content -#add_subdirectory(Core) -add_subdirectory(StatusQ) -add_subdirectory(Onboarding) +add_subdirectory(ApplicationCore) add_subdirectory(Assets) +add_subdirectory(Helpers) +add_subdirectory(Onboarding) +add_subdirectory(StatusGoQt) +add_subdirectory(StatusQ) diff --git a/libs/Core/CMakeLists.txt b/libs/Core/CMakeLists.txt deleted file mode 100644 index 370187121..000000000 --- a/libs/Core/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -# Base library. Expect most of the module libraries to depend on it -# -cmake_minimum_required(VERSION 3.21) - -project(Core - VERSION 0.1.0 - LANGUAGES CXX) - -set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) - -find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml REQUIRED) -qt6_standard_project_setup() - -qt6_add_qml_module(${PROJECT_NAME} - URI Status.Core - VERSION 1.0 - - # TODO: temporary until we make qt_target_qml_sources work - QML_FILES - qml/Status/Core/DevTest.qml - - # Required to suppress "qmllint may not work" warning - OUTPUT_DIRECTORY - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Status/Core -) - -add_subdirectory(qml/Status/Core) -add_subdirectory(src) - -target_link_libraries(${PROJECT_NAME} - PRIVATE - Qt6::Quick - Qt6::Qml -) - -install( - TARGETS - ${PROJECT_NAME} - RUNTIME -) \ No newline at end of file diff --git a/libs/Core/res/CMakeLists.txt b/libs/Core/res/CMakeLists.txt deleted file mode 100644 index 06a32a1b4..000000000 --- a/libs/Core/res/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -qt6_add_resources(${PROJECT_NAME} imageresources - PREFIX "/images" - FILES logo.png splashscreen.png -) \ No newline at end of file diff --git a/libs/Core/src/Core/CMakeLists.txt b/libs/Core/src/Core/CMakeLists.txt deleted file mode 100644 index 124b911e0..000000000 --- a/libs/Core/src/Core/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -target_sources(${PROJECT_NAME} - PRIVATE - - ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt -) diff --git a/libs/Helpers/CMakeLists.txt b/libs/Helpers/CMakeLists.txt index 1cd85ff05..0e821e6df 100644 --- a/libs/Helpers/CMakeLists.txt +++ b/libs/Helpers/CMakeLists.txt @@ -11,7 +11,8 @@ set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml REQUIRED) qt6_standard_project_setup() -add_library(${PROJECT_NAME} SHARED "") +add_library(Helpers SHARED) +add_library(Status::Helpers ALIAS Helpers) # Setup configuration type (Debug/Release) # Inspired by https://programmingrecluse.wordpress.com/2020/02/04/detect-debug-build-with-cmake/ @@ -36,21 +37,17 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/template/BuildConfiguration.h.in" "${BUILD_GENERATED_DIRECTORY}/Helpers/BuildConfiguration.h" @ONLY) -target_include_directories(${PROJECT_NAME} +target_include_directories(Helpers PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src ${BUILD_GENERATED_DIRECTORY} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers ${BUILD_GENERATED_DIRECTORY}/Helpers ) -target_sources(${PROJECT_NAME} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/template/BuildConfiguration.h.in -) - -add_subdirectory(src) - -target_link_libraries(${PROJECT_NAME} +target_link_libraries(Helpers PRIVATE Qt6::Quick Qt6::Qml @@ -58,6 +55,14 @@ target_link_libraries(${PROJECT_NAME} install( TARGETS - ${PROJECT_NAME} + Helpers RUNTIME -) \ No newline at end of file +) + +target_sources(Helpers + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/helpers.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/logs.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/logs.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/Singleton.h +) diff --git a/libs/Helpers/src/CMakeLists.txt b/libs/Helpers/src/CMakeLists.txt deleted file mode 100644 index 0ef4e7d5b..000000000 --- a/libs/Helpers/src/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -target_include_directories(${PROJECT_NAME} - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/Helpers -) - -add_subdirectory(Helpers) \ No newline at end of file diff --git a/libs/Helpers/src/Helpers/CMakeLists.txt b/libs/Helpers/src/Helpers/CMakeLists.txt deleted file mode 100644 index e0e4f1266..000000000 --- a/libs/Helpers/src/Helpers/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -target_sources(${PROJECT_NAME} - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/helpers.h - ${CMAKE_CURRENT_SOURCE_DIR}/logs.h - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/logs.cpp -) diff --git a/libs/Helpers/src/Helpers/Singleton.h b/libs/Helpers/src/Helpers/Singleton.h new file mode 100644 index 000000000..8d4132822 --- /dev/null +++ b/libs/Helpers/src/Helpers/Singleton.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace Status::Helpers +{ + +template +class Singleton +{ +public: + virtual ~Singleton() = default; + + static T& getInstance() + { + static T instance; + return instance; + } + + Singleton(const Singleton&) = delete; + Singleton& operator = (const Singleton&) = delete; +private: + + Singleton() = default; +}; + +} diff --git a/libs/Helpers/src/Helpers/helpers.h b/libs/Helpers/src/Helpers/helpers.h index 19a7b7ba5..05447f050 100644 --- a/libs/Helpers/src/Helpers/helpers.h +++ b/libs/Helpers/src/Helpers/helpers.h @@ -1,4 +1,4 @@ -#include +#include "Helpers/BuildConfiguration.h" namespace Status::Helpers { diff --git a/libs/Helpers/src/Helpers/logs.cpp b/libs/Helpers/src/Helpers/logs.cpp index a4dbff7dd..47130ec97 100644 --- a/libs/Helpers/src/Helpers/logs.cpp +++ b/libs/Helpers/src/Helpers/logs.cpp @@ -6,7 +6,7 @@ #include -#include +#include "BuildConfiguration.h" namespace Status::Helpers { diff --git a/libs/Onboarding/CMakeLists.txt b/libs/Onboarding/CMakeLists.txt index c8c459c66..6914b7c12 100644 --- a/libs/Onboarding/CMakeLists.txt +++ b/libs/Onboarding/CMakeLists.txt @@ -1,4 +1,4 @@ -# Base library. Expect most of the module libraries to depend on it +# Onboarding Module build definition # cmake_minimum_required(VERSION 3.21) @@ -8,33 +8,61 @@ project(Onboarding set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) -find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml REQUIRED) +find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml Concurrent REQUIRED) qt6_standard_project_setup() -qt6_add_qml_module(${PROJECT_NAME} +qt6_add_qml_module(Onboarding URI Status.Onboarding VERSION 1.0 - # TODO: temporary until we make qt_target_qml_sources work QML_FILES + qml/Status/Onboarding/base/SetupNewProfilePageBase.qml + qml/Status/Onboarding/base/OnboardingPageBase.qml + + qml/Status/Onboarding/ConfirmPasswordPage.qml + qml/Status/Onboarding/CreatePasswordPage.qml + qml/Status/Onboarding/LoginView.qml qml/Status/Onboarding/OnboardingView.qml + qml/Status/Onboarding/SetupNewProfileView.qml + qml/Status/Onboarding/SetUserNameAndPicturePage.qml + qml/Status/Onboarding/TempTextInput.qml + qml/Status/Onboarding/WelcomeView.qml # Required to suppress "qmllint may not work" warning OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Status/Onboarding/ ) +add_library(Status::Onboarding ALIAS Onboarding) -target_link_libraries(${PROJECT_NAME} +add_subdirectory(src) +add_subdirectory(tests) + +target_link_libraries(Onboarding PRIVATE Qt6::Quick Qt6::Qml + Qt6::Concurrent + + Status::ApplicationCore + + Status::StatusGoQt + Status::StatusGoConfig ) +# Required by the MacOS authenticator. Consider moving platform particular implementation in its own static linked library +if(APPLE) + target_link_libraries(Onboarding + PRIVATE + "-framework Security" + "-framework LocalAuthentication" + ) +endif() + # QtCreator needs this set(QML_IMPORT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/qml;${QML_IMPORT_PATH} CACHE STRING "For QtCreator" FORCE) list(REMOVE_DUPLICATES QML_IMPORT_PATH) install( TARGETS - ${PROJECT_NAME} + Onboarding RUNTIME -) \ No newline at end of file +) diff --git a/libs/Onboarding/qml/Status/Onboarding/ConfirmPasswordPage.qml b/libs/Onboarding/qml/Status/Onboarding/ConfirmPasswordPage.qml new file mode 100644 index 000000000..8cce49654 --- /dev/null +++ b/libs/Onboarding/qml/Status/Onboarding/ConfirmPasswordPage.qml @@ -0,0 +1,85 @@ +import QtQuick +import QtQuick.Controls + +import Status.Onboarding + +import "base" + +SetupNewProfilePageBase { + id: root + + TempTextInput { + id: confirmPasswordInput + + text: qsTr("1234567890") + + width: 416 + height: 44 + + anchors { + horizontalCenter: alignmentItem.horizontalCenter + verticalCenter: alignmentItem.verticalCenter + verticalCenterOffset: -baselineOffset + } + + font.pointSize: 23 + verticalAlignment: TextInput.AlignVCenter + } + + Label { + id: errorLabel + + anchors { + bottom: finalizeButton.top + horizontalCenter: finalizeButton.horizontalCenter + margins: 10 + } + + color: "red" + text: qsTr("Something went wrong") + visible: false + } + + Button { + id: finalizeButton + text: qsTr("Finalize Status Password Creation") + + anchors { + horizontalCenter: alignmentItem.horizontalCenter + top: alignmentItem.bottom + topMargin: 125 + } + + enabled: confirmPasswordInput.text === newAccountController.password + + onClicked: { + // TODO have states to drive async creation + errorLabel.visible = false + finalizeButton.enabled = false + busyIndicatorMouseArea.cursorShape = Qt.BusyCursor + + newAccountController.createAccount() + } + } + + Connections { + target: newAccountController + function onAccountCreatedAndLoggedIn() { + busyIndicatorMouseArea.cursorShape = undefined + root.pageDone() + } + function onAccountCreationError() { + errorLabel.visible = true; + busyIndicatorMouseArea.cursorShape = undefined + } + } + + MouseArea { + id: busyIndicatorMouseArea + + anchors.fill: parent + + acceptedButtons: Qt.NoButton + enabled: false + } +} diff --git a/libs/Onboarding/qml/Status/Onboarding/CreatePasswordPage.qml b/libs/Onboarding/qml/Status/Onboarding/CreatePasswordPage.qml new file mode 100644 index 000000000..0ac629969 --- /dev/null +++ b/libs/Onboarding/qml/Status/Onboarding/CreatePasswordPage.qml @@ -0,0 +1,68 @@ +import QtQuick +import QtQuick.Controls + +import Status.Onboarding + +import "base" + +SetupNewProfilePageBase { + id: root + + TempTextInput { + id: passwordInput + + text: newAccountController.password + Binding { + target: newAccountController + property: "password" + value: passwordInput.text + } + + width: 416 + height: 44 + + anchors { + horizontalCenter: parent.horizontalCenter + bottom: alignmentItem.top + bottomMargin: 112 + } + + font.pointSize: 23 + verticalAlignment: TextInput.AlignVCenter + } + + TempTextInput { + id: confirmPasswordInput + + text: newAccountController.confirmationPassword + Binding { + target: newAccountController + property: "confirmationPassword" + value: confirmPasswordInput.text + } + + width: 416 + height: 44 + + anchors { + horizontalCenter: alignmentItem.horizontalCenter + verticalCenter: alignmentItem.verticalCenter + verticalCenterOffset: -baselineOffset + } + + font.pointSize: 23 + verticalAlignment: TextInput.AlignVCenter + } + + Button { + text: qsTr("Create Password") + + anchors.horizontalCenter: alignmentItem.horizontalCenter + anchors.top: alignmentItem.bottom + anchors.topMargin: 125 + + enabled: newAccountController.passwordIsValid && newAccountController.confirmationPasswordIsValid + + onClicked: root.pageDone() + } +} diff --git a/libs/Onboarding/qml/Status/Onboarding/LoginView.qml b/libs/Onboarding/qml/Status/Onboarding/LoginView.qml new file mode 100644 index 000000000..6e6438aad --- /dev/null +++ b/libs/Onboarding/qml/Status/Onboarding/LoginView.qml @@ -0,0 +1,27 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Status.Containers + +import "base" + +OnboardingPageBase { + id: root + + ColumnLayout { + anchors { + centerIn: parent + verticalCenterOffset: 50 + } + + Label { + text: qsTr("Welcome back") + Layout.alignment: Qt.AlignHCenter + } + + LayoutSpacer { + Layout.preferredHeight: 210 + } + } +} diff --git a/libs/Onboarding/qml/Status/Onboarding/OnboardingView.qml b/libs/Onboarding/qml/Status/Onboarding/OnboardingView.qml index b17287fb2..f67220594 100644 --- a/libs/Onboarding/qml/Status/Onboarding/OnboardingView.qml +++ b/libs/Onboarding/qml/Status/Onboarding/OnboardingView.qml @@ -2,36 +2,64 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls +import QtQml + +import Qt.labs.platform + import Status.Containers import Status.Controls.Navigation +import Status.Onboarding +import Status.ApplicationCore +/** \brief Drives the onboarding workflow + * + */ Item { id: root signal userLoggedIn() - implicitWidth: mainLayout.implicitWidth - implicitHeight: mainLayout.implicitHeight + implicitWidth: 1232 + implicitHeight: 770 - ColumnLayout { - id: mainLayout + UserConfiguration { + id: userConfiguration + } + + OnboardingModule { + id: onboardingModule + + userDataPath: userConfiguration.userDataFolder + } + + MacTrafficLights { + anchors.left: parent.left + anchors.margins: 13 + anchors.top: parent.top + z: stackView.z + 1 + } + + StackView { + id: stackView anchors.fill: parent - MacTrafficLights { - Layout.margins: 13 + initialItem: WelcomeView { + onboardingController: onboardingModule.controller + onSetupNewAccount: stackView.push(setupNewProfileViewComponent) + onAccountLoggedIn: root.userLoggedIn() } + } - LayoutSpacer {} - Label { - Layout.alignment: Qt.AlignHCenter - text: "TODO OnboardingWorkflow" + Component { + id: setupNewProfileViewComponent + + SetupNewProfileView { + onAbortAccountCreation: stackView.pop() + onUserLoggedIn: root.userLoggedIn() + + newAccountController: onboardingModule.controller.initNewAccountController() + Component.onDestruction: onboardingModule.controller.terminateNewAccountController() } - Button { - text: "Done" - Layout.alignment: Qt.AlignHCenter - onClicked: root.userLoggedIn() - } - LayoutSpacer {} } } diff --git a/libs/Onboarding/qml/Status/Onboarding/SetUserNameAndPicturePage.qml b/libs/Onboarding/qml/Status/Onboarding/SetUserNameAndPicturePage.qml new file mode 100644 index 000000000..86a03fef0 --- /dev/null +++ b/libs/Onboarding/qml/Status/Onboarding/SetUserNameAndPicturePage.qml @@ -0,0 +1,59 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Status.Containers + +import "base" + +SetupNewProfilePageBase { + id: root + + ColumnLayout { + anchors { + centerIn: parent + verticalCenterOffset: 50 + } + + Label { + text: qsTr("Your profile") + Layout.alignment: Qt.AlignHCenter + } + + LayoutSpacer { + Layout.preferredHeight: 210 + } + + TempTextInput { + id: nameInput + + text: newAccountController.name + Binding { + target: newAccountController + property: "name" + value: nameInput.text + } + + Layout.preferredWidth: 328 + Layout.preferredHeight: 44 + + Layout.alignment: Qt.AlignHCenter + + font.pointSize: 23 + } + + LayoutSpacer { + Layout.preferredHeight: 144 + } + + Button { + text: qsTr("Next") + + Layout.alignment: Qt.AlignHCenter + + enabled: newAccountController.nameIsValid + + onClicked: root.pageDone() + } + } +} diff --git a/libs/Onboarding/qml/Status/Onboarding/SetupNewProfileView.qml b/libs/Onboarding/qml/Status/Onboarding/SetupNewProfileView.qml new file mode 100644 index 000000000..c9e710fce --- /dev/null +++ b/libs/Onboarding/qml/Status/Onboarding/SetupNewProfileView.qml @@ -0,0 +1,86 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import QtQml + +import Qt.labs.platform + +import Status.Containers +import Status.Controls.Navigation +import Status.Onboarding + +/** \brief Drives the onboarding workflow + * + */ +Item { + id: root + + // TODO: fix error "Unable to assign Status::Onboarding::NewAccountController to Status::Onboarding::NewAccountController" then enable typed properties + required property var/*NewAccountController*/ newAccountController + + signal userLoggedIn() + signal abortAccountCreation() + + QtObject { + id: d + + function goToPreviousPage() { + if(swipeView.currentItem === setUserNameAndPicturePage) + root.abortAccountCreation() + else + swipeView.currentIndex-- + } + function goToNextPage() { + if(swipeView.currentItem === confirmPasswordPage) + root.userLoggedIn() + else + swipeView.currentIndex++ + } + } + + ObjectModel { + id: pagesModel + + SetUserNameAndPicturePage { + id: setUserNameAndPicturePage + newAccountController: root.newAccountController + } + CreatePasswordPage { + newAccountController: root.newAccountController + } + ConfirmPasswordPage { + id: confirmPasswordPage + newAccountController: root.newAccountController + } + } + + SwipeView { + id: swipeView + anchors.fill: parent + + Repeater { + id: pageRepeater + model: pagesModel + + Loader { + id: pageLoader + active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem + source: modelData + } + } + Connections { + target: pageRepeater.itemAt(swipeView.currentIndex) + function onPageDone() { d.goToNextPage() } + function onGoBack() { d.goToPreviousPage() } + } + } + + PageIndicator { + count: swipeView.count + currentIndex: swipeView.currentIndex + + anchors.bottom: swipeView.bottom + anchors.horizontalCenter: swipeView.horizontalCenter + } +} diff --git a/libs/Onboarding/qml/Status/Onboarding/TempTextInput.qml b/libs/Onboarding/qml/Status/Onboarding/TempTextInput.qml new file mode 100644 index 000000000..bb69b1601 --- /dev/null +++ b/libs/Onboarding/qml/Status/Onboarding/TempTextInput.qml @@ -0,0 +1,18 @@ +import QtQuick + +TextInput { + width: 416 + height: 44 + + font.pointSize: 23 + verticalAlignment: TextInput.AlignVCenter + + Rectangle { + anchors { + fill: parent + margins: -1 + } + border.width: 1 + z: parent.z - 1 + } +} diff --git a/libs/Onboarding/qml/Status/Onboarding/WelcomeView.qml b/libs/Onboarding/qml/Status/Onboarding/WelcomeView.qml new file mode 100644 index 000000000..1a11cd17a --- /dev/null +++ b/libs/Onboarding/qml/Status/Onboarding/WelcomeView.qml @@ -0,0 +1,86 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Status.Containers + +import "base" + +OnboardingPageBase { + id: root + + required property var onboardingController // OnboardingController + + signal setupNewAccount() + signal accountLoggedIn() + + backAvailable: false + + ColumnLayout { + anchors { + centerIn: parent + verticalCenterOffset: -117 + } + spacing: 10 + + Label { + text: qsTr("Welcome to Status") + } + + LayoutSpacer { + Layout.preferredHeight: 50 + } + + ColumnLayout { + visible: accountsComboBox.count > 0 + ComboBox { + id: accountsComboBox + + Layout.preferredWidth: 328 + Layout.preferredHeight: 44 + + model: onboardingController.accounts + textRole: "name" + valueRole: "account" + } + + TempTextInput { + id: passwordInput + Layout.preferredWidth: 328 + Layout.preferredHeight: 44 + } + + Button { + text: qsTr("Login") + enabled: passwordInput.text.length >= 10 + onClicked: { + errorLabel.visible = false + onboardingController.login(accountsComboBox.currentValue, passwordInput.text) + } + } + Label { + id: errorLabel + text: qsTr("Failed logging in") + visible: false + color: "red" + } + } + + Button { + text: qsTr("I am new to Status") + onClicked: root.setupNewAccount() + } + } + + Connections { + target: onboardingController + + function onAccountLoggedIn() { + root.accountLoggedIn() + } + function onAccountLoginError(error) { + console.warn(`Error logging in "${error}"`) + errorLabel.visible = true + } + } +} diff --git a/libs/Onboarding/qml/Status/Onboarding/base/OnboardingPageBase.qml b/libs/Onboarding/qml/Status/Onboarding/base/OnboardingPageBase.qml new file mode 100644 index 000000000..632ff519d --- /dev/null +++ b/libs/Onboarding/qml/Status/Onboarding/base/OnboardingPageBase.qml @@ -0,0 +1,40 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Status.Onboarding +import Status.Controls.Navigation + +/*! Template to guide the onboarding layout + */ +Item { + id: root + + property bool backAvailable: true + + /// All done in the current page + signal pageDone() + signal goBack() + + Button { + id: backButton + text: "<" + + anchors { + left: parent.left + margins: 16 + bottom: parent.bottom + } + + visible: root.backAvailable + flat: true + background: Rectangle { + height: width + radius: width/2 + color: "#4360DF" + opacity: parent.hovered ? parent.pressed ? 1 : 0.5 : 0.1 + } + + onClicked: root.goBack() + } +} diff --git a/libs/Onboarding/qml/Status/Onboarding/base/SetupNewProfilePageBase.qml b/libs/Onboarding/qml/Status/Onboarding/base/SetupNewProfilePageBase.qml new file mode 100644 index 000000000..f82b2640b --- /dev/null +++ b/libs/Onboarding/qml/Status/Onboarding/base/SetupNewProfilePageBase.qml @@ -0,0 +1,21 @@ +import QtQuick + +/*! Proposal on how to templetize the alignment requirement of some views + */ +OnboardingPageBase { + // TODO: fix error "Unable to assign Status::Onboarding::NewAccountController to Status::Onboarding::NewAccountController" then enable typed properties + required property var/*NewAccountController*/ newAccountController + + /// Common reference item that doesn't change between common views/pages + readonly property Item alignmentItem: alignmentBaselineItem + + Item { + id: alignmentBaselineItem + + width: 1 + height: 1 + + anchors.horizontalCenter: parent.horizontalCenter + y: (root.height * 477/770) - baselineOffset + } +} diff --git a/libs/Core/src/CMakeLists.txt b/libs/Onboarding/src/CMakeLists.txt similarity index 67% rename from libs/Core/src/CMakeLists.txt rename to libs/Onboarding/src/CMakeLists.txt index a9fa39f4e..79238f21d 100644 --- a/libs/Core/src/CMakeLists.txt +++ b/libs/Onboarding/src/CMakeLists.txt @@ -1,13 +1,14 @@ # Internally we use includes directly # External clients have to explicitly use the module name -add_subdirectory(Core) +add_subdirectory(Onboarding) target_include_directories(${PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/Core + ${CMAKE_CURRENT_SOURCE_DIR}/Onboarding + # TODO: Workaround to QML_ELEMENT Qt6 INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/Core + ${CMAKE_CURRENT_SOURCE_DIR}/Onboarding PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/libs/Onboarding/src/Onboarding/Accounts/AccountDto.h b/libs/Onboarding/src/Onboarding/Accounts/AccountDto.h new file mode 100644 index 000000000..6ff62a82e --- /dev/null +++ b/libs/Onboarding/src/Onboarding/Accounts/AccountDto.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Common/Constants.h" +#include "Common/SigningPhrases.h" +#include "Common/Json.h" + +#include + +// TODO: Move to StatusGo library +namespace Status::Onboarding +{ + +struct AccountDto +{ + QString name; + long timestamp; + QString identicon; + QString keycardPairing; + QString keyUid; + + bool isValid() const + { + return !(name.isEmpty() || keyUid.isEmpty()); + } + + static AccountDto toAccountDto(const QJsonObject& jsonObj) + { + auto result = AccountDto(); + + try + { + result.name = Json::getMandatoryProp(jsonObj, "name")->toString(); + auto timestampIt = Json::getProp(jsonObj, "timestamp"); + if(timestampIt != jsonObj.constEnd()) { + bool ok = false; + auto t = timestampIt->toString().toLong(&ok); + if(ok) + result.timestamp = t; + } + result.identicon = Json::getMandatoryProp(jsonObj, "identicon")->toString(); + result.keycardPairing = Json::getMandatoryProp(jsonObj, "keycard-pairing")->toString(); + result.keyUid = Json::getMandatoryProp(jsonObj, "key-uid")->toString(); + + /// TODO: investigate unhandled `photo-path` value + } + catch (std::exception e) + { + qWarning() << QObject::tr("Mapping AccountDto failed: %1").arg(e.what()); + } + + return result; + } +}; + +} diff --git a/libs/Onboarding/src/Onboarding/Accounts/AccountsService.cpp b/libs/Onboarding/src/Onboarding/Accounts/AccountsService.cpp new file mode 100644 index 000000000..1be33ad81 --- /dev/null +++ b/libs/Onboarding/src/Onboarding/Accounts/AccountsService.cpp @@ -0,0 +1,419 @@ +#include "AccountsService.h" + +#include +#include +#include +#include + +#include + +#include + + +std::optional +getDataFromFile(const fs::path &path) +{ + QFile jsonFile{Status::toString(path)}; + if(!jsonFile.open(QIODevice::ReadOnly)) + { + qDebug() << "unable to open" << path.filename().c_str() << " for reading"; + return std::nullopt; + } + + QString data = jsonFile.readAll(); + jsonFile.close(); + return data; +} + +namespace Status::Onboarding +{ + +namespace StatusGo = Status::StatusGo; +namespace Utils = Status::StatusGo::Utils; + +AccountsService::AccountsService() + : m_isFirstTimeAccountLogin(false) +{ +} + +bool AccountsService::init(const fs::path& statusgoDataDir) +{ + m_statusgoDataDir = statusgoDataDir; + auto response = StatusGo::Accounts::generateAddresses(Constants::General::AccountDefaultPaths); + if(response.containsError()) + { + qWarning() << response.error.message; + return false; + } + + for(const auto &genAddressObj : response.result) + { + auto gAcc = GeneratedAccountDto::toGeneratedAccountDto(genAddressObj.toObject()); + gAcc.alias = generateAlias(gAcc.derivedAccounts.whisper.publicKey); + gAcc.identicon = generateIdenticon(gAcc.derivedAccounts.whisper.publicKey); + m_generatedAccounts.push_back(std::move(gAcc)); + } + return true; +} + +std::vector AccountsService::openAndListAccounts() +{ + auto response = StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()); + if(response.containsError()) + { + qWarning() << response.error.message; + return std::vector(); + } + + const auto multiAccounts = response.result; + std::vector result; + for(const auto &value : multiAccounts) + { + result.push_back(AccountDto::toAccountDto(value.toObject())); + } + return result; +} + +const std::vector& AccountsService::generatedAccounts() const +{ + return m_generatedAccounts; +} + +bool AccountsService::setupAccountAndLogin(const QString &accountId, const QString &password, const QString &displayName) +{ + QString installationId(QUuid::createUuid().toString(QUuid::WithoutBraces)); + QJsonObject accountData(getAccountDataForAccountId(accountId, displayName)); + + if(!setKeyStoreDir(accountData.value("key-uid").toString())) + return false; + + QJsonArray subAccountData(getSubaccountDataForAccountId(accountId, displayName)); + QJsonObject settings(getAccountSettings(accountId, installationId, displayName)); + QJsonObject nodeConfig(getDefaultNodeConfig(installationId)); + + QString hashedPassword(Utils::hashString(password)); + + // This initialize the DB if first time running. Required for storing accounts + if(StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()).containsError()) + return false; + + AccountsService::storeDerivedAccounts(accountId, hashedPassword, Constants::General::AccountDefaultPaths); + + m_loggedInAccount = saveAccountAndLogin(hashedPassword, accountData, subAccountData, settings, nodeConfig); + + return getLoggedInAccount().isValid(); +} + +const AccountDto& AccountsService::getLoggedInAccount() const +{ + return m_loggedInAccount; +} + +const GeneratedAccountDto& AccountsService::getImportedAccount() const +{ + return m_importedAccount; +} + +bool AccountsService::isFirstTimeAccountLogin() const +{ + return m_isFirstTimeAccountLogin; +} + +bool AccountsService::setKeyStoreDir(const QString &key) +{ + auto keyStoreDir = m_statusgoDataDir / m_keyStoreDirName / key.toStdString(); + auto response = StatusGo::General::initKeystore(keyStoreDir.c_str()); + return !response.containsError(); +} + +QString AccountsService::login(AccountDto account, const QString& password) +{ + // This is a requirement. Make it more explicit into the status go module + if(!setKeyStoreDir(account.keyUid)) + return QString("Failed to initialize keystore before logging in"); + + // This initialize the DB if first time running. Required before logging in + if(StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()).containsError()) + return QString("Failed to open accounts before logging in"); + + QString hashedPassword(Utils::hashString(password)); + + QString thumbnailImage; + QString largeImage; + auto response = StatusGo::Accounts::login(account.name, account.keyUid, hashedPassword, account.identicon, + thumbnailImage, largeImage); + if(response.containsError()) + { + qWarning() << response.error.message; + return QString(); + } + + m_loggedInAccount = std::move(account); + + return QString(); +} + +void AccountsService::clear() +{ + m_generatedAccounts.clear(); + m_loggedInAccount = AccountDto(); + m_importedAccount = GeneratedAccountDto(); + m_isFirstTimeAccountLogin = false; +} + +QString AccountsService::generateAlias(const QString& publicKey) +{ + auto response = StatusGo::Accounts::generateAlias(publicKey); + if(response.containsError()) + { + qWarning() << response.error.message; + return QString(); + } + + return response.result; +} + +QString AccountsService::generateIdenticon(const QString& publicKey) +{ + auto response = StatusGo::Accounts::generateIdenticon(publicKey); + if(response.containsError()) + { + qWarning() << response.error.message; + return QString(); + } + + return response.result; +} + +DerivedAccounts AccountsService::storeDerivedAccounts(const QString& accountId, const QString& hashedPassword, + const QVector& paths) +{ + auto response = StatusGo::Accounts::storeDerivedAccounts(accountId, hashedPassword, paths); + if(response.containsError()) + { + qWarning() << response.error.message; + return DerivedAccounts(); + } + return DerivedAccounts::toDerivedAccounts(response.result); +} + +StoredAccountDto AccountsService::storeAccount(const QString& accountId, const QString& hashedPassword) +{ + auto response = StatusGo::Accounts::storeAccount(accountId, hashedPassword); + if(response.containsError()) + { + qWarning() << response.error.message; + return StoredAccountDto(); + } + return toStoredAccountDto(response.result); +} + +AccountDto AccountsService::saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, + const QJsonArray& subaccounts, const QJsonObject& settings, + const QJsonObject& config) +{ + if(!StatusGo::Accounts::saveAccountAndLogin(hashedPassword, account, subaccounts, settings, config)) { + qWarning() << "Failed saving acccount" << account.value("name"); + return AccountDto(); + } + + m_isFirstTimeAccountLogin = true; + return AccountDto::toAccountDto(account); +} + +QJsonObject AccountsService::prepareAccountJsonObject(const GeneratedAccountDto& account, const QString &displayName) const +{ + return QJsonObject{{"name", displayName.isEmpty() ? account.alias : displayName}, + {"address", account.address}, + {"photo-path", account.identicon}, + {"identicon", account.identicon}, + {"key-uid", account.keyUid}, + {"keycard-pairing", QJsonValue()}}; +} + +QJsonObject AccountsService::getAccountDataForAccountId(const QString &accountId, const QString &displayName) const +{ + + for(const GeneratedAccountDto &acc : m_generatedAccounts) + { + if(acc.id == accountId) + { + return AccountsService::prepareAccountJsonObject(acc, displayName); + } + } + + if(m_importedAccount.isValid()) + { + if(m_importedAccount.id == accountId) + { + return AccountsService::prepareAccountJsonObject(m_importedAccount, displayName); + } + } + + qDebug() << "account not found"; + return QJsonObject(); +} + +QJsonArray AccountsService::prepareSubaccountJsonObject(const GeneratedAccountDto& account, const QString &displayName) const +{ + return { + QJsonObject{ + {"public-key", account.derivedAccounts.defaultWallet.publicKey}, + {"address", account.derivedAccounts.defaultWallet.address}, + {"color", "#4360df"}, + {"wallet", true}, + {"path", Constants::General::PathDefaultWallet}, + {"name", "Status account"} + }, + QJsonObject{ + {"public-key", account.derivedAccounts.whisper.publicKey}, + {"address", account.derivedAccounts.whisper.address}, + {"path", Constants::General::PathWhisper}, + {"name", displayName.isEmpty() ? account.alias : displayName}, + {"identicon", account.identicon}, + {"chat", true} + } + }; +} + +QJsonArray AccountsService::getSubaccountDataForAccountId(const QString& accountId, const QString &displayName) const +{ + // "All these for loops with a nested if cry for a std::find_if :)" + for(const GeneratedAccountDto &acc : m_generatedAccounts) + { + if(acc.id == accountId) + { + return prepareSubaccountJsonObject(acc, displayName); + } + } + if(m_importedAccount.isValid()) + { + if(m_importedAccount.id == accountId) + { + return prepareSubaccountJsonObject(m_importedAccount, displayName); + } + } + + // TODO: Is this expected? Have proper error propagation, otherwise throw + qDebug() << "account not found"; + return QJsonArray(); +} + +QString AccountsService::generateSigningPhrase(int count) const +{ + QStringList words; + for(int i = 0; i < count; i++) + { + words.append(Constants::SigningPhrases[QRandomGenerator::global()->bounded( + static_cast(Constants::SigningPhrases.size()))]); + } + return words.join(" "); +} + +QJsonObject AccountsService::prepareAccountSettingsJsonObject(const GeneratedAccountDto& account, + const QString& installationId, + const QString& displayName) const +{ + try { + auto templateDefaultNetworksJson = getDataFromFile(":/Status/StaticConfig/default-networks.json").value(); + auto infuraKey = getDataFromFile(":/Status/StaticConfig/infura_key").value(); + + QString defaultNetworksContent = templateDefaultNetworksJson.replace("%INFURA_KEY%", infuraKey); + QJsonArray defaultNetworksJson = QJsonDocument::fromJson(defaultNetworksContent.toUtf8()).array(); + + return QJsonObject{ + {"key-uid", account.keyUid}, + {"mnemonic", account.mnemonic}, + {"public-key", account.derivedAccounts.whisper.publicKey}, + {"name", account.alias}, + {"display-name", displayName}, + {"address", account.address}, + {"eip1581-address", account.derivedAccounts.eip1581.address}, + {"dapps-address", account.derivedAccounts.defaultWallet.address}, + {"wallet-root-address", account.derivedAccounts.walletRoot.address}, + {"preview-privacy", true}, + {"signing-phrase", generateSigningPhrase(3)}, + {"log-level", "INFO"}, + {"latest-derived-path", 0}, + {"networks/networks", defaultNetworksJson}, + {"currency", "usd"}, + {"identicon", account.identicon}, + {"waku-enabled", true}, + {"wallet/visible-tokens", { + {Constants::General::DefaultNetworkName, QJsonArray{"SNT"}} + } + }, + {"appearance", 0}, + {"networks/current-network", Constants::General::DefaultNetworkName}, + {"installation-id", installationId} + }; + } catch (std::bad_optional_access) { + return QJsonObject(); + } +} + +QJsonObject AccountsService::getAccountSettings(const QString& accountId, const QString& installationId, const QString &displayName) const +{ + for(const GeneratedAccountDto &acc : m_generatedAccounts) + + if(acc.id == accountId) + { + return AccountsService::prepareAccountSettingsJsonObject(acc, installationId, displayName); + } + + if(m_importedAccount.isValid()) + { + if(m_importedAccount.id == accountId) + { + return AccountsService::prepareAccountSettingsJsonObject(m_importedAccount, installationId, displayName); + } + } + + // TODO: Is this expected? Have proper error propagation, otherwise throw + qDebug() << "account not found"; + return QJsonObject(); +} + +QJsonArray getNodes(const QJsonObject& fleet, const QString& nodeType) +{ + auto nodes = fleet[nodeType].toObject(); + QJsonArray result; + for(auto it = nodes.begin(); it != nodes.end(); ++it) + result << *it; + return result; +} + +QJsonObject AccountsService::getDefaultNodeConfig(const QString& installationId) const +{ + try { + auto templateNodeConfigJsonStr = getDataFromFile(":/Status/StaticConfig/node-config.json").value(); + auto fleetJson = getDataFromFile(":/Status/StaticConfig/fleets.json").value(); + auto infuraKey = getDataFromFile(":/Status/StaticConfig/infura_key").value(); + + auto nodeConfigJsonStr = templateNodeConfigJsonStr.replace("%INSTALLATIONID%", installationId) + .replace("%INFURA_KEY%", infuraKey); + QJsonObject nodeConfigJson = QJsonDocument::fromJson(nodeConfigJsonStr.toUtf8()).object(); + QJsonObject clusterConfig = nodeConfigJson["ClusterConfig"].toObject(); + + QJsonObject fleetsJson = QJsonDocument::fromJson(fleetJson.toUtf8()).object()["fleets"].toObject(); + auto fleet = fleetsJson[Constants::Fleet::Prod].toObject(); + + clusterConfig["Fleet"] = Constants::Fleet::Prod; + clusterConfig["BootNodes"] = getNodes(fleet, Constants::FleetNodes::Bootnodes); + clusterConfig["TrustedMailServers"] = getNodes(fleet, Constants::FleetNodes::Mailservers); + clusterConfig["StaticNodes"] = getNodes(fleet, Constants::FleetNodes::Whisper); + clusterConfig["RendezvousNodes"] = getNodes(fleet, Constants::FleetNodes::Rendezvous); + clusterConfig["RelayNodes"] = getNodes(fleet, Constants::FleetNodes::Waku); + clusterConfig["StoreNodes"] = getNodes(fleet, Constants::FleetNodes::Waku); + clusterConfig["FilterNodes"] = getNodes(fleet, Constants::FleetNodes::Waku); + clusterConfig["LightpushNodes"] = getNodes(fleet, Constants::FleetNodes::Waku); + + nodeConfigJson["ClusterConfig"] = clusterConfig; + + return nodeConfigJson; + } catch (std::bad_optional_access) { + return QJsonObject(); + } +} + +} diff --git a/libs/Onboarding/src/Onboarding/Accounts/AccountsService.h b/libs/Onboarding/src/Onboarding/Accounts/AccountsService.h new file mode 100644 index 000000000..dc6b2d31b --- /dev/null +++ b/libs/Onboarding/src/Onboarding/Accounts/AccountsService.h @@ -0,0 +1,95 @@ +#pragma once + +#include "AccountsServiceInterface.h" + +namespace Status::Onboarding +{ + +/*! + * \brief The Service class + * + * \todo Refactor static dependencies + * :/resources/default-networks.json + * :/resources/node-config.json + * :/resources/fleets.json + * :/resources/infura_key + * \todo AccountsService + * \todo Consider removing unneded states (first time account login, user) + */ +class AccountsService : public AccountsServiceInterface +{ +public: + AccountsService(); + + /// \see ServiceInterface + bool init(const fs::path& statusgoDataDir) override; + + /// \see ServiceInterface + [[nodiscard]] std::vector openAndListAccounts() override; + + /// \see ServiceInterface + [[nodiscard]] const std::vector& generatedAccounts() const override; + + /// \see ServiceInterface + bool setupAccountAndLogin(const QString& accountId, const QString& password, const QString& displayName) override; + + /// \see ServiceInterface + [[nodiscard]] const AccountDto& getLoggedInAccount() const override; + + [[nodiscard]] const GeneratedAccountDto& getImportedAccount() const override; + + /// \see ServiceInterface + [[nodiscard]] bool isFirstTimeAccountLogin() const override; + + /// \see ServiceInterface + bool setKeyStoreDir(const QString &key) override; + + QString login(AccountDto account, const QString& password) override; + + void clear() override; + + QString generateAlias(const QString& publicKey) override; + + QString generateIdenticon(const QString& publicKey) override; + +private: + QJsonObject prepareAccountJsonObject(const GeneratedAccountDto& account, const QString& displayName) const; + + DerivedAccounts storeDerivedAccounts(const QString& accountId, const QString& hashedPassword, + const QVector& paths); + StoredAccountDto storeAccount(const QString& accountId, const QString& hashedPassword); + + AccountDto saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, + const QJsonArray& subaccounts, const QJsonObject& settings, + const QJsonObject& config); + + QJsonObject getAccountDataForAccountId(const QString& accountId, const QString& displayName) const; + + QJsonArray prepareSubaccountJsonObject(const GeneratedAccountDto& account, const QString& displayName) const; + + QJsonArray getSubaccountDataForAccountId(const QString& accountId, const QString& displayName) const; + + QString generateSigningPhrase(int count) const; + + QJsonObject prepareAccountSettingsJsonObject(const GeneratedAccountDto& account, + const QString& installationId, + const QString& displayName) const; + + QJsonObject getAccountSettings(const QString& accountId, const QString& installationId, const QString& displayName) const; + + QJsonObject getDefaultNodeConfig(const QString& installationId) const; + +private: + std::vector m_generatedAccounts; + + fs::path m_statusgoDataDir; + bool m_isFirstTimeAccountLogin; + // TODO: don't see the need for this state here + AccountDto m_loggedInAccount; + GeneratedAccountDto m_importedAccount; + + // Here for now. Extract them if used by other services + static constexpr auto m_keyStoreDirName = "keystore"; +}; + +} diff --git a/libs/Onboarding/src/Onboarding/Accounts/AccountsServiceInterface.h b/libs/Onboarding/src/Onboarding/Accounts/AccountsServiceInterface.h new file mode 100644 index 000000000..9ac2d454f --- /dev/null +++ b/libs/Onboarding/src/Onboarding/Accounts/AccountsServiceInterface.h @@ -0,0 +1,51 @@ +#pragma once + +#include "AccountDto.h" +#include "GeneratedAccountDto.h" + +#include + +namespace fs = std::filesystem; + +namespace Status::Onboarding +{ + +class AccountsServiceInterface +{ +public: + + virtual ~AccountsServiceInterface() = default; + + /// Generates and cache addresses accessible by \c generatedAccounts + virtual bool init(const fs::path& statusgoDataDir) = 0; + + /// opens database and returns accounts list. + [[nodiscard]] virtual std::vector openAndListAccounts() = 0; + + /// Retrieve cached accounts generated in \c init + [[nodiscard]] virtual const std::vector& generatedAccounts() const = 0; + + /// Configure an generated account. \a accountID must be sourced from \c generatedAccounts + virtual bool setupAccountAndLogin(const QString& accountID, const QString& password, const QString& displayName) = 0; + + /// Account that is currently logged-in + [[nodiscard]] virtual const AccountDto& getLoggedInAccount() const = 0; + + [[nodiscard]] virtual const GeneratedAccountDto& getImportedAccount() const = 0; + + /// Check if the login was never done in the current \c data directory + [[nodiscard]] virtual bool isFirstTimeAccountLogin() const = 0; + + /// Set and initializes the keystore directory. \see StatusGo::General::initKeystore + virtual bool setKeyStoreDir(const QString &key) = 0; + + virtual QString login(AccountDto account, const QString& password) = 0; + + virtual void clear() = 0; + + virtual QString generateAlias(const QString& publicKey) = 0; + + virtual QString generateIdenticon(const QString& publicKey) = 0; +}; + +} diff --git a/libs/Onboarding/src/Onboarding/Accounts/GeneratedAccountDto.h b/libs/Onboarding/src/Onboarding/Accounts/GeneratedAccountDto.h new file mode 100644 index 000000000..12c468bbf --- /dev/null +++ b/libs/Onboarding/src/Onboarding/Accounts/GeneratedAccountDto.h @@ -0,0 +1,141 @@ +#pragma once + +#include "Common/Constants.h" +#include "Common/SigningPhrases.h" +#include "Common/Json.h" + +#include + +namespace Status::Onboarding +{ + +struct DerivedAccountDetails +{ + QString publicKey; + QString address; + QString derivationPath; + + static DerivedAccountDetails toDerivedAccountDetails(const QJsonObject& jsonObj, const QString& derivationPath) + { + // Mapping this DTO is not strightforward since only keys are used for id. We + // handle it a bit different. + auto result = DerivedAccountDetails(); + + try + { + result.derivationPath = derivationPath; + result.publicKey = Json::getMandatoryProp(jsonObj, "publicKey")->toString(); + result.address = Json::getMandatoryProp(jsonObj, "address")->toString(); + } + catch (std::exception e) + { + qWarning() << QString("Mapping DerivedAccountDetails failed: %1").arg(e.what()); + } + + return result; + } +}; + +struct DerivedAccounts +{ + DerivedAccountDetails whisper; + DerivedAccountDetails walletRoot; + DerivedAccountDetails defaultWallet; + DerivedAccountDetails eip1581; + + static DerivedAccounts toDerivedAccounts(const QJsonObject& jsonObj) + { + auto result = DerivedAccounts(); + + for(const auto &derivationPath : jsonObj.keys()) + { + auto derivedObj = jsonObj.value(derivationPath).toObject(); + if(derivationPath == Constants::General::PathWhisper) + { + result.whisper = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); + } + else if(derivationPath == Constants::General::PathWalletRoot) + { + result.walletRoot = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); + } + else if(derivationPath == Constants::General::PathDefaultWallet) + { + result.defaultWallet = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); + } + else if(derivationPath == Constants::General::PathEIP1581) + { + result.eip1581 = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); + } + } + + return result; + } +}; + +struct StoredAccountDto +{ + QString publicKey; + QString address; + +}; + +static StoredAccountDto toStoredAccountDto(const QJsonObject& jsonObj) +{ + auto result = StoredAccountDto(); + + try { + result.address = Json::getMandatoryProp(jsonObj, "address")->toString(); + result.publicKey = Json::getMandatoryProp(jsonObj, "publicKey")->toString(); + } catch (std::exception e) { + qWarning() << QString("Mapping StoredAccountDto failed: %1").arg(e.what()); + } + + return result; +} + +struct GeneratedAccountDto +{ + QString id; + QString publicKey; + QString address; + QString keyUid; + QString mnemonic; + DerivedAccounts derivedAccounts; + + // The following two are set additionally. + QString alias; + QString identicon; + + bool isValid() const + { + return !(id.isEmpty() || publicKey.isEmpty() || address.isEmpty() || keyUid.isEmpty()); + } + + static GeneratedAccountDto toGeneratedAccountDto(const QJsonObject& jsonObj) + { + auto result = GeneratedAccountDto(); + + try + { + result.id = Json::getMandatoryProp(jsonObj, "id")->toString(); + result.address = Json::getMandatoryProp(jsonObj, "address")->toString(); + result.keyUid = Json::getMandatoryProp(jsonObj, "keyUid")->toString(); + result.mnemonic = Json::getMandatoryProp(jsonObj, "mnemonic")->toString(); + result.publicKey = Json::getMandatoryProp(jsonObj, "publicKey")->toString(); + + auto derivedObj = Json::getProp(jsonObj, "derived")->toObject(); + if(!derivedObj.isEmpty()) + { + result.derivedAccounts = DerivedAccounts::toDerivedAccounts(derivedObj); + } + } + catch (std::exception e) + { + qWarning() << QString("Mapping GeneratedAccountDto failed: %1").arg(e.what()); + } + + return result; + } +}; + +} diff --git a/libs/Onboarding/src/Onboarding/CMakeLists.txt b/libs/Onboarding/src/Onboarding/CMakeLists.txt new file mode 100644 index 000000000..c65abf4cb --- /dev/null +++ b/libs/Onboarding/src/Onboarding/CMakeLists.txt @@ -0,0 +1,26 @@ +target_sources(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/NewAccountController.h + ${CMAKE_CURRENT_SOURCE_DIR}/OnboardingController.h + ${CMAKE_CURRENT_SOURCE_DIR}/OnboardingModule.h + ${CMAKE_CURRENT_SOURCE_DIR}/UserAccount.h + ${CMAKE_CURRENT_SOURCE_DIR}/UserAccountsModel.h + + ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/AccountDto.h + ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/GeneratedAccountDto.h + ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/AccountsService.h + ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/AccountsServiceInterface.h + + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/NewAccountController.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/OnboardingController.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/OnboardingModule.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/UserAccount.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/UserAccountsModel.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/AccountsService.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/Common/Constants.h + ${CMAKE_CURRENT_SOURCE_DIR}/Common/Json.h + ${CMAKE_CURRENT_SOURCE_DIR}/Common/SigningPhrases.h +) diff --git a/libs/Onboarding/src/Onboarding/Common/Constants.h b/libs/Onboarding/src/Onboarding/Common/Constants.h new file mode 100644 index 000000000..11a3f2dc8 --- /dev/null +++ b/libs/Onboarding/src/Onboarding/Common/Constants.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +namespace Status::Constants +{ + +namespace Fleet +{ + inline const auto Prod = u"eth.prod"_qs; + inline const auto Staging = u"eth.staging"_qs; + inline const auto Test = u"eth.test"_qs; + inline const auto WakuV2Prod = u"wakuv2.prod"_qs; + inline const auto WakuV2Test = u"wakuv2.test"_qs; + inline const auto GoWakuTest = u"go-waku.test"_qs; +} + +namespace FleetNodes +{ + inline const auto Bootnodes = u"boot"_qs; + inline const auto Mailservers = u"mail"_qs; + inline const auto Rendezvous = u"rendezvous"_qs; + inline const auto Whisper = u"whisper"_qs; + inline const auto Waku = u"waku"_qs; + inline const auto LibP2P = u"libp2p"_qs; + inline const auto Websocket = u"websocket"_qs; +} + +namespace General +{ + inline const auto DefaultNetworkName = u"mainnet_rpc"_qs; + //const DEFAULT_NETWORKS_IDS* = @["mainnet_rpc", "testnet_rpc", "rinkeby_rpc", "goerli_rpc", "xdai_rpc", "poa_rpc" ] + + inline const auto ZeroAddress = u"0x0000000000000000000000000000000000000000"_qs; + + inline const auto PathWalletRoot = u"m/44'/60'/0'/0"_qs; + // EIP1581 Root Key, the extended key from which any whisper key/encryption key can be derived + inline const auto PathEIP1581 = u"m/43'/60'/1581'"_qs; + // BIP44-0 Wallet key, the default wallet key + inline const auto PathDefaultWallet = PathWalletRoot + u"/0"_qs; + // EIP1581 Chat Key 0, the default whisper key + inline const auto PathWhisper = PathEIP1581 + u"/0'/0"_qs; + + inline const QVector AccountDefaultPaths {PathWalletRoot, PathEIP1581, PathWhisper, PathDefaultWallet}; +} + +} diff --git a/libs/Onboarding/src/Onboarding/Common/Json.h b/libs/Onboarding/src/Onboarding/Common/Json.h new file mode 100644 index 000000000..dae72fde3 --- /dev/null +++ b/libs/Onboarding/src/Onboarding/Common/Json.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace Status +{ + +class Json +{ +public: + /// TODO: refactor to get std::optional or rename it to get*It + static QJsonObject::const_iterator getProp(const QJsonObject& object, const QString& field) + { + const auto it = object.constFind(field); + return it; + } + + /// TODO: refactor to get QJsonObject + static QJsonObject::const_iterator getMandatoryProp(const QJsonObject& object, const QString& field) + { + const auto it = getProp(object, field); + + if (it == object.constEnd()) + { + throw std::logic_error(QString("No field `%1`").arg(field).toStdString()); + } + + return it; + } +}; + +} diff --git a/libs/Onboarding/src/Onboarding/Common/SigningPhrases.h b/libs/Onboarding/src/Onboarding/Common/SigningPhrases.h new file mode 100644 index 000000000..b5889f4e9 --- /dev/null +++ b/libs/Onboarding/src/Onboarding/Common/SigningPhrases.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +namespace Status::Constants +{ + +constexpr std::array SigningPhrases { + "area", "army", "atom", "aunt", "babe", "baby", "back", "bail", "bait", "bake", "ball", "band", "bank", "barn", + "base", "bass", "bath", "bead", "beak", "beam", "bean", "bear", "beat", "beef", "beer", "beet", "bell", "belt", + "bend", "bike", "bill", "bird", "bite", "blow", "blue", "boar", "boat", "body", "bolt", "bomb", "bone", "book", + "boot", "bore", "boss", "bowl", "brow", "bulb", "bull", "burn", "bush", "bust", "cafe", "cake", "calf", "call", + "calm", "camp", "cane", "cape", "card", "care", "carp", "cart", "case", "cash", "cast", "cave", "cell", "cent", + "chap", "chef", "chin", "chip", "chop", "chub", "chug", "city", "clam", "clef", "clip", "club", "clue", "coal", + "coat", "code", "coil", "coin", "coke", "cold", "colt", "comb", "cone", "cook", "cope", "copy", "cord", "cork", + "corn", "cost", "crab", "craw", "crew", "crib", "crop", "crow", "curl", "cyst", "dame", "dare", "dark", "dart", + "dash", "data", "date", "dead", "deal", "dear", "debt", "deck", "deep", "deer", "desk", "dhow", "diet", "dill", + "dime", "dirt", "dish", "disk", "dock", "doll", "door", "dory", "drag", "draw", "drop", "drug", "drum", "duck", + "dump", "dust", "duty", "ease", "east", "eave", "eddy", "edge", "envy", "epee", "exam", "exit", "face", "fact", + "fail", "fall", "fame", "fang", "farm", "fawn", "fear", "feed", "feel", "feet", "file", "fill", "film", "find", + "fine", "fire", "fish", "flag", "flat", "flax", "flow", "foam", "fold", "font", "food", "foot", "fork", "form", + "fort", "fowl", "frog", "fuel", "full", "gain", "gale", "galn", "game", "garb", "gate", "gear", "gene", "gift", + "girl", "give", "glad", "glen", "glue", "glut", "goal", "goat", "gold", "golf", "gong", "good", "gown", "grab", + "gram", "gray", "grey", "grip", "grit", "gyro", "hail", "hair", "half", "hall", "hand", "hang", "harm", "harp", + "hate", "hawk", "head", "heat", "heel", "hell", "helo", "help", "hemp", "herb", "hide", "high", "hill", "hire", + "hive", "hold", "hole", "home", "hood", "hoof", "hook", "hope", "hops", "horn", "hose", "host", "hour", "hunt", + "hurt", "icon", "idea", "inch", "iris", "iron", "item", "jail", "jeep", "jeff", "joey", "join", "joke", "judo", + "jump", "junk", "jury", "jute", "kale", "keep", "kick", "kill", "kilt", "kind", "king", "kiss", "kite", "knee", + "knot", "lace", "lack", "lady", "lake", "lamb", "lamp", "land", "lark", "lava", "lawn", "lead", "leaf", "leek", + "lier", "life", "lift", "lily", "limo", "line", "link", "lion", "lisa", "list", "load", "loaf", "loan", "lock", + "loft", "long", "look", "loss", "lout", "love", "luck", "lung", "lute", "lynx", "lyre", "maid", "mail", "main", + "make", "male", "mall", "manx", "many", "mare", "mark", "mask", "mass", "mate", "math", "meal", "meat", "meet", + "menu", "mess", "mice", "midi", "mile", "milk", "mime", "mind", "mine", "mini", "mint", "miss", "mist", "moat", + "mode", "mole", "mood", "moon", "most", "moth", "move", "mule", "mutt", "nail", "name", "neat", "neck", "need", + "neon", "nest", "news", "node", "nose", "note", "oboe", "okra", "open", "oval", "oven", "oxen", "pace", "pack", + "page", "pail", "pain", "pair", "palm", "pard", "park", "part", "pass", "past", "path", "peak", "pear", "peen", + "peer", "pelt", "perp", "pest", "pick", "pier", "pike", "pile", "pimp", "pine", "ping", "pink", "pint", "pipe", + "piss", "pith", "plan", "play", "plot", "plow", "poem", "poet", "pole", "polo", "pond", "pony", "poof", "pool", + "port", "post", "prow", "pull", "puma", "pump", "pupa", "push", "quit", "race", "rack", "raft", "rage", "rail", + "rain", "rake", "rank", "rate", "read", "rear", "reef", "rent", "rest", "rice", "rich", "ride", "ring", "rise", + "risk", "road", "robe", "rock", "role", "roll", "roof", "room", "root", "rope", "rose", "ruin", "rule", "rush", + "ruth", "sack", "safe", "sage", "sail", "sale", "salt", "sand", "sari", "sash", "save", "scow", "seal", "seat", + "seed", "self", "sell", "shed", "shin", "ship", "shoe", "shop", "shot", "show", "sick", "side", "sign", "silk", + "sill", "silo", "sing", "sink", "site", "size", "skin", "sled", "slip", "smog", "snob", "snow", "soap", "sock", + "soda", "sofa", "soft", "soil", "song", "soot", "sort", "soup", "spot", "spur", "stag", "star", "stay", "stem", + "step", "stew", "stop", "stud", "suck", "suit", "swan", "swim", "tail", "tale", "talk", "tank", "tard", "task", + "taxi", "team", "tear", "teen", "tell", "temp", "tent", "term", "test", "text", "thaw", "tile", "till", "time", + "tire", "toad", "toga", "togs", "tone", "tool", "toot", "tote", "tour", "town", "tram", "tray", "tree", "trim", + "trip", "tuba", "tube", "tuna", "tune", "turn", "tutu", "twig", "type", "unit", "user", "vane", "vase", "vast", + "veal", "veil", "vein", "vest", "vibe", "view", "vise", "wait", "wake", "walk", "wall", "wash", "wasp", "wave", + "wear", "weed", "week", "well", "west", "whip", "wife", "will", "wind", "wine", "wing", "wire", "wish", "wolf", + "wood", "wool", "word", "work", "worm", "wrap", "wren", "yard", "yarn", "yawl", "year", "yoga", "yoke", "yurt", + "zinc", "zone" +}; + +} diff --git a/libs/Onboarding/src/Onboarding/NewAccountController.cpp b/libs/Onboarding/src/Onboarding/NewAccountController.cpp new file mode 100644 index 000000000..3a89393d6 --- /dev/null +++ b/libs/Onboarding/src/Onboarding/NewAccountController.cpp @@ -0,0 +1,138 @@ +#include "NewAccountController.h" + +#include "Accounts/AccountsService.h" + +#include + +#include + +namespace Status::Onboarding +{ + +namespace StatusGo = Status::StatusGo; + +NewAccountController::NewAccountController(std::shared_ptr accountsService, QObject *parent) + // TODO: remove dev dev setup after the final implementation + : m_name("TestAccount") + , m_nameIsValid(true) + , m_password("1234567890") + , m_passwordIsValid(true) + , m_confirmationPassword("1234567890") + , m_confirmationPasswordIsValid(true) + // END dev setup + , m_accountsService(accountsService) +{ + connect(this, &NewAccountController::passwordChanged, this, &NewAccountController::checkAndUpdateDataValidity); + connect(this, &NewAccountController::confirmationPasswordChanged, this, &NewAccountController::checkAndUpdateDataValidity); + connect(this, &NewAccountController::nameChanged, this, &NewAccountController::checkAndUpdateDataValidity); +} + +void NewAccountController::createAccount() +{ + // TODO: fix this after moving SingalManager to StatusGo wrapper lib + QObject::connect(StatusGo::SignalsManager::instance(), &StatusGo::SignalsManager::nodeLogin, this, &NewAccountController::onNodeLogin); + + auto setupAccountFn = [this]() { + if(m_nameIsValid && m_passwordIsValid && m_confirmationPasswordIsValid) { + auto genAccounts = m_accountsService->generatedAccounts(); + if(genAccounts.size() > 0) { + if(m_accountsService->setupAccountAndLogin(genAccounts[0].id, m_password, m_name)) + return; + } + } + }; + // TODO: refactor StatusGo wrapper to work with futures instead of SignalManager + m_createAccountFuture = QtConcurrent::run(setupAccountFn) + .then([]{ /*Nothing, we expect status-go events*/ }) + .onFailed([this] { + emit accountCreationError(); + }) + .onCanceled([this] { + emit accountCreationError(); + }); +} + +const QString &NewAccountController::password() const +{ + return m_password; +} + +void NewAccountController::setPassword(const QString &newPassword) +{ + if (m_password == newPassword) + return; + m_password = newPassword; + emit passwordChanged(); +} + +const QString &NewAccountController::confirmationPassword() const +{ + return m_confirmationPassword; +} + +void NewAccountController::setConfirmationPassword(const QString &newConfirmationPassword) +{ + if (m_confirmationPassword == newConfirmationPassword) + return; + m_confirmationPassword = newConfirmationPassword; + emit confirmationPasswordChanged(); +} + +const QString &NewAccountController::name() const +{ + return m_name; +} + +void NewAccountController::setName(const QString &newName) +{ + if (m_name == newName) + return; + m_name = newName; + emit nameChanged(); +} + +bool NewAccountController::passwordIsValid() const +{ + return m_passwordIsValid; +} + +bool NewAccountController::confirmationPasswordIsValid() const +{ + return m_confirmationPasswordIsValid; +} + +bool NewAccountController::nameIsValid() const +{ + return m_nameIsValid; +} + +void NewAccountController::onNodeLogin(const QString& error) +{ + if(error.isEmpty()) + emit accountCreatedAndLoggedIn(); + else + emit accountCreationError(); +} + +void NewAccountController::checkAndUpdateDataValidity() +{ + auto passwordValid = m_password.length() >= 6; + if(passwordValid != m_passwordIsValid) { + m_passwordIsValid = passwordValid; + emit passwordIsValidChanged(); + } + + auto confirmationPasswordValid = m_password == m_confirmationPassword; + if(confirmationPasswordValid != m_confirmationPasswordIsValid) { + m_confirmationPasswordIsValid = confirmationPasswordValid; + emit confirmationPasswordIsValidChanged(); + } + + auto nameValid = m_name.length() >= 10; + if(nameValid != m_nameIsValid) { + m_nameIsValid = nameValid; + emit nameIsValidChanged(); + } +} + +} // namespace Status::Onboarding diff --git a/libs/Onboarding/src/Onboarding/NewAccountController.h b/libs/Onboarding/src/Onboarding/NewAccountController.h new file mode 100644 index 000000000..ea2e26d3a --- /dev/null +++ b/libs/Onboarding/src/Onboarding/NewAccountController.h @@ -0,0 +1,84 @@ +#pragma once + +#include "UserAccountsModel.h" + +#include "Accounts/AccountsServiceInterface.h" +#include "Accounts/AccountDto.h" + +#include + +#include + +#include + +namespace Status::Onboarding +{ + +class ServiceInterface; + +/*! \brief presentation layer for creation of a new account workflow + * + * \todo shared functionality should be moved to Common library (e.g. Name/Picture Validation) + */ +class NewAccountController: public QObject +{ + Q_OBJECT + + QML_ELEMENT + QML_UNCREATABLE("Created and owned externally") + + Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged) + Q_PROPERTY(QString confirmationPassword READ confirmationPassword WRITE setConfirmationPassword NOTIFY confirmationPasswordChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(bool passwordIsValid READ passwordIsValid NOTIFY passwordIsValidChanged) + Q_PROPERTY(bool confirmationPasswordIsValid READ confirmationPasswordIsValid NOTIFY confirmationPasswordIsValidChanged) + Q_PROPERTY(bool nameIsValid READ nameIsValid NOTIFY nameIsValidChanged) + +public: + explicit NewAccountController(std::shared_ptr accountsService, QObject* parent = nullptr); + + Q_INVOKABLE void createAccount(); + + const QString &password() const; + void setPassword(const QString &newPassword); + + const QString &confirmationPassword() const; + void setConfirmationPassword(const QString &newConfirmationPassword); + + const QString &name() const; + void setName(const QString &newName); + + bool passwordIsValid() const; + bool confirmationPasswordIsValid() const; + bool nameIsValid() const; + +signals: + void passwordChanged(); + void confirmationPasswordChanged(); + void nameChanged(); + void nameIsValidChanged(); + void passwordIsValidChanged(); + void confirmationPasswordIsValidChanged(); + + void accountCreatedAndLoggedIn(); + void accountCreationError(); + +private slots: + void onNodeLogin(const QString& error); + +private: + void checkAndUpdateDataValidity(); + + QString m_password; + QString m_confirmationPassword; + QString m_name; + + bool m_passwordIsValid = false; + bool m_confirmationPasswordIsValid; + bool m_nameIsValid = false; + + std::shared_ptr m_accountsService; + QFuture m_createAccountFuture; +}; + +} // namespace Status::Onboarding diff --git a/libs/Onboarding/src/Onboarding/OnboardingController.cpp b/libs/Onboarding/src/Onboarding/OnboardingController.cpp new file mode 100644 index 000000000..2a35642d1 --- /dev/null +++ b/libs/Onboarding/src/Onboarding/OnboardingController.cpp @@ -0,0 +1,77 @@ +#include "OnboardingController.h" + +#include "NewAccountController.h" +#include "UserAccount.h" + +#include + +namespace Status::Onboarding { + +namespace StatusGo = Status::StatusGo; + +OnboardingController::OnboardingController(std::shared_ptr accountsService) + : QObject(nullptr) + , m_accountsService(std::move(accountsService)) +{ + { // Init accounts + std::vector> accounts; + for(auto &account : getOpenedAccounts()) { + accounts.push_back(std::make_shared(std::make_unique(std::move(account)))); + } + m_accounts = std::make_shared(std::move(accounts)); + } + + connect(StatusGo::SignalsManager::instance(), &StatusGo::SignalsManager::nodeLogin, this, &OnboardingController::onLogin); +} + +OnboardingController::~OnboardingController() +{ + // Here to move instatiation of unique_ptrs into this compilation unit +} + +void OnboardingController::onLogin(const QString& error) +{ + if(error.isEmpty()) + emit accountLoggedIn(); + else + emit accountLoginError(error); +} + +std::vector OnboardingController::getOpenedAccounts() const +{ + return m_accountsService->openAndListAccounts(); +} + +void OnboardingController::login(QObject* user, const QString& password) +{ + auto account = qobject_cast(user); + assert(account != nullptr); + auto error = m_accountsService->login(account->accountData(), password); + if(!error.isEmpty()) + emit accountLoginError(error); +} + +UserAccountsModel *OnboardingController::accounts() const +{ + return m_accounts.get(); +} + +NewAccountController *OnboardingController::initNewAccountController() +{ + m_newAccountController = std::make_unique(m_accountsService); + emit newAccountControllerChanged(); + return m_newAccountController.get(); +} + +void OnboardingController::terminateNewAccountController() +{ + m_newAccountController.release()->deleteLater(); + emit newAccountControllerChanged(); +} + +NewAccountController *OnboardingController::newAccountController() const +{ + return m_newAccountController.get(); +} + +} diff --git a/libs/Onboarding/src/Onboarding/OnboardingController.h b/libs/Onboarding/src/Onboarding/OnboardingController.h new file mode 100644 index 000000000..87bd66e4c --- /dev/null +++ b/libs/Onboarding/src/Onboarding/OnboardingController.h @@ -0,0 +1,75 @@ +#pragma once + +#include "UserAccountsModel.h" + +#include "Accounts/AccountsServiceInterface.h" +#include "Accounts/AccountDto.h" + +#include +#include + +#include + +namespace Status::Onboarding +{ + +class UserAccount; + +class NewAccountController; + +/*! + * \todo refactor and remove the requirement to build only shared_ptr instances or use a factory + * \todo refactor unnedded multiple inheritance + * \todo don't use DTOs in controllers, use QObjects directly + * \todo make dependency on SignalManager explicit. Now it is hidden. + */ +class OnboardingController final : public QObject + , public std::enable_shared_from_this +{ + Q_OBJECT + + QML_ELEMENT + QML_UNCREATABLE("Created by Module, for now") + + Q_PROPERTY(UserAccountsModel* accounts READ accounts CONSTANT) + Q_PROPERTY(NewAccountController* newAccountController READ newAccountController NOTIFY newAccountControllerChanged) + +public: + explicit OnboardingController(std::shared_ptr accountsService); + ~OnboardingController(); + + /// Retrieve available accounts + std::vector getOpenedAccounts() const; + + /// Login user account + /// TODO: \a user should be of type \c UserAccount but this doesn't work with Qt6 CMake API. Investigate and fix later on + Q_INVOKABLE void login(QObject* user, const QString& password); + + UserAccountsModel *accounts() const; + + Q_INVOKABLE NewAccountController *initNewAccountController(); + Q_INVOKABLE void terminateNewAccountController(); + NewAccountController *newAccountController() const; + +signals: + void accountLoggedIn(); + void accountLoginError(const QString& error); + void obtainingPasswordError(const QString& errorDescription); + void obtainingPasswordSuccess(const QString& password); + + void newAccountControllerChanged(); + +private slots: + void onLogin(const QString& error); + +private: + const UserAccount* getSelectedAccount() const; + +private: + std::shared_ptr m_accountsService; + std::shared_ptr m_accounts; + + std::unique_ptr m_newAccountController; +}; + +} diff --git a/libs/Onboarding/src/Onboarding/OnboardingModule.cpp b/libs/Onboarding/src/Onboarding/OnboardingModule.cpp new file mode 100644 index 000000000..4c4da2a5f --- /dev/null +++ b/libs/Onboarding/src/Onboarding/OnboardingModule.cpp @@ -0,0 +1,65 @@ +#include "OnboardingModule.h" + +#include "Accounts/AccountsService.h" + +#include + + +namespace AppCore = Status::ApplicationCore; + +namespace fs = std::filesystem; + +namespace Status::Onboarding { + +OnboardingModule::OnboardingModule(const fs::path& userDataPath, QObject *parent) + : OnboardingModule{parent} +{ + m_userDataPath = userDataPath; + initWithUserDataPath(m_userDataPath); +} + +OnboardingModule::OnboardingModule(QObject *parent) + : QObject{parent} + , m_accountsService(std::make_shared()) +{ +} + +OnboardingController* OnboardingModule::controller() const +{ + return m_controller.get(); +} + +void OnboardingModule::componentComplete() +{ + try { + initWithUserDataPath(m_userDataPath); + } catch(const std::exception &e) { + qCritical() << "OnboardingModule: failed to initialize"; + } +} + +void OnboardingModule::initWithUserDataPath(const fs::path &path) +{ + auto result = m_accountsService->init(path); + if(!result) + throw std::runtime_error(std::string("Failed to initialize OnboadingService") + path.string()); + m_controller = std::make_shared( + m_accountsService); + emit controllerChanged(); +} + +const QString OnboardingModule::userDataPath() const +{ + return QString::fromStdString(m_userDataPath.string()); +} + +void OnboardingModule::setUserDataPath(const QString &newUserDataPath) +{ + auto newVal = newUserDataPath.toStdString(); + if (m_userDataPath.compare(newVal) == 0) + return; + m_userDataPath = newVal; + emit userDataPathChanged(); +} + +} diff --git a/libs/Onboarding/src/Onboarding/OnboardingModule.h b/libs/Onboarding/src/Onboarding/OnboardingModule.h new file mode 100644 index 000000000..ae7fbee1b --- /dev/null +++ b/libs/Onboarding/src/Onboarding/OnboardingModule.h @@ -0,0 +1,62 @@ +#pragma once + +#include "OnboardingController.h" + +#include +#include + +#include + +namespace fs = std::filesystem; + +namespace Status::Onboarding { + +class AccountsService; + +/*! + * \brief Provide bootstrap of controllers and corresponding services + * + * \warning status-go is a stateful library and having multiple insteances of the same module is undefined behaviour + * \todo current state is temporary until refactor StatusGo wrapper to match status-go requirements + * \warning current state all module spawned/controlled objects have C++ ownership this generate the risk of dangling + * invalid QML objects after module is destroyed. Consider moving all the ownership into QML after refactoring + */ +class OnboardingModule : public QObject, public QQmlParserStatus +{ + Q_OBJECT + QML_ELEMENT + Q_INTERFACES(QQmlParserStatus) + + Q_PROPERTY(OnboardingController* controller READ controller NOTIFY controllerChanged) + Q_PROPERTY(QString userDataPath READ userDataPath WRITE setUserDataPath NOTIFY userDataPathChanged REQUIRED) + +public: + explicit OnboardingModule(const fs::path& userDataPath, QObject *parent = nullptr); + explicit OnboardingModule(QObject *parent = nullptr); + + OnboardingController* controller() const; + + const QString userDataPath() const; + void setUserDataPath(const QString &newUserDataPath); + + /// QML inteface + void classBegin() override {}; + void componentComplete() override; + +signals: + void controllerChanged(); + void userDataPathChanged(); + +private: + + /// Throws exceptions + void initWithUserDataPath(const fs::path &path); + + // TODO: plain object after refactoring shared_ptr requirement for now + std::shared_ptr m_accountsService; + std::shared_ptr m_controller; + + fs::path m_userDataPath; +}; + +} diff --git a/libs/Onboarding/src/Onboarding/TODO.md b/libs/Onboarding/src/Onboarding/TODO.md new file mode 100644 index 000000000..b7d823e68 --- /dev/null +++ b/libs/Onboarding/src/Onboarding/TODO.md @@ -0,0 +1,9 @@ +# Onboarding refactoring + +TODO + +- [ ] Consider moving path requirements, into `StatusGoQt` or unify them as module requirement through abstraction +- [ ] Refactor to use typed IDs across Account and Login services instead of plain strings. + - A quick workaround would be to add a generic NamedType and convert strings at status-go APIs boundaries +- [ ] Bring uniformity to namespace: `Status::`. Don't go too deep, not deeper than two domain-related namespaces +- [ ] Consider RAII for controllers, remove `init` diff --git a/libs/Onboarding/src/Onboarding/UserAccount.cpp b/libs/Onboarding/src/Onboarding/UserAccount.cpp new file mode 100644 index 000000000..be4541311 --- /dev/null +++ b/libs/Onboarding/src/Onboarding/UserAccount.cpp @@ -0,0 +1,37 @@ +#include "UserAccount.h" + +#include "Accounts/AccountDto.h" + + +namespace Status::Onboarding +{ + +UserAccount::UserAccount(std::unique_ptr data) + : QObject() + , m_data(std::move(data)) +{ + +} + +const QString &UserAccount::name() const +{ + return m_data->name; +} + +const AccountDto &UserAccount::accountData() const +{ + return *m_data; +} + +void UserAccount::updateAccountData(const AccountDto& newData) +{ + std::vector> notifyUpdates; + + *m_data = newData; + + if(newData.name != m_data->name) + notifyUpdates.push_back([this]() { emit nameChanged(); }); + +} + +} diff --git a/libs/Onboarding/src/Onboarding/UserAccount.h b/libs/Onboarding/src/Onboarding/UserAccount.h new file mode 100644 index 000000000..1a2fafaff --- /dev/null +++ b/libs/Onboarding/src/Onboarding/UserAccount.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace Status::Onboarding +{ + +class AccountDto; + +/*! + * \brief Represents a user account in Onboarding Presentation Layer + * + * @see OnboardingController + * @see UserAccountsModel + */ +class UserAccount: public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("Created by Controller") + + Q_PROPERTY(QString name READ name NOTIFY nameChanged) +public: + explicit UserAccount(std::unique_ptr data); + + const QString &name() const; + + const AccountDto& accountData() const; + void updateAccountData(const AccountDto& newData); + +signals: + void nameChanged(); + +private: + std::unique_ptr m_data; +}; + +} diff --git a/libs/Onboarding/src/Onboarding/UserAccountsModel.cpp b/libs/Onboarding/src/Onboarding/UserAccountsModel.cpp new file mode 100644 index 000000000..b025afd98 --- /dev/null +++ b/libs/Onboarding/src/Onboarding/UserAccountsModel.cpp @@ -0,0 +1,44 @@ +#include "UserAccountsModel.h" + +#include + +namespace Status::Onboarding { + + +UserAccountsModel::UserAccountsModel(const std::vector> accounts, QObject* parent) + : QAbstractListModel(parent) + , m_accounts(std::move(accounts)) +{ +} + +UserAccountsModel::~UserAccountsModel() +{ +} + +QHash UserAccountsModel::roleNames() const +{ + static QHash roles{ + {Name, "name"}, + {Account, "account"} + }; + return roles; +} + +int UserAccountsModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent) + return m_accounts.size(); +} + +QVariant UserAccountsModel::data(const QModelIndex& index, int role) const +{ + if(!QAbstractItemModel::checkIndex(index)) + return QVariant(); + + switch(static_cast(role)) { + case Name: return QVariant::fromValue(m_accounts[index.row()].get()->name()); + case Account: return QVariant::fromValue(m_accounts[index.row()].get()); + } +} + +} diff --git a/libs/Onboarding/src/Onboarding/UserAccountsModel.h b/libs/Onboarding/src/Onboarding/UserAccountsModel.h new file mode 100644 index 000000000..b1f21593b --- /dev/null +++ b/libs/Onboarding/src/Onboarding/UserAccountsModel.h @@ -0,0 +1,36 @@ +#pragma once + +#include "UserAccount.h" + +#include +#include + +namespace Status::Onboarding { + +/*! + * \brief Available UserAccount elements + */ +class UserAccountsModel : public QAbstractListModel +{ + Q_OBJECT + + QML_ELEMENT + QML_UNCREATABLE("Created by OnboardingController") + + enum ModelRole { + Name = Qt::UserRole + 1, + Account + }; +public: + + explicit UserAccountsModel(const std::vector> accounts, QObject* parent = nullptr); + ~UserAccountsModel(); + QHash roleNames() const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role) const override; + +private: + const std::vector> m_accounts; +}; + +} diff --git a/libs/Onboarding/tests/CMakeLists.txt b/libs/Onboarding/tests/CMakeLists.txt new file mode 100644 index 000000000..5445ad9be --- /dev/null +++ b/libs/Onboarding/tests/CMakeLists.txt @@ -0,0 +1,54 @@ +# Unit and interface tests +cmake_minimum_required(VERSION 3.21) + +project(TestOnboarding VERSION 0.1.0 LANGUAGES CXX) + +set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) + +find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Core REQUIRED) +qt6_standard_project_setup() + +find_package(GTest REQUIRED) + +enable_testing() + +add_executable(${PROJECT_NAME} + test_AccountService.cpp + test_OnboardingController.cpp + test_OnboardingModule.cpp + + ServiceMock.h +) + +target_include_directories(${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_subdirectory(OnboardingTestHelpers) +add_subdirectory(qml_tests) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + Qt6::Core + + GTest::gtest + GTest::gmock + GTest::gtest_main + + Status::TestHelpers + Status::ApplicationCore + + Status::OnboardingTestHelpers + Status::Onboarding + + # TODO tmp + Status::StatusGoQt +) + + +include(GoogleTest) +gtest_add_tests( + TARGET ${PROJECT_NAME} + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} +) diff --git a/libs/Onboarding/tests/OnboardingTestHelpers/CMakeLists.txt b/libs/Onboarding/tests/OnboardingTestHelpers/CMakeLists.txt new file mode 100644 index 000000000..d80bfa613 --- /dev/null +++ b/libs/Onboarding/tests/OnboardingTestHelpers/CMakeLists.txt @@ -0,0 +1,50 @@ +# Base library. Expect most of the module libraries to depend on it +# +cmake_minimum_required(VERSION 3.21) + +project(OnboardingTestHelpers + VERSION 0.1.0 + LANGUAGES CXX) + +set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) + +find_package(GTest REQUIRED) + +find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml REQUIRED) +qt6_standard_project_setup() + +qt6_add_qml_module(${PROJECT_NAME} + URI Status.${PROJECT_NAME} + VERSION 1.0 +) +add_library(Status::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_sources(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/ScopedTestAccount.h + ${CMAKE_CURRENT_SOURCE_DIR}/Constants.h + + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/ScopedTestAccount.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt +) + +target_link_libraries(${PROJECT_NAME} + PUBLIC + Qt6::Quick + Qt6::Qml + + PRIVATE + Status::TestHelpers + Status::ApplicationCore + Status::Onboarding + Status::StatusGoQt + + GTest::gtest +) diff --git a/libs/Onboarding/tests/OnboardingTestHelpers/Constants.h b/libs/Onboarding/tests/OnboardingTestHelpers/Constants.h new file mode 100644 index 000000000..c2a858a67 --- /dev/null +++ b/libs/Onboarding/tests/OnboardingTestHelpers/Constants.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace Status::Testing::Constants { + +inline constexpr auto userDataDirName = "StatusTest"; +inline constexpr auto statusGoDataDirName = "data"; +inline constexpr auto tmpDataDirName = "tmp"; +inline constexpr auto logsDataDirName = "logs"; +inline constexpr auto qtDataDirName = "qt"; +inline constexpr auto keystoreDataDirName = "keystore"; +inline constexpr auto globalSettingsFileName = "global"; + +} diff --git a/libs/Onboarding/tests/OnboardingTestHelpers/ScopedTestAccount.cpp b/libs/Onboarding/tests/OnboardingTestHelpers/ScopedTestAccount.cpp new file mode 100644 index 000000000..b8cb7e777 --- /dev/null +++ b/libs/Onboarding/tests/OnboardingTestHelpers/ScopedTestAccount.cpp @@ -0,0 +1,119 @@ +#include "ScopedTestAccount.h" + +#include + +#include + +#include +#include + +#include + +#include + +#include + +namespace Testing = Status::Testing; +namespace Onboarding = Status::Onboarding; + +namespace fs = std::filesystem; + +namespace Status::Testing { + +ScopedTestAccount::ScopedTestAccount(const std::string &tempTestSubfolderName, const QString &accountName, const QString &accountPassword, bool ignorePreviousState) + : m_fusedTestFolder{std::make_unique(tempTestSubfolderName)} + , m_accountName(accountName) + , m_accountPassword(accountPassword) +{ + int argc = 1; + std::string appName{"test"}; + char* args[] = {appName.data()}; + m_app = std::make_unique(argc, reinterpret_cast(args)); + + m_testFolderPath = m_fusedTestFolder->tempFolder() / Constants::statusGoDataDirName; + fs::create_directory(m_testFolderPath); + + // Setup accounts + auto accountsService = std::make_shared(); + auto result = accountsService->init(m_testFolderPath); + if(!result) + throw std::runtime_error("ScopedTestAccount - Failed to create temporary test account"); + + // TODO refactor and merge account creation events with login into Onboarding controller + // + // Create Login early to register and not miss onLoggedIn event signal from setupAccountAndLogin + // + + // Beware, smartpointer is a requirement + m_onboarding = std::make_shared(accountsService); + if(m_onboarding->getOpenedAccounts().size() != 0 && !ignorePreviousState) + throw std::runtime_error("ScopedTestAccount - already have opened account"); + + int accountLoggedInCount = 0; + QObject::connect(m_onboarding.get(), &Onboarding::OnboardingController::accountLoggedIn, [&accountLoggedInCount]() { + accountLoggedInCount++; + }); + bool accountLoggedInError = false; + QObject::connect(m_onboarding.get(), &Onboarding::OnboardingController::accountLoginError, [&accountLoggedInError]() { + accountLoggedInError = true; + }); + + // Create Accounts + auto genAccounts = accountsService->generatedAccounts(); + if(genAccounts.size() == 0) + throw std::runtime_error("ScopedTestAccount - missing generated accounts"); + + if(accountsService->isFirstTimeAccountLogin()) + throw std::runtime_error("ScopedTestAccount - Service::isFirstTimeAccountLogin returned true"); + + if(!accountsService->setupAccountAndLogin(genAccounts[0].id, m_accountPassword, m_accountName)) + throw std::runtime_error("ScopedTestAccount - Service::setupAccountAndLogin failed"); + + if(!accountsService->isFirstTimeAccountLogin()) + throw std::runtime_error("ScopedTestAccount - Service::isFirstTimeAccountLogin returned false"); + if(!accountsService->getLoggedInAccount().isValid()) + throw std::runtime_error("ScopedTestAccount - newly created account is not valid"); + if(accountsService->getLoggedInAccount().name != accountName) + throw std::runtime_error("ScopedTestAccount - newly created account has a wrong name"); + processMessages(2000, [accountLoggedInCount]() { + return accountLoggedInCount == 0; + }); + if(accountLoggedInCount != 1) + throw std::runtime_error("ScopedTestAccount - missing confirmation of account creation"); + if(accountLoggedInError) + throw std::runtime_error("ScopedTestAccount - account loggedin error"); +} + +ScopedTestAccount::~ScopedTestAccount() +{ +} + +void ScopedTestAccount::processMessages(size_t maxWaitTimeMillis, std::function shouldWaitUntilTimeout) { + using namespace std::chrono_literals; + std::chrono::milliseconds maxWaitTime{maxWaitTimeMillis}; + auto iterationSleepTime = 2ms; + auto remainingIterations = maxWaitTime/iterationSleepTime; + while (remainingIterations-- > 0 && shouldWaitUntilTimeout()) { + std::this_thread::sleep_for(iterationSleepTime); + + QCoreApplication::sendPostedEvents(); + } +} + +void ScopedTestAccount::logOut() +{ + if(Status::StatusGo::Accounts::logout().containsError()) + throw std::runtime_error("ScopedTestAccount - failed logging out"); +} + +Onboarding::OnboardingController *ScopedTestAccount::onboardingController() const +{ + return m_onboarding.get(); +} + +const std::filesystem::path &ScopedTestAccount::fusedTestFolder() const +{ + return m_testFolderPath; +} + +} diff --git a/libs/Onboarding/tests/OnboardingTestHelpers/ScopedTestAccount.h b/libs/Onboarding/tests/OnboardingTestHelpers/ScopedTestAccount.h new file mode 100644 index 000000000..3186439a1 --- /dev/null +++ b/libs/Onboarding/tests/OnboardingTestHelpers/ScopedTestAccount.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#include + +class QCoreApplication; + +namespace Status::Onboarding { + class OnboardingController; +} + +namespace Status::Testing { + +class AutoCleanTempTestDir; + +class ScopedTestAccount final { +public: + /*! + * \brief Create and logs in a new test account + * \param tempTestSubfolderName subfolder name of the temporary test folder where to initalize user data \see AutoCleanTempTestDir + * \todo make it more flexible by splitting into create account, login and wait for events + */ + explicit ScopedTestAccount(const std::string &tempTestSubfolderName, + const QString &accountName = defaultAccountName, + const QString &accountPassword = defaultAccountPassword, + bool ignorePreviousState = false /*workaround to status-go persisting state*/); + ~ScopedTestAccount(); + + void processMessages(size_t millis, std::function shouldWaitUntilTimeout); + void logOut(); + + Status::Onboarding::OnboardingController* onboardingController() const; + + const std::filesystem::path& fusedTestFolder() const;; + +private: + std::unique_ptr m_fusedTestFolder; + std::unique_ptr m_app; + std::filesystem::path m_testFolderPath; + std::shared_ptr m_onboarding; + std::function m_checkIfShouldContinue; + + QString m_accountName; + QString m_accountPassword; + + static constexpr auto defaultAccountName = "test_name"; + static constexpr auto defaultAccountPassword = "test_pwd*"; +}; + +} diff --git a/libs/Onboarding/tests/ServiceMock.h b/libs/Onboarding/tests/ServiceMock.h new file mode 100644 index 000000000..40f37ad73 --- /dev/null +++ b/libs/Onboarding/tests/ServiceMock.h @@ -0,0 +1,37 @@ +#pragma once + +#include "Onboarding/Accounts/AccountsServiceInterface.h" + +#include + +namespace Onboarding = Status::Onboarding; + +namespace Status::Testing +{ + +/*! + * \brief The AccountsServiceMock test class + * + * \todo Consider if this is really neaded for testing controllers + * \todo Move it to mocks subfolder + */ +class AccountsServiceMock final : public Onboarding::AccountsServiceInterface +{ +public: + virtual ~AccountsServiceMock() override {}; + + MOCK_METHOD(bool, init, (const fs::path&), (override)); + MOCK_METHOD(std::vector, openAndListAccounts, (), (override)); + MOCK_METHOD(const std::vector&, generatedAccounts, (), (const, override)); + MOCK_METHOD(bool, setupAccountAndLogin, (const QString&, const QString&, const QString&), (override)); + MOCK_METHOD(const Onboarding::AccountDto&, getLoggedInAccount, (), (const, override)); + MOCK_METHOD(const Onboarding::GeneratedAccountDto&, getImportedAccount, (), (const, override)); + MOCK_METHOD(bool, isFirstTimeAccountLogin, (), (const, override)); + MOCK_METHOD(bool, setKeyStoreDir, (const QString&), (override)); + MOCK_METHOD(QString, login, (Onboarding::AccountDto, const QString&), (override)); + MOCK_METHOD(void, clear, (), (override)); + MOCK_METHOD(QString, generateAlias, (const QString&), (override)); + MOCK_METHOD(QString, generateIdenticon, (const QString&), (override)); +}; + +} diff --git a/libs/Onboarding/tests/qml_tests/CMakeLists.txt b/libs/Onboarding/tests/qml_tests/CMakeLists.txt new file mode 100644 index 000000000..17a6bba63 --- /dev/null +++ b/libs/Onboarding/tests/qml_tests/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.5) + +project(TestOnboardingQml LANGUAGES CXX) + +enable_testing(true) + +set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) + +find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml QuickTest REQUIRED) + +add_executable(TestOnboardingQml + "main.cpp" +) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# no need to copy around qml test files for shadow builds - just set the respective define +add_compile_definitions(QUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") + +add_test(NAME TestOnboardingQml WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/TestOnboardingQml -input "${CMAKE_CURRENT_SOURCE_DIR}") +add_custom_target("Run_TestOnboardingQml" COMMAND ${CMAKE_CTEST_COMMAND} --test-dir "${CMAKE_CURRENT_BINARY_DIR}") +add_dependencies("Run_TestOnboardingQml" TestOnboardingQml) + +target_link_libraries(TestOnboardingQml PRIVATE + Qt6::QuickTest + Qt6::Qml + Qt6::Quick + + Status::TestHelpers + + Status::Onboarding +) diff --git a/libs/Onboarding/tests/qml_tests/main.cpp b/libs/Onboarding/tests/qml_tests/main.cpp new file mode 100644 index 000000000..36d927a73 --- /dev/null +++ b/libs/Onboarding/tests/qml_tests/main.cpp @@ -0,0 +1,3 @@ +#include + +QUICK_TEST_MAIN(TestOnboardingQml) diff --git a/libs/Onboarding/tests/qml_tests/tst_OnboardingController.qml b/libs/Onboarding/tests/qml_tests/tst_OnboardingController.qml new file mode 100644 index 000000000..1ba991da8 --- /dev/null +++ b/libs/Onboarding/tests/qml_tests/tst_OnboardingController.qml @@ -0,0 +1,86 @@ +import QtQuick +import QtQml +import QtTest + +import Status.Onboarding + +import Status.TestHelpers + +/**! + * \todo use mocked values + */ +Item { + id: root + width: 400 + height: 300 + + Component { + id: onboardingDepsComponent + + Item { + OnboardingModule { + id: module + + userDataPath: "/tmp/StatusTests/demo" + } + + // TODO: fix error "unable to assign Status::Onboarding::OnboardingController to Status::Onboarding::OnboardingController" then enable typed properties + readonly property var /*OnboardingController*/ controller: module.controller + readonly property var /*UserAccountsModel*/ accounts: controller.accounts + } + } + + Loader { + id: testLoader + + anchors.fill: parent + active: false + } + + TestCase { + id: qmlWarningsTest + + name: "TestQmlWarnings" + + when: windowShown + + // + // Test guards + + function init() { + qtOuput.restartCapturing() + } + + function cleanup() { + testLoader.active = false + } + + // + // Tests + + function test_moduleInitialization() { + testLoader.sourceComponent = onboardingDepsComponent + testLoader.active = true + verify(waitForRendering(testLoader.item)) + testLoader.active = false + verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`) + } + } + +// TestCase { +// id: qmlBenchmarks + +// name: "QmlBenchmarks" + +// function benchmark_loadAndUnloadModule() { +// skip("Enable benchmarking after integrating it with reporting in CI") +// testLoader.sourceComponent = onboardingDepsComponent +// testLoader.active = true +// testLoader.active = false +// } +// } + + MonitorQtOutput { + id: qtOuput + } +} diff --git a/libs/Onboarding/tests/test_AccountService.cpp b/libs/Onboarding/tests/test_AccountService.cpp new file mode 100644 index 000000000..c19e7b8c1 --- /dev/null +++ b/libs/Onboarding/tests/test_AccountService.cpp @@ -0,0 +1,60 @@ +#include "ServiceMock.h" + +#include +#include + +#include + +#include + +namespace Testing = Status::Testing; +namespace Onboarding = Status::Onboarding; + +namespace fs = std::filesystem; + +namespace Status::Testing { + +class AccountsServicesTest : public ::testing::Test +{ +protected: + std::unique_ptr m_accountsService; + std::unique_ptr m_fusedTestFolder; + + void SetUp() override { + m_fusedTestFolder = std::make_unique("AccountsServicesTest"); + m_accountsService = std::make_unique(); + m_accountsService->init(m_fusedTestFolder->tempFolder() / Constants::statusGoDataDirName); + } + + void TearDown() override { + m_fusedTestFolder.reset(); + m_accountsService.reset(); + } +}; + + +TEST_F(AccountsServicesTest, GeneratedAccounts) +{ + auto genAccounts = m_accountsService->generatedAccounts(); + + ASSERT_EQ(5, genAccounts.size()); + + for(const auto& acc : genAccounts) + { + ASSERT_STRNE(qUtf8Printable(acc.id), ""); + ASSERT_STRNE(qUtf8Printable(acc.publicKey), ""); + ASSERT_STRNE(qUtf8Printable(acc.address), ""); + ASSERT_STRNE(qUtf8Printable(acc.keyUid), ""); + } +} + +TEST_F(AccountsServicesTest, DISABLED_GenerateAlias) // temporary disabled till we see what's happening on the status-go side since it doesn't return aliases for any pk +{ + QString testPubKey = "0x04487f44bac3e90825bfa9720148308cb64835bebb7e888f519cebc127223187067629f8b70d0661a35d4af6516b225286"; + + auto alias = m_accountsService->generateAlias(testPubKey); + + ASSERT_NE(alias, QString("")); +} + +} // namespace diff --git a/libs/Onboarding/tests/test_OnboardingController.cpp b/libs/Onboarding/tests/test_OnboardingController.cpp new file mode 100644 index 000000000..49c2ba25a --- /dev/null +++ b/libs/Onboarding/tests/test_OnboardingController.cpp @@ -0,0 +1,55 @@ +#include "ServiceMock.h" + +#include + +#include + +#include +#include + +#include + +#include + +namespace Onboarding = Status::Onboarding; + +namespace fs = std::filesystem; + +namespace Status::Testing { + +class LoginTest : public ::testing::Test +{ +protected: + static std::shared_ptr m_accountsServiceMock; + + std::unique_ptr m_accountsService; + std::unique_ptr m_fusedTestFolder; + + static void SetUpTestSuite() { + m_accountsServiceMock = std::make_shared(); + } + static void TearDownTestSuite() { + m_accountsServiceMock.reset(); + } + + void SetUp() override { + m_fusedTestFolder = std::make_unique("LoginTest"); + m_accountsService = std::make_unique(); + m_accountsService->init(m_fusedTestFolder->tempFolder() / Constants::statusGoDataDirName); + } + + void TearDown() override { + m_fusedTestFolder.release(); + m_accountsService.release(); + } +}; + +std::shared_ptr LoginTest::m_accountsServiceMock; + +TEST_F(LoginTest, DISABLED_TestLoginController) +{ + // Controller hides as a regular class but at runtime it must be a shared pointer; TODO: refactor + auto controller = std::make_shared(m_accountsServiceMock); +} + +} // namespace diff --git a/libs/Onboarding/tests/test_OnboardingModule.cpp b/libs/Onboarding/tests/test_OnboardingModule.cpp new file mode 100644 index 000000000..cfba04263 --- /dev/null +++ b/libs/Onboarding/tests/test_OnboardingModule.cpp @@ -0,0 +1,170 @@ +#include + +#include "ServiceMock.h" + +#include + +#include +#include + +#include +#include + +#include + +#include + +#include + +namespace Testing = Status::Testing; +namespace Onboarding = Status::Onboarding; + +namespace fs = std::filesystem; + +namespace Status::Testing { + +static std::unique_ptr m_accountsServiceMock; + +TEST(OnboardingModule, TestInitService) +{ + Testing::AutoCleanTempTestDir fusedTestFolder{test_info_->name()}; + auto testFolderPath = fusedTestFolder.tempFolder() / Constants::statusGoDataDirName; + fs::create_directory(testFolderPath); + auto accountsService = std::make_unique(); + ASSERT_TRUE(accountsService->init(testFolderPath)); +} + +/// This integration end to end test is here for documentation purpose and until all the functionality is covered by unit-tests +/// \warning the test depends on IO and it is not deterministic, fast, focused or reliable and uses production classes. It is here for documenting only and dev process +/// \todo refactor into unit-tests with mocked interfaces +TEST(OnboardingModule, TestCreateAndLoginAccountEndToEnd) +{ + int argc = 1; + std::string appName{"test"}; + char* args[] = {appName.data()}; + QCoreApplication dummyApp{argc, reinterpret_cast(args)}; + + Testing::AutoCleanTempTestDir fusedTestFolder{test_info_->name()}; + auto testFolderPath = fusedTestFolder.tempFolder() / "Status Desktop"; + fs::create_directory(testFolderPath); + + // Setup accounts + auto accountsService = std::make_shared(); + auto result = accountsService->init(testFolderPath); + ASSERT_TRUE(result); + + // TODO refactor and merge account creation events with login into Onboarding controller + // + // Create Login early to register and not miss onLoggedIn event signal from setupAccountAndLogin + // + + // Beware, smartpointer is a requirement + auto onboarding = std::make_shared(accountsService); + EXPECT_EQ(onboarding->getOpenedAccounts().size(), 0); + + int accountLoggedInCount = 0; + QObject::connect(onboarding.get(), &Onboarding::OnboardingController::accountLoggedIn, [&accountLoggedInCount]() { + accountLoggedInCount++; + }); + bool accountLoggedInError = false; + QObject::connect(onboarding.get(), &Onboarding::OnboardingController::accountLoginError, [&accountLoggedInError]() { + accountLoggedInError = true; + }); + + // Create Accounts + auto genAccounts = accountsService->generatedAccounts(); + ASSERT_GT(genAccounts.size(), 0); + + ASSERT_FALSE(accountsService->isFirstTimeAccountLogin()); + + constexpr auto accountName = "test_name"; + constexpr auto accountPassword = "test_pwd*"; + ASSERT_TRUE(accountsService->setupAccountAndLogin(genAccounts[0].id, accountPassword, accountName)); + + ASSERT_TRUE(accountsService->isFirstTimeAccountLogin()); + ASSERT_TRUE(accountsService->getLoggedInAccount().isValid()); + ASSERT_TRUE(accountsService->getLoggedInAccount().name == accountName); + ASSERT_FALSE(accountsService->getImportedAccount().isValid()); + + using namespace std::chrono_literals; + auto maxWaitTime = 2000ms; + auto iterationSleepTime = 2ms; + auto remainingIterations = maxWaitTime/iterationSleepTime; + while (remainingIterations-- > 0 && accountLoggedInCount == 0) { + std::this_thread::sleep_for(iterationSleepTime); + + QCoreApplication::sendPostedEvents(); + } + + EXPECT_EQ(accountLoggedInCount, 1); + EXPECT_FALSE(accountLoggedInError); + EXPECT_FALSE(Status::StatusGo::Accounts::logout().containsError()); +} + +/// This integration end to end test is here for documentation purpose and until all the functionality is covered by unit-tests +/// \warning the test depends on IO and it is not deterministic, fast, focused or reliable. It is here for validation only +/// \todo find a way to test the integration within a test environment. Also how about reusing an existing account +/// \todo due to keeping status-go keeping the state thsi works only run separately +TEST(OnboardingModule, TestLoginEndToEnd) +{ + // Create test account and login + // + bool createAndLogin = false; + QObject::connect(StatusGo::SignalsManager::instance(), &StatusGo::SignalsManager::nodeLogin, [&createAndLogin](const QString& error) { + if(error.isEmpty()) { + if(createAndLogin) { + createAndLogin = false; + } else + createAndLogin = true; + } + }); + + constexpr auto accountName = "TestLoginAccountName"; + constexpr auto accountPassword = "1234567890"; + ScopedTestAccount testAccount(test_info_->name(), accountName, accountPassword, true); + testAccount.processMessages(1000, [createAndLogin]() { + return !createAndLogin; + }); + ASSERT_TRUE(createAndLogin); + + testAccount.logOut(); + + // Test account log in + // + + // Setup accounts + auto accountsService = std::make_shared(); + auto result = accountsService->init(testAccount.fusedTestFolder()); + ASSERT_TRUE(result); + + auto onboarding = std::make_shared(accountsService); + // We don't have a way yet to simulate status-go process exit + //EXPECT_EQ(onboarding->getOpenedAccounts().count(), 0); + + auto accounts = accountsService->openAndListAccounts(); + //ASSERT_EQ(accounts.size(), 1); + ASSERT_GT(accounts.size(), 0); + + int accountLoggedInCount = 0; + QObject::connect(onboarding.get(), &Onboarding::OnboardingController::accountLoggedIn, [&accountLoggedInCount]() { + accountLoggedInCount++; + }); + bool accountLoggedInError = false; + QObject::connect(onboarding.get(), &Onboarding::OnboardingController::accountLoginError, [&accountLoggedInError]() { + accountLoggedInError = true; + }); + + //auto errorString = accountsService->login(accounts[0], accountPassword); + // Workaround until we reset the status-go state + auto ourAccountRes = std::find_if(accounts.begin(), accounts.end(), [accountName](const auto &a) { return a.name == accountName; }); + auto errorString = accountsService->login(*ourAccountRes, accountPassword); + ASSERT_EQ(errorString.length(), 0); + + testAccount.processMessages(1000, [accountLoggedInCount, accountLoggedInError]() { + return accountLoggedInCount == 0 && !accountLoggedInError; + }); + ASSERT_EQ(accountLoggedInCount, 1); + ASSERT_EQ(accountLoggedInError, 0); +} + +} // namespace diff --git a/libs/StatusGoQt/CMakeLists.txt b/libs/StatusGoQt/CMakeLists.txt index 823d839a7..81de8e39c 100644 --- a/libs/StatusGoQt/CMakeLists.txt +++ b/libs/StatusGoQt/CMakeLists.txt @@ -8,10 +8,10 @@ project(StatusGoQt set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) -find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Core REQUIRED) +find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Core Concurrent REQUIRED) qt6_standard_project_setup() -add_library(${PROJECT_NAME} SHARED "") +add_library(${PROJECT_NAME} SHARED) # Use by linker only set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug) @@ -19,19 +19,30 @@ set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug) add_subdirectory(src) target_link_libraries(${PROJECT_NAME} - PRIVATE + PUBLIC Qt6::Core + Qt6::Concurrent + PRIVATE statusgo_shared ) +add_library(Status::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) # Copy status-go lib close to the executable # Temporary workaround; TODO: see a better alternative that doesn't depend on target order (dedicated dependencies dir?) +# and current directory (on mac). Use bundle or set rpath relative to executable get_target_property(STATUSGO_LIBRARY_PATH statusgo_shared IMPORTED_LOCATION) -configure_file(${STATUSGO_LIBRARY_PATH} ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) +add_custom_command( + TARGET + ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + $ + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMENT "Copying status-go lib beside project executable" +) install( - TARGETS - ${PROJECT_NAME} - RUNTIME -) \ No newline at end of file + IMPORTED_RUNTIME_ARTIFACTS + statusgo_shared +) diff --git a/src-cpp-structure/projects/Backend/src/Accounts.cpp b/libs/StatusGoQt/src/StatusGo/Accounts/Accounts.cpp similarity index 51% rename from src-cpp-structure/projects/Backend/src/Accounts.cpp rename to libs/StatusGoQt/src/StatusGo/Accounts/Accounts.cpp index 8c134c741..4d745a0cd 100644 --- a/src-cpp-structure/projects/Backend/src/Accounts.cpp +++ b/libs/StatusGoQt/src/StatusGo/Accounts/Accounts.cpp @@ -1,14 +1,14 @@ -#include "StatusBackend/Accounts.h" +#include "Accounts.h" -#include "StatusBackend/Utils.h" +#include "Utils.h" #include "libstatus.h" const int NUMBER_OF_ADDRESSES_TO_GENERATE = 5; const int MNEMONIC_PHRASE_LENGTH = 12; -using namespace Backend; +namespace Status::StatusGo::Accounts { -RpcResponse Accounts::generateAddresses(const QVector& paths) +RpcResponse generateAddresses(const QVector& paths) { QJsonObject payload{ {"n", NUMBER_OF_ADDRESSES_TO_GENERATE}, @@ -27,7 +27,7 @@ RpcResponse Accounts::generateAddresses(const QVector& path throw std::domain_error(msg.toStdString()); } - return Utils::buildJsonRpcResponse(jsonResult); + return Utils::buildPrivateRPCResponse(jsonResult); } catch (std::exception& e) { @@ -43,7 +43,7 @@ RpcResponse Accounts::generateAddresses(const QVector& path } } -RpcResponse Accounts::generateIdenticon(const QString& publicKey) +RpcResponse generateIdenticon(const QString& publicKey) { try { @@ -52,7 +52,7 @@ RpcResponse Accounts::generateIdenticon(const QString& publicKey) { identicon = Identicon(publicKey.toUtf8().data()); } - return Utils::buildJsonRpcResponse(identicon); + return Utils::buildPrivateRPCResponse(identicon); } catch (...) { @@ -62,7 +62,7 @@ RpcResponse Accounts::generateIdenticon(const QString& publicKey) } } -RpcResponse Accounts::generateAlias(const QString& publicKey) +RpcResponse generateAlias(const QString& publicKey) { try { @@ -72,7 +72,7 @@ RpcResponse Accounts::generateAlias(const QString& publicKey) alias = GenerateAlias(publicKey.toUtf8().data()); } - return Utils::buildJsonRpcResponse(alias); + return Utils::buildPrivateRPCResponse(alias); } catch (...) { @@ -82,7 +82,7 @@ RpcResponse Accounts::generateAlias(const QString& publicKey) } } -RpcResponse Accounts::storeDerivedAccounts(const QString& id, const QString& hashedPassword, +RpcResponse storeDerivedAccounts(const QString& id, const QString& hashedPassword, const QVector& paths) { QJsonObject payload{ @@ -101,7 +101,9 @@ RpcResponse Accounts::storeDerivedAccounts(const QString& id, const throw std::domain_error(msg.toStdString()); } - return Utils::buildJsonRpcResponse(jsonResult); + RpcResponse rpcResponse(jsonResult); + rpcResponse.error = Utils::getRPCErrorInJson(jsonResult).value_or(RpcError()); + return rpcResponse; } catch (std::exception& e) { @@ -117,17 +119,16 @@ RpcResponse Accounts::storeDerivedAccounts(const QString& id, const } } -RpcResponse Accounts::saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, - const QJsonArray& subaccounts, const QJsonObject& settings, - const QJsonObject& nodeConfig) +RpcResponse storeAccount(const QString& id, const QString& hashedPassword) { + QJsonObject payload{ + {"accountID", id}, + {"password", hashedPassword} + }; + try { - auto result = SaveAccountAndLogin(Utils::jsonToByteArray(std::move(account)).data(), - hashedPassword.toUtf8().data(), - Utils::jsonToByteArray(std::move(settings)).data(), - Utils::jsonToByteArray(std::move(nodeConfig)).data(), - Utils::jsonToByteArray(std::move(subaccounts)).data()); + auto result = MultiAccountStoreAccount(Utils::jsonToByteArray(std::move(payload)).data()); QJsonObject jsonResult; if(!Utils::checkReceivedResponse(result, jsonResult)) { @@ -135,39 +136,70 @@ RpcResponse Accounts::saveAccountAndLogin(const QString& hashedPass throw std::domain_error(msg.toStdString()); } - return Utils::buildJsonRpcResponse(jsonResult); + RpcResponse rpcResponse(jsonResult); + rpcResponse.error = Utils::getRPCErrorInJson(jsonResult).value_or(RpcError()); + return rpcResponse; } catch (std::exception& e) { auto response = RpcResponse(QJsonObject()); - response.error.message = QObject::tr("an error saving account and login occurred, msg: %1").arg(e.what()); + response.error.message = QObject::tr("an error storing account occurred, msg: %1").arg(e.what()); return response; } catch (...) { auto response = RpcResponse(QJsonObject()); - response.error.message = QObject::tr("an error saving account and login occurred"); + response.error.message = QObject::tr("an error storing account occurred"); return response; } } -Backend::RpcResponse Backend::Accounts::openAccounts(const QString& path) +bool saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, + const QJsonArray& subaccounts, const QJsonObject& settings, + const QJsonObject& nodeConfig) { try { - auto result = OpenAccounts(path.toUtf8().data()); - QJsonArray jsonResult; + auto result = SaveAccountAndLogin(Utils::jsonToByteArray(account).data(), + hashedPassword.toUtf8().data(), + Utils::jsonToByteArray(settings).data(), + Utils::jsonToByteArray(nodeConfig).data(), + Utils::jsonToByteArray(subaccounts).data()); + QJsonObject jsonResult; if(!Utils::checkReceivedResponse(result, jsonResult)) { auto msg = QObject::tr("parsing response failed"); throw std::domain_error(msg.toStdString()); } - return Utils::buildJsonRpcResponse(jsonResult); + return !Utils::getRPCErrorInJson(jsonResult).has_value(); + } catch (std::exception& e) { + qWarning() << QString("an error saving account and login occurred, msg: %1").arg(e.what()); + } catch (...) { + qWarning() << "an error saving account and login occurred"; + } + return false; +} + +RpcResponse openAccounts(const char* dataDirPath) +{ + try + { + auto result = QString(OpenAccounts(const_cast(dataDirPath))); + if(result == "null") + return RpcResponse(QJsonArray()); + + QJsonArray jsonResult; + if(!Utils::checkReceivedResponse(result, jsonResult)) { + throw std::domain_error("parsing response failed"); + } + + return Utils::buildPrivateRPCResponse(jsonResult); } catch (std::exception& e) { auto response = RpcResponse(QJsonArray()); + // TODO: don't translate exception messages. Exceptions are for developers and should never reach users response.error.message = QObject::tr("an error opening accounts occurred, msg: %1").arg(e.what()); return response; } @@ -179,14 +211,13 @@ Backend::RpcResponse Backend::Accounts::openAccounts(const QString& } } -RpcResponse Accounts::login(const QString& name, const QString& keyUid, const QString& hashedPassword, +RpcResponse login(const QString& name, const QString& keyUid, const QString& hashedPassword, const QString& identicon, const QString& thumbnail, const QString& large) { QJsonObject payload{ {"name", name}, {"key-uid", keyUid}, - {"identityImage", QJsonValue()}, - {"identicon", identicon} + {"identityImage", QJsonValue()} }; if(!thumbnail.isEmpty() && !large.isEmpty()) @@ -196,7 +227,8 @@ RpcResponse Accounts::login(const QString& name, const QString& key try { - auto result = Login(Utils::jsonToByteArray(std::move(payload)).data(), hashedPassword.toUtf8().data()); + auto payloadData = Utils::jsonToByteArray(std::move(payload)); + auto result = Login(payloadData.data(), hashedPassword.toUtf8().data()); QJsonObject jsonResult; if(!Utils::checkReceivedResponse(result, jsonResult)) { @@ -204,7 +236,7 @@ RpcResponse Accounts::login(const QString& name, const QString& key throw std::domain_error(msg.toStdString()); } - return Utils::buildJsonRpcResponse(jsonResult); + return Utils::buildPrivateRPCResponse(jsonResult); } catch (std::exception& e) { @@ -219,3 +251,78 @@ RpcResponse Accounts::login(const QString& name, const QString& key return response; } } + +RpcResponse loginWithConfig(const QString& name, const QString& keyUid, const QString& hashedPassword, + const QString& identicon, const QString& thumbnail, const QString& large, + const QJsonObject& nodeConfig) +{ + QJsonObject payload{ + {"name", name}, + {"key-uid", keyUid}, + {"identityImage", QJsonValue()}, + }; + + if(!thumbnail.isEmpty() && !large.isEmpty()) + { + payload["identityImage"] = QJsonObject{{"thumbnail", thumbnail}, {"large", large}}; + } + + try + { + auto payloadData = Utils::jsonToByteArray(std::move(payload)); + auto nodeConfigData = Utils::jsonToByteArray(nodeConfig); + auto result = LoginWithConfig(payloadData.data(), hashedPassword.toUtf8().data(), nodeConfigData.data()); + QJsonObject jsonResult; + if(!Utils::checkReceivedResponse(result, jsonResult)) + { + auto msg = QObject::tr("parsing response failed"); + throw std::domain_error(msg.toStdString()); + } + + return Utils::buildPrivateRPCResponse(jsonResult); + } + catch (std::exception& e) + { + auto response = RpcResponse(QJsonObject()); + response.error.message = QObject::tr("an error logining in account occurred, msg: %1").arg(e.what()); + return response; + } + catch (...) + { + auto response = RpcResponse(QJsonObject()); + response.error.message = QObject::tr("an error logining in account occurred"); + return response; + } +} + +RpcResponse logout() +{ + try + { + auto result = Logout(); + QJsonObject jsonResult; + if(!Utils::checkReceivedResponse(result, jsonResult)) + { + auto msg = QObject::tr("parsing response failed"); + throw std::domain_error(msg.toStdString()); + } + + RpcResponse rpcResponse(jsonResult); + rpcResponse.error = Utils::getRPCErrorInJson(jsonResult).value_or(RpcError()); + return rpcResponse; + } + catch (std::exception& e) + { + auto response = RpcResponse(QJsonObject()); + response.error.message = QObject::tr("an error logging out account occurred, msg: %1").arg(e.what()); + return response; + } + catch (...) + { + auto response = RpcResponse(QJsonObject()); + response.error.message = QObject::tr("an error logging out account occurred"); + return response; + } +} + +} diff --git a/src-cpp-structure/projects/Backend/include/StatusBackend/Accounts.h b/libs/StatusGoQt/src/StatusGo/Accounts/Accounts.h similarity index 55% rename from src-cpp-structure/projects/Backend/include/StatusBackend/Accounts.h rename to libs/StatusGoQt/src/StatusGo/Accounts/Accounts.h index ab2e2567d..c6b24c0b1 100644 --- a/src-cpp-structure/projects/Backend/include/StatusBackend/Accounts.h +++ b/libs/StatusGoQt/src/StatusGo/Accounts/Accounts.h @@ -4,7 +4,7 @@ #include -namespace Backend::Accounts +namespace Status::StatusGo::Accounts { RpcResponse generateAddresses(const QVector& paths); @@ -15,12 +15,19 @@ namespace Backend::Accounts RpcResponse storeDerivedAccounts(const QString& accountId, const QString& hashedPassword, const QVector& paths); - RpcResponse saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, + RpcResponse storeAccount(const QString& id, const QString& hashedPassword); + + bool saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, const QJsonArray& subaccounts, const QJsonObject& settings, const QJsonObject& nodeConfig); - RpcResponse openAccounts(const QString& path); + /// opens database and returns accounts list. + RpcResponse openAccounts(const char* dataDirPath); RpcResponse login(const QString& name, const QString& keyUid, const QString& hashedPassword, const QString& identicon, const QString& thumbnail, const QString& large); + RpcResponse loginWithConfig(const QString& name, const QString& keyUid, const QString& hashedPassword, + const QString& identicon, const QString& thumbnail, const QString& large, + const QJsonObject& nodeConfig); + RpcResponse logout(); } diff --git a/libs/StatusGoQt/src/StatusGo/CMakeLists.txt b/libs/StatusGoQt/src/StatusGo/CMakeLists.txt new file mode 100644 index 000000000..0393538c5 --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/CMakeLists.txt @@ -0,0 +1,22 @@ +target_sources(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/General.h + ${CMAKE_CURRENT_SOURCE_DIR}/Utils.h + + ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/Accounts.h + + ${CMAKE_CURRENT_SOURCE_DIR}/Messenger/Service.h + + ${CMAKE_CURRENT_SOURCE_DIR}/SignalsManager.h + + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/General.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Types.h + ${CMAKE_CURRENT_SOURCE_DIR}/Utils.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/Accounts.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/Messenger/Service.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/SignalsManager.cpp +) diff --git a/libs/StatusGoQt/src/StatusGo/General.cpp b/libs/StatusGoQt/src/StatusGo/General.cpp new file mode 100644 index 000000000..d769a0c79 --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/General.cpp @@ -0,0 +1,37 @@ +#include "General.h" + +#include "Utils.h" +#include "libstatus.h" + +namespace Status::StatusGo::General +{ + +RpcResponse initKeystore(const char* keystoreDir) +{ + try + { + auto result = InitKeystore(const_cast(keystoreDir)); + QJsonObject jsonResult; + if(!Utils::checkReceivedResponse(result, jsonResult)) { + throw std::domain_error("parsing response failed"); + } + + return Utils::buildPrivateRPCResponse(jsonResult); + } + catch (std::exception& e) + { + // TODO: either use optional/smartpointers or exceptions instead of plain objects + auto response = RpcResponse(QJsonObject()); + // TODO: don't translate exception messages. Exceptions are for developers and should never reach users + response.error.message = QObject::tr("an error opening accounts occurred, msg: %1").arg(e.what()); + return response; + } + catch (...) + { + auto response = RpcResponse(QJsonObject()); + response.error.message = QObject::tr("an error opening accounts occurred"); + return response; + } +} + +} diff --git a/libs/StatusGoQt/src/StatusGo/General.h b/libs/StatusGoQt/src/StatusGo/General.h new file mode 100644 index 000000000..309a94d57 --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/General.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Types.h" + +#include + +namespace Status::StatusGo::General +{ + +RpcResponse initKeystore(const char* keystoreDir); + +} diff --git a/libs/StatusGoQt/src/StatusGo/Messenger/Service.cpp b/libs/StatusGoQt/src/StatusGo/Messenger/Service.cpp new file mode 100644 index 000000000..6d07ef7f9 --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/Messenger/Service.cpp @@ -0,0 +1,22 @@ +#include "Service.h" + +#include "Utils.h" + +namespace Status::StatusGo::Messenger +{ + +bool startMessenger() +{ + QJsonObject payload{ + {"jsonrpc", "2.0"}, + {"method", "wakuext_startMessenger"}, + {"params", QJsonArray()} + }; + + auto callResult = Utils::callPrivateRpc(Utils::jsonToByteArray(payload)); + if(callResult.containsError()) + qWarning() << "Failed starting Messenger service. Error: " << callResult.error.message; + return !callResult.containsError(); +} + +} diff --git a/libs/StatusGoQt/src/StatusGo/Messenger/Service.h b/libs/StatusGoQt/src/StatusGo/Messenger/Service.h new file mode 100644 index 000000000..c0ad1e609 --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/Messenger/Service.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Types.h" + +#include + +namespace Status::StatusGo::Messenger +{ + +bool startMessenger(); + +} diff --git a/libs/StatusGoQt/src/StatusGo/SignalsManager.cpp b/libs/StatusGoQt/src/StatusGo/SignalsManager.cpp new file mode 100644 index 000000000..419cbcd23 --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/SignalsManager.cpp @@ -0,0 +1,138 @@ +#include "SignalsManager.h" + +#include + +#include "libstatus.h" + +using namespace std::string_literals; + +namespace Status::StatusGo { + +std::map SignalsManager::signalMap; + +// TODO: make me thread safe or better refactor into broadcasting mechanism +SignalsManager* SignalsManager::instance() +{ + static SignalsManager manager; + return &manager; +} + +SignalsManager::SignalsManager() + : QObject(nullptr) +{ + SetSignalEventCallback((void*)&SignalsManager::signalCallback); + + signalMap = { + {"node.ready"s, SignalType::NodeReady}, + {"node.started"s, SignalType::NodeStarted}, + {"node.stopped"s, SignalType::NodeStopped}, + {"node.login"s, SignalType::NodeLogin}, + {"node.crashed"s, SignalType::NodeCrashed}, + + {"discovery.started"s, SignalType::DiscoveryStarted}, + {"discovery.stopped"s, SignalType::DiscoveryStopped}, + {"discovery.summary"s, SignalType::DiscoverySummary}, + + {"mailserver.changed"s, SignalType::MailserverChanged}, + {"mailserver.available"s, SignalType::MailserverAvailable}, + + {"history.request.started"s, SignalType::HistoryRequestStarted}, + {"history.request.batch.processed"s, SignalType::HistoryRequestBatchProcessed}, + {"history.request.completed"s, SignalType::HistoryRequestCompleted} + }; +} + +SignalsManager::~SignalsManager() +{ +} + +void SignalsManager::processSignal(const QString& statusSignal) +{ + try + { + QJsonParseError json_error; + const QJsonDocument signalEventDoc(QJsonDocument::fromJson(statusSignal.toUtf8(), &json_error)); + if(json_error.error != QJsonParseError::NoError) + { + qWarning() << "Invalid signal received"; + return; + } + decode(signalEventDoc.object()); + } + catch(const std::exception& e) + { + qWarning() << "Error decoding signal, err: ", e.what(); + return; + } +} + +void SignalsManager::decode(const QJsonObject& signalEvent) +{ + SignalType signalType(Unknown); + auto signalName = signalEvent["type"].toString().toStdString(); + if(!signalMap.contains(signalName)) + { + qWarning() << "Unknown signal received: " << signalName.c_str(); + return; + } + + signalType = signalMap[signalName]; + auto signalError = signalEvent["event"]["error"].toString(); + + switch(signalType) + { + // TODO: create extractor functions like in nim + case NodeLogin: + emit nodeLogin(signalError); + break; + case NodeReady: + emit nodeReady(signalError); + break; + case NodeStarted: + emit nodeStarted(signalError); + break; + case NodeStopped: + emit nodeStopped(signalError); + break; + case NodeCrashed: + qWarning() << "node.crashed, error: " << signalError; + emit nodeCrashed(signalError); + break; + case DiscoveryStarted: + emit discoveryStarted(signalError); + break; + case DiscoveryStopped: + emit discoveryStopped(signalError); + break; + case DiscoverySummary: + emit discoverySummary(signalEvent["event"].toArray().count(), signalError); + break; + case MailserverChanged: + emit mailserverChanged(signalError); + break; + case MailserverAvailable: + emit mailserverAvailable(signalError); + break; + case HistoryRequestStarted: + emit historyRequestStarted(signalError); + break; + case HistoryRequestBatchProcessed: + emit historyRequestBatchProcessed(signalError); + break; + case HistoryRequestCompleted: + emit historyRequestCompleted(signalError); + break; + case Unknown: assert(false); break; + } +} + +void SignalsManager::signalCallback(const char* data) +{ + // TODO: overkill, use some kind of message broker + auto dataStrPtr = std::make_shared(data); + QFuture future = QtConcurrent::run([dataStrPtr](){ + SignalsManager::instance()->processSignal(*dataStrPtr); + }); +} + +} diff --git a/libs/StatusGoQt/src/StatusGo/SignalsManager.h b/libs/StatusGoQt/src/StatusGo/SignalsManager.h new file mode 100644 index 000000000..719cec3b9 --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/SignalsManager.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +namespace Status::StatusGo { + +enum SignalType +{ + Unknown, + NodeLogin, + NodeReady, + NodeStarted, + NodeStopped, + NodeCrashed, + + DiscoveryStarted, + DiscoveryStopped, + DiscoverySummary, + + MailserverChanged, + MailserverAvailable, + + HistoryRequestStarted, + HistoryRequestBatchProcessed, + HistoryRequestCompleted +}; + +/*! + \todo refactor into a message broker helper to be used by specific service APIs to deliver signals + as part of the specific StatusGoAPI service + \todo address thread safety + */ +class SignalsManager final : public QObject +{ + Q_OBJECT + +public: + + static SignalsManager* instance(); + + void processSignal(const QString& ev); + +signals: + void nodeReady(const QString& error); + void nodeStarted(const QString& error); + void nodeStopped(const QString& error); + void nodeLogin(const QString& error); + void nodeCrashed(const QString& error); + + void discoveryStarted(const QString& error); + void discoveryStopped(const QString& error); + void discoverySummary(size_t nodeCount, const QString& error); + + void mailserverChanged(const QString& error); + void mailserverAvailable(const QString& error); + + void historyRequestStarted(const QString& error); + void historyRequestBatchProcessed(const QString& error); + void historyRequestCompleted(const QString& error); +private: + explicit SignalsManager(); + ~SignalsManager(); + +private: + static std::map signalMap; + static void signalCallback(const char* data); + void decode(const QJsonObject& signalEvent); +}; + +} diff --git a/libs/StatusGoQt/src/StatusGo/Types.h b/libs/StatusGoQt/src/StatusGo/Types.h new file mode 100644 index 000000000..b55bb4f9b --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/Types.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace Status::StatusGo +{ + +// Used in calls where we don't have version and id returned from `status-go` + +struct RpcError +{ + // TODO: enum instead for known errors? + static constexpr int NoError = -1; + int code = NoError; + QString message; + + static constexpr auto UnknownVersion{""}; + static constexpr int UnknownId = 0; +}; + +template +struct RpcResponse +{ + T result; + QString jsonRpcVersion; + int id; + RpcError error; + + explicit + RpcResponse(T result, QString version = RpcError::UnknownVersion, int id = RpcError::UnknownId, + RpcError error = RpcError()) + : result(std::move(result)) + , jsonRpcVersion(std::move(version)) + , id(id) + , error(std::move(error)) + { } + + bool containsError() const { + return !error.message.isEmpty() || error.code != RpcError::NoError; + } +}; + +} diff --git a/libs/StatusGoQt/src/StatusGo/Utils.cpp b/libs/StatusGoQt/src/StatusGo/Utils.cpp new file mode 100644 index 000000000..e4007dd34 --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/Utils.cpp @@ -0,0 +1,48 @@ +#include "Utils.h" + +#include "libstatus.h" + +#include + +namespace Status::StatusGo::Utils +{ + +QJsonArray toJsonArray(const QVector& value) +{ + QJsonArray array; + for(auto& v : value) + array << v; + return array; +} + +const char* statusgoCallPrivateRPC(const char* inputJSON) { + // Evil done here! status-go API doesn't follow the proper so we adapt + return CallPrivateRPC(const_cast(inputJSON)); +} + +QString hashString(const QString &str) +{ + return "0x" + QString::fromUtf8(QCryptographicHash::hash(str.toUtf8(), + QCryptographicHash::Keccak_256).toHex()); +} + +std::optional getRPCErrorInJson(const QJsonObject& json) +{ + auto errVal = json[Param::Error]; + if (errVal.isNull() || errVal.isUndefined()) + return std::nullopt; + if(errVal.isString() && errVal.toString().length() == 0) + return std::nullopt; + + RpcError response; + auto errObj = json[Param::Id].toObject(); + if (!errObj[Param::ErrorCode].isNull() && !errObj[Param::ErrorCode].isUndefined()) + response.code = errObj[Param::ErrorCode].toInt(); + if (!errObj[Param::ErrorMessage].isNull() && !errObj[Param::ErrorMessage].isUndefined()) + response.message = errObj[Param::ErrorMessage].toString(); + else + response.message = errVal.toString(); + return response; +} + +} diff --git a/libs/StatusGoQt/src/StatusGo/Utils.h b/libs/StatusGoQt/src/StatusGo/Utils.h new file mode 100644 index 000000000..f39a306fe --- /dev/null +++ b/libs/StatusGoQt/src/StatusGo/Utils.h @@ -0,0 +1,116 @@ +#pragma once + +#include "Types.h" + +#include +#include +#include + +namespace Status::StatusGo::Utils +{ + +namespace Param { + static constexpr auto Id{"id"}; + static constexpr auto JsonRpc{"jsonrpc"}; + static constexpr auto Result{"result"}; + static constexpr auto Error{"error"}; + static constexpr auto ErrorMessage{"message"}; + static constexpr auto ErrorCode{"code"}; +} + +template +QByteArray jsonToByteArray(const T& json) +{ + static_assert(std::is_same_v || + std::is_same_v, "Wrong Json type. Supported: Object, Array"); + return QJsonDocument(json).toJson(QJsonDocument::Compact); +} + +QJsonArray toJsonArray(const QVector& value); + +/// Check if json contains a standard status-go error and +std::optional getRPCErrorInJson(const QJsonObject& json); + +template +bool checkReceivedResponse(const QString& response, T& json) +{ + QJsonParseError error; + auto jsonDocument = QJsonDocument::fromJson(response.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) + return false; + + if constexpr (std::is_same_v) + { + json = jsonDocument.object(); + return true; + } + else if constexpr (std::is_same_v) + { + json = jsonDocument.array(); + return true; + } + + return false; +} + +// TODO: Clarify scope. The assumption done here are valid only for status-go::CallPrivateRPC API. +template +RpcResponse buildPrivateRPCResponse(const T& json) +{ + auto response = RpcResponse(T()); + + if constexpr (std::is_same_v) + { + if (!json[Param::Id].isNull() && !json[Param::Id].isUndefined()) + response.id = json[Param::Id].toInt(); + + if (!json[Param::JsonRpc].isNull() && !json[Param::JsonRpc].isUndefined()) + response.jsonRpcVersion = json[Param::JsonRpc].toString(); + + response.error = getRPCErrorInJson(json).value_or(RpcError()); + + if (!json[Param::Result].isNull() && !json[Param::Result].isUndefined()) + response.result = json[Param::Result].toObject(); + } + else if constexpr (std::is_same_v) + { + response.result = json; + } + + return response; +} + +const char* statusgoCallPrivateRPC(const char* inputJSON); + +template +RpcResponse callPrivateRpc(const QByteArray& payload) +{ + try + { + auto result = statusgoCallPrivateRPC(payload.data()); + T jsonResult; + if(!Utils::checkReceivedResponse(result, jsonResult)) + { + auto msg = QObject::tr("parsing response failed"); + throw std::domain_error(msg.toStdString()); + } + + return Utils::buildPrivateRPCResponse(jsonResult); + } + catch (std::exception& e) + { + auto response = RpcResponse(T()); + response.error.message = QObject::tr("an error executing rpc call occurred, msg: %1").arg(e.what()); + return response; + } + catch (...) + { + auto response = RpcResponse(T()); + response.error.message = QObject::tr("an error executing rpc call"); + return response; + } +} + +QString hashString(const QString &str); + +} diff --git a/libs/StatusQ/CMakeLists.txt b/libs/StatusQ/CMakeLists.txt index 314ed02cd..a14e2df5d 100644 --- a/libs/StatusQ/CMakeLists.txt +++ b/libs/StatusQ/CMakeLists.txt @@ -20,6 +20,7 @@ qt6_add_qml_module(${PROJECT_NAME} OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Status ) +add_library(Status::StatusQ ALIAS StatusQ) add_subdirectory(qml/Status/Containers) add_subdirectory(qml/Status/Controls) @@ -45,4 +46,4 @@ install( TARGETS ${PROJECT_NAME} RUNTIME -) \ No newline at end of file +) diff --git a/libs/StatusQ/qml/Status/Controls/Navigation/NavigationBarButton.qml b/libs/StatusQ/qml/Status/Controls/Navigation/NavigationBarButton.qml index 371a250df..ab69f331d 100644 --- a/libs/StatusQ/qml/Status/Controls/Navigation/NavigationBarButton.qml +++ b/libs/StatusQ/qml/Status/Controls/Navigation/NavigationBarButton.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick /*! Template for a NavigationBar square button diff --git a/libs/StatusQ/qml/Status/Core/Theme/Utils.qml b/libs/StatusQ/qml/Status/Core/Theme/Utils.qml index c502aaf3e..bd13122bb 100644 --- a/libs/StatusQ/qml/Status/Core/Theme/Utils.qml +++ b/libs/StatusQ/qml/Status/Core/Theme/Utils.qml @@ -1,6 +1,6 @@ pragma Singleton -import QtQuick 2.0 +import QtQuick /*! Helper functions for colors and sizes transformations diff --git a/libs/StatusQ/tests/CMakeLists.txt b/libs/StatusQ/tests/CMakeLists.txt index bb86bedc0..190fa58eb 100644 --- a/libs/StatusQ/tests/CMakeLists.txt +++ b/libs/StatusQ/tests/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5) project(TestStatusQ LANGUAGES CXX) -enable_testing() +enable_testing(true) set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) @@ -10,7 +10,7 @@ find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml QuickTest REQUIRED) qt6_standard_project_setup() qt6_add_qml_module(${PROJECT_NAME} - URI StatusQ.TestHelpers + URI Status.TestHelpers VERSION 1.0 # TODO: temporary until we make qt_target_qml_sources work QML_FILES @@ -29,7 +29,6 @@ add_test(NAME ${PROJECT_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMM add_custom_target("Run_${PROJECT_NAME}" COMMAND ${CMAKE_CTEST_COMMAND} --test-dir "${CMAKE_CURRENT_BINARY_DIR}") add_dependencies("Run_${PROJECT_NAME}" ${PROJECT_NAME}) -# TODO: move this to a test helpers library target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/libs/StatusQ/tests/TestHelpers/CMakeLists.txt b/libs/StatusQ/tests/TestHelpers/CMakeLists.txt index c54ed205d..aa0467ce6 100644 --- a/libs/StatusQ/tests/TestHelpers/CMakeLists.txt +++ b/libs/StatusQ/tests/TestHelpers/CMakeLists.txt @@ -14,9 +14,10 @@ find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml REQUIRED) qt6_standard_project_setup() qt6_add_qml_module(${PROJECT_NAME} - URI Status.TestHelpers + URI Status.${PROJECT_NAME} VERSION 1.0 ) +add_library(Status::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} PUBLIC @@ -43,5 +44,3 @@ target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest_main ) - -add_library(Status::TestHelpers ALIAS TestHelpers) diff --git a/libs/StatusQ/tests/TestHelpers/IOTestHelpers.cpp b/libs/StatusQ/tests/TestHelpers/IOTestHelpers.cpp index bc1ef26bc..b0e229f1f 100644 --- a/libs/StatusQ/tests/TestHelpers/IOTestHelpers.cpp +++ b/libs/StatusQ/tests/TestHelpers/IOTestHelpers.cpp @@ -12,25 +12,25 @@ fs::path createTestFolder(const std::string& testName) auto tm = *std::localtime(&t); std::ostringstream timeOss; timeOss << std::put_time(&tm, "%d-%m-%Y_%H-%M-%S"); - auto tmpPath = fs::path(testing::TempDir())/(testName + "-" + timeOss.str()); - fs::create_directories(tmpPath); - return tmpPath; + return fs::path(testing::TempDir())/"StatusTests"/(testName + "-" + timeOss.str()); } -AutoCleanTempTestDir::AutoCleanTempTestDir(const std::string &testName) +AutoCleanTempTestDir::AutoCleanTempTestDir(const std::string &testName, bool createDir) : m_testFolder(createTestFolder(testName)) { + if(createDir) + fs::create_directories(m_testFolder); } AutoCleanTempTestDir::~AutoCleanTempTestDir() { - fs::remove_all(m_testFolder); + // TODO: Consider making this concurrent safe and cleanup the root folder as well if empty + fs::remove_all(m_testFolder); } -const std::filesystem::path& AutoCleanTempTestDir::testFolder() +const std::filesystem::path& AutoCleanTempTestDir::tempFolder() { return m_testFolder; } - } diff --git a/libs/StatusQ/tests/TestHelpers/IOTestHelpers.h b/libs/StatusQ/tests/TestHelpers/IOTestHelpers.h index e7ba656ae..238cb61d3 100644 --- a/libs/StatusQ/tests/TestHelpers/IOTestHelpers.h +++ b/libs/StatusQ/tests/TestHelpers/IOTestHelpers.h @@ -10,10 +10,10 @@ class AutoCleanTempTestDir { public: /// Creates a temporary folder to be used in tests. The folder content's will /// be removed when out of scope - explicit AutoCleanTempTestDir(const std::string& testName); + explicit AutoCleanTempTestDir(const std::string& testName, bool createDir = true); ~AutoCleanTempTestDir(); - const std::filesystem::path& testFolder(); + const std::filesystem::path& tempFolder(); private: const std::filesystem::path m_testFolder; diff --git a/libs/StatusQ/tests/TestHelpers/MonitorQtOutput.h b/libs/StatusQ/tests/TestHelpers/MonitorQtOutput.h index e9b0180db..3134a40da 100644 --- a/libs/StatusQ/tests/TestHelpers/MonitorQtOutput.h +++ b/libs/StatusQ/tests/TestHelpers/MonitorQtOutput.h @@ -22,6 +22,8 @@ namespace Status::Testing { class MonitorQtOutput : public QQuickItem { Q_OBJECT + + QML_ELEMENT public: MonitorQtOutput(); ~MonitorQtOutput(); diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt new file mode 100644 index 000000000..75ed25d3d --- /dev/null +++ b/resources/CMakeLists.txt @@ -0,0 +1,31 @@ +# Temporary library not to duplicate resources +# TODO: refactor it when switching to C++ code +# +cmake_minimum_required(VERSION 3.21) + +project(StatusGoConfig + VERSION 0.1.0 + LANGUAGES CXX) + +set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) + +find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Qml REQUIRED) +qt6_standard_project_setup() + +# Resource path ":/Status/StaticConfig/" +qt6_add_qml_module(${PROJECT_NAME} + URI Status.StaticConfig + VERSION 1.0 + RESOURCES + default-networks.json + fleets.json + infura_key + node-config.json + RESOURCE_PREFIX "" +) +add_library(Status::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + Qt6::Qml +) diff --git a/src-cpp-structure/CMakeLists.txt b/src-cpp-structure/CMakeLists.txt deleted file mode 100644 index c328671d2..000000000 --- a/src-cpp-structure/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -cmake_minimum_required(VERSION 3.18 FATAL_ERROR) - -project(Status DESCRIPTION "Status project" LANGUAGES CXX) - -include("${CMAKE_SOURCE_DIR}/cmake/project-config.cmake") -include("${CMAKE_SOURCE_DIR}/cmake/conan.cmake") - -find_package(Qt5 REQUIRED COMPONENTS - Core - Gui - Quick - Widgets - Qml - Quick - QuickControls2 - QuickTemplates2 - Multimedia - Concurrent - LinguistTools) - -# The following should be moved to conan. -# But so far we're just adding libs from the vendor folder this way. -# statusgo lib is registered globaly - /vendor/status-go/CMakeLists.txt -SET(STATUS_GO_LIB statusgo_shared) -add_subdirectory(${CMAKE_SOURCE_DIR}/../vendor/status-go bin/status-go) - -add_subdirectory(projects) diff --git a/src-cpp-structure/CMakeSettings.json b/src-cpp-structure/CMakeSettings.json deleted file mode 100644 index fdb5ae764..000000000 --- a/src-cpp-structure/CMakeSettings.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "configurations": [ - { - "name": "Release", - "generator": "Ninja", - "configurationType": "RelWithDebInfo", - "buildRoot": "${projectDir}\\build\\${name}", - "installRoot": "${projectDir}\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "", - "inheritEnvironments": [ "msvc_x64_x64" ], - "variables": [] - }, - { - "name": "Debug", - "generator": "Ninja", - "configurationType": "Debug", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${projectDir}\\build\\${name}", - "installRoot": "${projectDir}\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "", - "variables": [] - } - ] -} \ No newline at end of file diff --git a/src-cpp-structure/cmake/app-linux.cmake b/src-cpp-structure/cmake/app-linux.cmake deleted file mode 100644 index 8d3c21774..000000000 --- a/src-cpp-structure/cmake/app-linux.cmake +++ /dev/null @@ -1,25 +0,0 @@ -add_executable(${PROJECT_NAME}) - -target_link_libraries( - ${PROJECT_NAME} PRIVATE - Qt5::Core - Qt5::Gui - Qt5::Widgets - Qt5::Quick - Qt5::Qml - Qt5::Quick - Qt5::QuickControls2 - Qt5::QuickTemplates2 - Qt5::Multimedia - Qt5::Concurrent - Status.Services - ${STATUS_GO_LIB} - ) - -file(GLOB_RECURSE SOURCES - "*.h" - "*.cpp" - ${STATUS_RCC} - ${STATUS_RESOURCES_QRC} - ${STATUS_QRC} - ) diff --git a/src-cpp-structure/cmake/app-mac.cmake b/src-cpp-structure/cmake/app-mac.cmake deleted file mode 100644 index 301244304..000000000 --- a/src-cpp-structure/cmake/app-mac.cmake +++ /dev/null @@ -1,31 +0,0 @@ -add_executable(${PROJECT_NAME} MACOSX_BUNDLE) - -find_library(FOUNDATION_FRAMEWORK Foundation) -find_library(IO_KIT_FRAMEWORK IOKit) - -target_link_libraries( - ${PROJECT_NAME} PRIVATE - Qt5::Core - Qt5::Gui - Qt5::Widgets - Qt5::Quick - Qt5::Qml - Qt5::Quick - Qt5::QuickControls2 - Qt5::QuickTemplates2 - Qt5::Multimedia - Qt5::Concurrent - ${FOUNDATION_FRAMEWORK} - ${IO_KIT_FRAMEWORK} - Status.Services - ${STATUS_GO_LIB} - ) - -file(GLOB_RECURSE SOURCES - "*.h" - "*.cpp" - "*.mm" - ${STATUS_RCC} - ${STATUS_RESOURCES_QRC} - ${STATUS_QRC} - ) diff --git a/src-cpp-structure/cmake/app-win.cmake b/src-cpp-structure/cmake/app-win.cmake deleted file mode 100644 index 3ed352ca4..000000000 --- a/src-cpp-structure/cmake/app-win.cmake +++ /dev/null @@ -1,25 +0,0 @@ -add_executable(${PROJECT_NAME} WIN32) - -target_link_libraries( - ${PROJECT_NAME} PRIVATE - Qt5::Core - Qt5::Gui - Qt5::Widgets - Qt5::Quick - Qt5::Qml - Qt5::Quick - Qt5::QuickControls2 - Qt5::QuickTemplates2 - Qt5::Multimedia - Qt5::Concurrent - Status.Services - ${STATUS_GO_LIB} - ) - -file(GLOB_RECURSE SOURCES - "*.h" - "*.cpp" - ${STATUS_RCC} - ${STATUS_RESOURCES_QRC} - ${STATUS_QRC} - ) diff --git a/src-cpp-structure/cmake/conan.cmake b/src-cpp-structure/cmake/conan.cmake deleted file mode 100644 index 8ad2539de..000000000 --- a/src-cpp-structure/cmake/conan.cmake +++ /dev/null @@ -1,27 +0,0 @@ -set(CONAN_WORKING_DIR ${CMAKE_BINARY_DIR}/conan) - -if (EXISTS ${CONAN_WORKING_DIR}) - file(REMOVE_RECURSE ${CONAN_WORKING_DIR}) -endif () - -file(MAKE_DIRECTORY ${CONAN_WORKING_DIR}) - -if (${CMAKE_BUILD_TYPE} STREQUAL Debug) - set(CONAN_PROFILE ${PROJECT_SOURCE_DIR}/conan-debug-profile) -else () - set(CONAN_PROFILE ${PROJECT_SOURCE_DIR}/conan-release-profile) -endif () - -execute_process( - COMMAND conan install ${PROJECT_SOURCE_DIR} -pr=${CONAN_PROFILE} - WORKING_DIRECTORY ${CONAN_WORKING_DIR} - RESULT_VARIABLE CONAN_RESULT - ) - -if (NOT ${CONAN_RESULT} EQUAL 0) - message(FATAL_ERROR "Conan failed: ${CONAN_RESULT}.") -endif () - -include(${CONAN_WORKING_DIR}/conanbuildinfo.cmake) - -conan_basic_setup(KEEP_RPATHS) diff --git a/src-cpp-structure/cmake/project-config.cmake b/src-cpp-structure/cmake/project-config.cmake deleted file mode 100644 index 687a94356..000000000 --- a/src-cpp-structure/cmake/project-config.cmake +++ /dev/null @@ -1,19 +0,0 @@ -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -#set(CMAKE_AUTORCC ON) This is disabled because of `/ui/generate-rcc.go` script usage for generating .qrc and cmake command for .rcc - -# Set this to TRUE if you want to create .ts files and translations.qrc -set(UPDATE_TRANSLATIONS FALSE) - -if ($ENV{QTDIR} LESS_EQUAL "") - message(FATAL_ERROR "Please set the path to your Qt dir as `QTDIR` variable in your ENV. Example: QTDIR=/Qt/Qt5.14.2/5.14.2/clang_64") -endif() - -message("Located QtDir: " $ENV{QTDIR}) -set(CMAKE_PREFIX_PATH $ENV{QTDIR}) - -add_definitions(-DSTATUS_SOURCE_DIR="${CMAKE_SOURCE_DIR}") -add_definitions(-DSTATUS_DEVELOPMENT=true) diff --git a/src-cpp-structure/cmake/services-linux.cmake b/src-cpp-structure/cmake/services-linux.cmake deleted file mode 100644 index d12afa8d2..000000000 --- a/src-cpp-structure/cmake/services-linux.cmake +++ /dev/null @@ -1,10 +0,0 @@ -target_link_libraries( - ${PROJECT_NAME} PRIVATE - Qt5::Core - Status.Backend - ) - -file(GLOB_RECURSE SOURCES - "*.h" - "*.cpp" - ) diff --git a/src-cpp-structure/cmake/services-mac.cmake b/src-cpp-structure/cmake/services-mac.cmake deleted file mode 100644 index efd1dc1cf..000000000 --- a/src-cpp-structure/cmake/services-mac.cmake +++ /dev/null @@ -1,22 +0,0 @@ -find_library(FOUNDATION_FRAMEWORK Foundation) -find_library(IO_KIT_FRAMEWORK IOKit) -find_library(SECURITY_FRAMEWORK Security) -find_library(CORE_SERVICES_FRAMEWORK CoreServices) -find_library(LOCAL_AUTHENTICATION_FRAMEWORK LocalAuthentication) - -target_link_libraries( - ${PROJECT_NAME} PRIVATE - Qt5::Core - ${FOUNDATION_FRAMEWORK} - ${IO_KIT_FRAMEWORK} - ${SECURITY_FRAMEWORK} - ${CORE_SERVICES_FRAMEWORK} - ${LOCAL_AUTHENTICATION_FRAMEWORK} - Status.Backend - ) - -file(GLOB_RECURSE SOURCES - "*.h" - "*.cpp" - "*.mm" - ) diff --git a/src-cpp-structure/cmake/services-win.cmake b/src-cpp-structure/cmake/services-win.cmake deleted file mode 100644 index d12afa8d2..000000000 --- a/src-cpp-structure/cmake/services-win.cmake +++ /dev/null @@ -1,10 +0,0 @@ -target_link_libraries( - ${PROJECT_NAME} PRIVATE - Qt5::Core - Status.Backend - ) - -file(GLOB_RECURSE SOURCES - "*.h" - "*.cpp" - ) diff --git a/src-cpp-structure/cmake/translation.cmake b/src-cpp-structure/cmake/translation.cmake deleted file mode 100644 index 0c8ccbf59..000000000 --- a/src-cpp-structure/cmake/translation.cmake +++ /dev/null @@ -1,36 +0,0 @@ -function(check_translations) - set(TRANSLATION_DIR "translations") - set(TRANSLATION_BASE_FILE "app_en_US.ts") - set(QM_RESOURCE_FILE "translations.qrc") - set(QM_FILES_FOLDER_NAME "i18n") - - if(NOT UPDATE_TRANSLATIONS) - file(REMOVE ${QM_RESOURCE_FILE}) - else() - # New translations will be added just here - set(TS_FILES - ${TRANSLATION_DIR}/${TRANSLATION_BASE_FILE} - ${TRANSLATION_DIR}/app_es_ES.ts - ) - - if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${TRANSLATION_DIR}/${TRANSLATION_BASE_FILE}) - - set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${QM_FILES_FOLDER_NAME}) - qt5_create_translation(QM_FILES ${TS_FILES} ${SOURCES}) - - endif () - - if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${QM_RESOURCE_FILE}) - - file(WRITE ${QM_RESOURCE_FILE} "\n \n") - - foreach(qm_file ${QM_FILES}) - get_filename_component(qm_name ${qm_file} NAME) - file(APPEND ${QM_RESOURCE_FILE} " ${QM_FILES_FOLDER_NAME}/${qm_name}\n") - endforeach(qm_file) - - file(APPEND ${QM_RESOURCE_FILE} " \n\n") - - endif () - endif () -endfunction() diff --git a/src-cpp-structure/conan-debug-profile b/src-cpp-structure/conan-debug-profile deleted file mode 100644 index 2aea5b36d..000000000 --- a/src-cpp-structure/conan-debug-profile +++ /dev/null @@ -1,4 +0,0 @@ -include(default) - -[settings] -build_type=Debug \ No newline at end of file diff --git a/src-cpp-structure/conan-release-profile b/src-cpp-structure/conan-release-profile deleted file mode 100644 index e5cd0fbd0..000000000 --- a/src-cpp-structure/conan-release-profile +++ /dev/null @@ -1,4 +0,0 @@ -include(default) - -[settings] -build_type=Release \ No newline at end of file diff --git a/src-cpp-structure/conanfile.py b/src-cpp-structure/conanfile.py deleted file mode 100644 index 9e555dd45..000000000 --- a/src-cpp-structure/conanfile.py +++ /dev/null @@ -1,10 +0,0 @@ -from conans import ConanFile - -class StatusConan(ConanFile): - name = "status-desktop" - settings = "os", "compiler", "build_type" - generators = "cmake" - - def requirements(self): - self.requires("di/1.2.0") - self.requires("gtest/1.10.0") \ No newline at end of file diff --git a/src-cpp-structure/projects/App/Boot/AppController.cpp b/src-cpp-structure/projects/App/Boot/AppController.cpp deleted file mode 100644 index 79892454a..000000000 --- a/src-cpp-structure/projects/App/Boot/AppController.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "AppController.h" - -#include "DI.h" -#include "../Core/Engine.h" -#include "../Core/StatusSyntaxHighlighter.h" -#include "../Core/SingleInstance.h" -#include "../Common/Utils.h" -#include "../Global/LocalAppSettings.h" -#include "../Global/LocalAccountSettings.h" - -#include "AppWindow.h" -#include "../Modules/ModuleBuilder.h" - -#include -#include -//#include <- include for QTranslator - -using namespace Status; - -AppController::AppController() -{ -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#endif - - QGuiApplication::setOrganizationName("Status"); - QGuiApplication::setOrganizationDomain("status.im"); - QGuiApplication::setApplicationName("Status Desktop"); - - Status::Logger::init(); - Utils::ensureDirectories(); -} - -void registerTypes() -{ - // Once we fully move to c++ we should include the following line instead the line below it (it's here just to align with the current qml files). - // qmlRegisterType("AppWindow", 0 , 1, "AppWindow"); - qmlRegisterType("DotherSide", 0 , 1, "StatusWindow"); - qmlRegisterType("DotherSide", 0, 1, "StatusSyntaxHighlighter"); -} - -void registerResources() -{ - Engine::instance()->addImportPath("qrc:/./StatusQ/src"); - Engine::instance()->addImportPath("qrc:/./imports"); - Engine::instance()->addImportPath("qrc:/./app"); - - // This will be removed once we completely move to c++, it's here to align with the current qml code. - Engine::instance()->rootContext()->setContextProperty("cppApp", true); - Engine::instance()->rootContext()->setContextProperty("production", STATUS_DEVELOPMENT); - - Engine::instance()->rootContext()->setContextProperty("localAppSettings", &LocalAppSettings::instance()); - Engine::instance()->rootContext()->setContextProperty("localAccountSettings", &LocalAccountSettings::instance()); - - QString statusSourceDir(STATUS_SOURCE_DIR); - QResource::registerResource(statusSourceDir + "/../resources.rcc"); -} - -int AppController::exec(int& argc, char** argv) -{ - int code; - - registerTypes(); - - try - { - QGuiApplication app(argc, argv); - - // This is here just to check loading translation on the cmake side, - // will be handled much better later. - // - // QTranslator translator; - // const QString baseName = "app_es_ES"; - // if (translator.load(baseName, QLatin1String(":/i18n"))){ - // app.installTranslator(&translator); - // } - - auto md5DataDir = QString(QCryptographicHash::hash(Utils::defaultDataDir().toLatin1(), QCryptographicHash::Md5).toHex()); - auto openUri = ""; // CLI uri should be used here ("status-im:// URI to open a chat or other") - auto singleInstance = std::make_unique(md5DataDir, openUri); - - if (!singleInstance->isFirstInstance()) - { - auto err = "Terminating the app as the second instance"; - throw std::runtime_error(err); - } - - auto rootModule = Injector.create()(); - rootModule->load(); - - registerResources(); - - AppWindow* appWindow = nullptr; - QString qmlFile = QStringLiteral("qrc:/main.qml"); - - auto handleAppWinCreation = [url = qmlFile, &appWindow, &singleInstance](QObject* obj, const QUrl& objUrl) { - if(url == objUrl.toString()) - { - if(obj) - { - AppWindow* appWindow = qobject_cast(obj); - QObject::connect(singleInstance.get(), &SingleInstance::secondInstanceDetected, [appWindow](){ - appWindow->makeTheAppActive(); - }); - - QObject::connect(singleInstance.get(), &SingleInstance::eventReceived, [](const QString& eventStr){ - qInfo() << "Received event: " << eventStr; - // We need to handle it here. - }); - } - else - { - auto err = "Failed to create: " + url; - throw std::runtime_error(err.toStdString()); - } - } - }; - - Engine::create(qmlFile); - QObject::connect(Engine::instance(), &Engine::objectCreated, &app, handleAppWinCreation); - - code = app.exec(); - } - catch (std::exception& e) - { - fprintf(stderr, "error: %s\n", e.what()); - code = -1; - } - catch (...) - { - fprintf(stderr, "unknown error\n"); - code = -1; - } - - return code; -} diff --git a/src-cpp-structure/projects/App/Boot/AppController.h b/src-cpp-structure/projects/App/Boot/AppController.h deleted file mode 100644 index d02daf72d..000000000 --- a/src-cpp-structure/projects/App/Boot/AppController.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -namespace Status -{ - class AppController final - { - public: - - AppController(); - int exec(int& argc, char** argv); - }; -} diff --git a/src-cpp-structure/projects/App/Boot/AppWindow.cpp b/src-cpp-structure/projects/App/Boot/AppWindow.cpp deleted file mode 100644 index 3ad24fba0..000000000 --- a/src-cpp-structure/projects/App/Boot/AppWindow.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "AppWindow.h" - -using namespace Status; - -AppWindow::AppWindow(QWindow *parent) - : QQuickWindow(parent), - m_isFullScreen(false) -{ - removeTitleBar(); - - connect(this, &QQuickWindow::windowStateChanged, [&](Qt::WindowState windowState) { - if (windowState == Qt::WindowNoState) { - removeTitleBar(); - m_isFullScreen = false; - emit isFullScreenChanged(); - } else if (windowState == Qt::WindowFullScreen) { - m_isFullScreen = true; - emit isFullScreenChanged(); - showTitleBar(); - } - }); -} - -void AppWindow::toggleFullScreen() -{ - if (m_isFullScreen) { - showNormal(); - } else { - showFullScreen(); - } -} - -bool AppWindow::isFullScreen() const -{ - return m_isFullScreen; -} - -void AppWindow::makeTheAppActive() -{ - show(); - raise(); - requestActivate(); -} - -void AppWindow::removeTitleBar() -{ -#ifdef Q_OS_MACOS - removeTitleBarMacOs(); -#endif -} - -void AppWindow::showTitleBar() -{ -#ifdef Q_OS_MACOS - showTitleBarMacOs(); -#endif -} diff --git a/src-cpp-structure/projects/App/Boot/AppWindow.h b/src-cpp-structure/projects/App/Boot/AppWindow.h deleted file mode 100644 index 338ca5cc6..000000000 --- a/src-cpp-structure/projects/App/Boot/AppWindow.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include - -namespace Status -{ - class AppWindow: public QQuickWindow - { - Q_OBJECT - - /*********************************************** - * Not Refactored, Just Taken From DOtherSide - ***********************************************/ - - Q_PROPERTY(bool isFullScreen READ isFullScreen NOTIFY isFullScreenChanged) - - public: - - explicit AppWindow(QWindow *parent = nullptr); - - Q_INVOKABLE void toggleFullScreen(); - - bool isFullScreen() const; - void makeTheAppActive(); - - Q_INVOKABLE void updatePosition() { - auto point = QPoint(screen()->geometry().center().x() - geometry().width() / 2, - screen()->geometry().center().y() - geometry().height() / 2); - if (point != this->position()) { - this->setPosition(point); - } - } - - signals: - void isFullScreenChanged(); - void secondInstanceDetected(); - - private: - void initCallbacks(); - void removeTitleBar(); - void showTitleBar(); -#ifdef Q_OS_MACOS - void removeTitleBarMacOs(); - void showTitleBarMacOs(); -#endif - - private: - bool m_isFullScreen; - }; -} diff --git a/src-cpp-structure/projects/App/Boot/AppWindow.mm b/src-cpp-structure/projects/App/Boot/AppWindow.mm deleted file mode 100644 index d606d0896..000000000 --- a/src-cpp-structure/projects/App/Boot/AppWindow.mm +++ /dev/null @@ -1,37 +0,0 @@ -#include "AppWindow.h" - -#include -#include -#include -#include -#include -#include -#include - -using namespace Status; - -void AppWindow::removeTitleBarMacOs() -{ - NSView *nsView = reinterpret_cast(this->winId()); - NSWindow *window = [nsView window]; - - window.titlebarAppearsTransparent = true; - window.titleVisibility = NSWindowTitleHidden; - window.styleMask |= NSWindowStyleMaskFullSizeContentView; - NSButton* close = [window standardWindowButton:NSWindowCloseButton]; - NSView* titleBarContainerView = close.superview.superview; - [titleBarContainerView setHidden:YES]; -} - -void AppWindow::showTitleBarMacOs() -{ - NSView *nsView = reinterpret_cast(this->winId()); - NSWindow *window = [nsView window]; - - window.titlebarAppearsTransparent = true; - window.titleVisibility = NSWindowTitleHidden; - window.styleMask |= NSWindowStyleMaskFullSizeContentView; - NSButton* close = [window standardWindowButton:NSWindowCloseButton]; - NSView* titleBarContainerView = close.superview.superview; - [titleBarContainerView setHidden:NO]; -} diff --git a/src-cpp-structure/projects/App/Boot/DI.h b/src-cpp-structure/projects/App/Boot/DI.h deleted file mode 100644 index 0597dad61..000000000 --- a/src-cpp-structure/projects/App/Boot/DI.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -namespace Status -{ - using namespace boost::di; - - const auto Injector = make_injector( - bind.to(), - bind.to(), - bind.to() - ); -} diff --git a/src-cpp-structure/projects/App/Boot/main.cpp b/src-cpp-structure/projects/App/Boot/main.cpp deleted file mode 100644 index dcaf64008..000000000 --- a/src-cpp-structure/projects/App/Boot/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "AppController.h" - -int main(int argc, char *argv[]) -{ - return Status::AppController().exec(argc, argv); -} diff --git a/src-cpp-structure/projects/App/CMakeLists.txt b/src-cpp-structure/projects/App/CMakeLists.txt deleted file mode 100644 index d701af917..000000000 --- a/src-cpp-structure/projects/App/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 3.18 FATAL_ERROR) - -project(Status.App DESCRIPTION "Main Status App Project") - -include("${CMAKE_SOURCE_DIR}/cmake/translation.cmake") - -# Building resource.rcc from multiple .qrc files -SET(STATUS_RCC ${CMAKE_SOURCE_DIR}/../resources.rcc) -SET(STATUS_QRC_SOURCE ${CMAKE_SOURCE_DIR}/../ui) -SET(STATUS_QRC ${STATUS_QRC_SOURCE}/resources.qrc) -SET(STATUS_RESOURCES_QRC ${CMAKE_SOURCE_DIR}/../resources/resources.qrc) - -add_custom_target(rcc ALL DEPENDS resources.rcc) -add_custom_command( - OUTPUT resources.rcc - COMMENT "Building resources.rcc" - COMMAND ${CMAKE_COMMAND} -E rm -f ${STATUS_RCC} - COMMAND ${CMAKE_COMMAND} -E rm -f ${STATUS_QRC} - COMMAND go run ${CMAKE_SOURCE_DIR}/../ui/generate-rcc.go -source=${STATUS_QRC_SOURCE} -output=${STATUS_QRC} - COMMAND rcc -binary --no-compress ${STATUS_QRC} ${STATUS_RESOURCES_QRC} -o ${STATUS_RCC} - VERBATIM - USES_TERMINAL - PRE_BUILD -) - -# Platform specific stuff are place in the corresponding .cmake file. -if (WIN32) - include("${CMAKE_SOURCE_DIR}/cmake/app-win.cmake") -elseif (APPLE) - include("${CMAKE_SOURCE_DIR}/cmake/app-mac.cmake") -elseif(UNIX) - include("${CMAKE_SOURCE_DIR}/cmake/app-linux.cmake") -endif () - -check_translations() - -add_dependencies(${PROJECT_NAME} ${STATUS_GO_LIB}) -add_dependencies(${PROJECT_NAME} Status.Services) - -target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/src-cpp-structure/projects/App/Common/Constants.h b/src-cpp-structure/projects/App/Common/Constants.h deleted file mode 100644 index 23260405c..000000000 --- a/src-cpp-structure/projects/App/Common/Constants.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -namespace Status::Constants -{ - const QString UserDataDirName = "Status"; - const QString StatusGoDataDirName = "data"; - const QString TmpDataDirName = "tmp"; - const QString LogsDataDirName = "logs"; - const QString QtDataDirName = "qt"; - const QString KeystoreDataDirName = "keystore"; - const QString GlobalSettingsFileName = "global"; -} diff --git a/src-cpp-structure/projects/App/Common/Utils.h b/src-cpp-structure/projects/App/Common/Utils.h deleted file mode 100644 index 3515cef99..000000000 --- a/src-cpp-structure/projects/App/Common/Utils.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "Constants.h" - -#include -#include - -namespace Status -{ - class Utils final { - public: - static QString applicationDir() - { - return qGuiApp->applicationDirPath(); - } - - static QString defaultDataDir() - { - auto d = QDir(); - if(STATUS_DEVELOPMENT){ - d = QDir(STATUS_SOURCE_DIR); - d.cdUp(); - } - else { - // We should handle paths for different platforms here in case of non development - } - - return d.absolutePath() + QDir::separator() + Constants::UserDataDirName; - } - - static QString statusGoDataDir() - { - return defaultDataDir() + QDir::separator() + Constants::StatusGoDataDirName; - } - - static QString keystoreDataDir() - { - return statusGoDataDir() + QDir::separator() + Constants::KeystoreDataDirName; - } - - static QString tmpDataDir() - { - return defaultDataDir() + QDir::separator() + Constants::TmpDataDirName; - } - - static QString logsDataDir() - { - return defaultDataDir() + QDir::separator() + Constants::LogsDataDirName; - } - - static QString qtDataDir() - { - return defaultDataDir() + QDir::separator() + Constants::QtDataDirName; - } - - static void ensureDirectories() - { - QDir d; - d.mkpath(defaultDataDir()); - d.mkpath(statusGoDataDir()); - d.mkpath(keystoreDataDir()); - d.mkpath(tmpDataDir()); - d.mkpath(logsDataDir()); - d.mkpath(qtDataDir()); - } - }; -} diff --git a/src-cpp-structure/projects/App/Core/Engine.cpp b/src-cpp-structure/projects/App/Core/Engine.cpp deleted file mode 100644 index 7198dda04..000000000 --- a/src-cpp-structure/projects/App/Core/Engine.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "Engine.h" - -using namespace Status; - -Engine* Engine::instance() -{ - static auto* engine = new Engine(); - return engine; -} - -Engine::Engine() - : QQmlApplicationEngine(nullptr) -{ -} - -Engine::~Engine() -{ -} - -std::shared_ptr Engine::load(const QString& qmlFile) -{ - return std::shared_ptr(new QQmlComponent(instance(), qmlFile, QQmlComponent::Asynchronous)); -} - -void Engine::create(const QString& qmlFile, QQmlContext* context) -{ - QObject* createdObj = nullptr; - auto component = Engine::load(qmlFile); - const auto status = component->status(); - - if (status == QQmlComponent::Ready) - { - createdObj = component->create(); - emit instance()->objectCreated(createdObj, qmlFile); - } - else if (status == QQmlComponent::Loading) - { - const auto create = [c = component, f = qmlFile](const QQmlComponent::Status status) mutable - { - if (status == QQmlComponent::Loading) - { - return; - } - - if (status == QQmlComponent::Ready) - { - emit instance()->objectCreated(c->create(), f); - } - else if (status == QQmlComponent::Null || status == QQmlComponent::Error) - { - emit instance()->objectCreated(nullptr, f); - } - }; - QObject::connect(component.get(), &QQmlComponent::statusChanged, create); - } - else - { - emit instance()->objectCreated(createdObj, qmlFile); - } -} diff --git a/src-cpp-structure/projects/App/Core/Engine.h b/src-cpp-structure/projects/App/Core/Engine.h deleted file mode 100644 index e077b605d..000000000 --- a/src-cpp-structure/projects/App/Core/Engine.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -namespace Status -{ - class Engine final : public QQmlApplicationEngine - { - Q_OBJECT - - public: - - static Engine* instance(); - - // Methos here are just a concept how we can have a single engine instance shared accross the app - // and use it for async instantiation for all qml files over the app. Also we can this way register - // context only within a certain file, not globally. - static std::shared_ptr load(const QString& qmlFile); - static void create(const QString& qmlFile, QQmlContext *context = nullptr); - - private: - explicit Engine(); - ~Engine(); - }; -} diff --git a/src-cpp-structure/projects/App/Core/GlobalEvents.cpp b/src-cpp-structure/projects/App/Core/GlobalEvents.cpp deleted file mode 100644 index 5d9d467fa..000000000 --- a/src-cpp-structure/projects/App/Core/GlobalEvents.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "GlobalEvents.h" - -using namespace Status; - -GlobalEvents& GlobalEvents::instance() -{ - static GlobalEvents events; - - return events; -} - -GlobalEvents::GlobalEvents() -{ -} - -GlobalEvents::~GlobalEvents() -{ -} diff --git a/src-cpp-structure/projects/App/Core/GlobalEvents.h b/src-cpp-structure/projects/App/Core/GlobalEvents.h deleted file mode 100644 index fce4755ab..000000000 --- a/src-cpp-structure/projects/App/Core/GlobalEvents.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -namespace Status -{ - class GlobalEvents final : public QObject - { - Q_OBJECT - - public: - - static GlobalEvents& instance(); - - private: - explicit GlobalEvents(); - ~GlobalEvents(); - - signals: - void nodeReady(const QString& error); - void nodeStarted(const QString& error); - void nodeStopped(const QString& error); - void nodeLogin(const QString& error); - void nodeCrashed(const QString& error); - }; -} diff --git a/src-cpp-structure/projects/App/Core/SignalsManager.cpp b/src-cpp-structure/projects/App/Core/SignalsManager.cpp deleted file mode 100644 index 3b8876e13..000000000 --- a/src-cpp-structure/projects/App/Core/SignalsManager.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "SignalsManager.h" - -#include "GlobalEvents.h" -#include - -#include "libstatus.h" - -using namespace Status; - -std::map SignalsManager::signalMap; - -SignalsManager* SignalsManager::instance() -{ - static auto* manager = new SignalsManager(); - return manager; -} - -SignalsManager::SignalsManager() - : QObject(nullptr) -{ - SetSignalEventCallback((void*)&SignalsManager::signalCallback); - - signalMap = { - {"node.ready", SignalType::NodeReady}, - {"node.started", SignalType::NodeStarted}, - {"node.stopped", SignalType::NodeStopped}, - {"node.login", SignalType::NodeLogin}, - {"node.crashed", SignalType::NodeCrashed} - }; -} - -SignalsManager::~SignalsManager() -{ -} - -void SignalsManager::processSignal(const QString& statusSignal) -{ - try - { - QJsonParseError json_error; - const QJsonDocument signalEventDoc(QJsonDocument::fromJson(statusSignal.toUtf8(), &json_error)); - if(json_error.error != QJsonParseError::NoError) - { - qWarning() << "Invalid signal received"; - return; - } - decode(signalEventDoc.object()); - } - catch(const std::exception& e) - { - qWarning() << "Error decoding signal, err: ", e.what(); - return; - } -} - -void SignalsManager::decode(const QJsonObject& signalEvent) -{ - SignalType signalType(Unknown); - if(!signalMap.count(signalEvent["type"].toString())) - { - qWarning() << "Unknown signal received: " << signalEvent["type"].toString(); - return; - } - - signalType = signalMap[signalEvent["type"].toString()]; - auto signalError = signalEvent["event"]["error"].toString(); - - switch(signalType) - { - // TODO: create extractor functions like in nim - case NodeLogin: - emit GlobalEvents::instance().nodeLogin(signalError); - break; - case NodeReady: - emit GlobalEvents::instance().nodeReady(signalError); - break; - case NodeStarted: - emit GlobalEvents::instance().nodeStarted(signalError); - break; - case NodeStopped: - emit GlobalEvents::instance().nodeStopped(signalError); - break; - case NodeCrashed: { - qWarning() << "node.crashed, error: " << signalError; - emit GlobalEvents::instance().nodeCrashed(signalError); - break; - } - default: - qWarning() << "Signal decoding not implemented: " << signalEvent; -} -} - -void SignalsManager::signalCallback(const char* data) -{ - QtConcurrent::run(instance(), &SignalsManager::processSignal, QString(data)); -} diff --git a/src-cpp-structure/projects/App/Core/SignalsManager.h b/src-cpp-structure/projects/App/Core/SignalsManager.h deleted file mode 100644 index 739da9100..000000000 --- a/src-cpp-structure/projects/App/Core/SignalsManager.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -namespace Status -{ - enum SignalType - { - Unknown, - NodeLogin, - NodeReady, - NodeStarted, - NodeStopped, - NodeCrashed - }; - - class SignalsManager final : public QObject - { - Q_OBJECT - - public: - - static SignalsManager* instance(); - - private: - explicit SignalsManager(); - ~SignalsManager(); - - private: - static std::map signalMap; - static void signalCallback(const char* data); - void processSignal(const QString& ev); - void decode(const QJsonObject& signalEvent); - }; - -} diff --git a/src-cpp-structure/projects/App/Core/SingleInstance.cpp b/src-cpp-structure/projects/App/Core/SingleInstance.cpp deleted file mode 100644 index 8739dff59..000000000 --- a/src-cpp-structure/projects/App/Core/SingleInstance.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "SingleInstance.h" - -#include - -using namespace Status; - -namespace { - const int ReadWriteTimeoutMs = 1000; -} - -SingleInstance::SingleInstance(const QString &uniqueName, const QString &eventStr, QObject *parent) - : QObject(parent) - , m_localServer(new QLocalServer(this)) -{ - QString socketName = uniqueName; - -#ifndef Q_OS_WIN - socketName = QString("/tmp/%1").arg(socketName); -#endif - - QLocalSocket localSocket; - localSocket.connectToServer(socketName); - - // the first instance start will be delayed by this timeout (ms) to ensure there are no other instances. - // note: this is an ad-hoc timeout value selected based on prior experience. - if (!localSocket.waitForConnected(100)) { - connect(m_localServer, &QLocalServer::newConnection, this, &SingleInstance::handleNewConnection); - // on *nix a crashed process will leave /tmp/xyz file preventing to start a new server. - // therefore, if we were unable to connect, then we assume the server died and we need to clean up. - // p.s. on Windows, this function does nothing. - QLocalServer::removeServer(socketName); - if (!m_localServer->listen(socketName)) { - qWarning() << "QLocalServer::listen(" << socketName << ") failed"; - } - } else if (!eventStr.isEmpty()) { - localSocket.write(eventStr.toUtf8() + '\n'); - localSocket.waitForBytesWritten(ReadWriteTimeoutMs); - } -} - -SingleInstance::~SingleInstance() -{ - if (m_localServer->isListening()) { - m_localServer->close(); - } -} - -bool SingleInstance::isFirstInstance() const -{ - return m_localServer->isListening(); -} - -void SingleInstance::handleNewConnection() -{ - emit secondInstanceDetected(); - - auto socket = m_localServer->nextPendingConnection(); - if (socket->waitForReadyRead(ReadWriteTimeoutMs) && socket->canReadLine()) { - auto event = socket->readLine(); - emit eventReceived(QString::fromUtf8(event)); - } - - socket->deleteLater(); -} diff --git a/src-cpp-structure/projects/App/Core/SingleInstance.h b/src-cpp-structure/projects/App/Core/SingleInstance.h deleted file mode 100644 index d933d84f0..000000000 --- a/src-cpp-structure/projects/App/Core/SingleInstance.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -class QLocalServer; - -namespace Status { - - class SingleInstance : public QObject - { - Q_OBJECT - - public: - // uniqueName - the name of named pipe - // eventStr - optional event to send if another instance is detected - explicit SingleInstance(const QString& uniqueName, const QString& eventStr, QObject* parent = nullptr); - ~SingleInstance() override; - - bool isFirstInstance() const; - - signals: - void secondInstanceDetected(); - void eventReceived(const QString& eventStr); - - private slots: - void handleNewConnection(); - - private: - QLocalServer* m_localServer; - }; -} diff --git a/src-cpp-structure/projects/App/Core/StatusSyntaxHighlighter.cpp b/src-cpp-structure/projects/App/Core/StatusSyntaxHighlighter.cpp deleted file mode 100644 index c2665657f..000000000 --- a/src-cpp-structure/projects/App/Core/StatusSyntaxHighlighter.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "StatusSyntaxHighlighter.h" - -using namespace Status; - -StatusSyntaxHighlighter::StatusSyntaxHighlighter(QTextDocument* parent) - : QSyntaxHighlighter(parent) -{ - HighlightingRule rule; - - //BOLD - singlelineBoldFormat.setFontWeight(QFont::Bold); - rule.pattern = QRegularExpression(QStringLiteral("\\*\\*(.*?)\\*\\*")); - rule.format = singlelineBoldFormat; - highlightingRules.append(rule); - //BOLD - - //ITALIC - singleLineItalicFormat.setFontItalic(true); - rule.pattern = QRegularExpression(QStringLiteral("\\*(.*?)\\*")); - rule.format = singleLineItalicFormat; - highlightingRules.append(rule); - //ITALIC - - //CODE - singlelineCodeBlockFormat.setFontFamily("Roboto Mono"); - rule.pattern = QRegularExpression(QStringLiteral("\\`(.*?)\\`")); - rule.format = singlelineCodeBlockFormat; - highlightingRules.append(rule); - //CODE - - //STRIKETHROUGH - singleLineStrikeThroughFormat.setFontStrikeOut(true); - rule.pattern = QRegularExpression(QStringLiteral("\\~+(.*?)\\~+")); - rule.format = singleLineStrikeThroughFormat; - highlightingRules.append(rule); - //STRIKETHROUGH - - //CODE BLOCK - multiLineCodeBlockFormat.setFontFamily("Roboto Mono"); - rule.pattern = QRegularExpression(QStringLiteral("\\`\\`\\`(.*?)\\`\\`\\`")); - rule.format = multiLineCodeBlockFormat; - highlightingRules.append(rule); - //CODE BLOCK -} - -void StatusSyntaxHighlighter::highlightBlock(const QString& text) -{ - for(const HighlightingRule& rule : qAsConst(highlightingRules)) - { - QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text); - while(matchIterator.hasNext()) - { - QRegularExpressionMatch match = matchIterator.next(); - setFormat(match.capturedStart(), match.capturedLength(), rule.format); - } - } - setCurrentBlockState(0); -} - -QQuickTextDocument* StatusSyntaxHighlighterHelper::quickTextDocument() const -{ - return m_quicktextdocument; -} - -void StatusSyntaxHighlighterHelper::setQuickTextDocument(QQuickTextDocument* quickTextDocument) -{ - m_quicktextdocument = quickTextDocument; - if(m_quicktextdocument) - { - new StatusSyntaxHighlighter(m_quicktextdocument->textDocument()); - } -} diff --git a/src-cpp-structure/projects/App/Core/StatusSyntaxHighlighter.h b/src-cpp-structure/projects/App/Core/StatusSyntaxHighlighter.h deleted file mode 100644 index 58cd2c9e3..000000000 --- a/src-cpp-structure/projects/App/Core/StatusSyntaxHighlighter.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace Status { - - class StatusSyntaxHighlighter : public QSyntaxHighlighter - { - Q_OBJECT - - public: - StatusSyntaxHighlighter(QTextDocument* parent = nullptr); - - protected: - void highlightBlock(const QString& text) override; - - private: - struct HighlightingRule - { - QRegularExpression pattern; - QTextCharFormat format; - }; - QVector highlightingRules; - - QTextCharFormat singlelineBoldFormat; - QTextCharFormat singleLineItalicFormat; - QTextCharFormat singlelineCodeBlockFormat; - QTextCharFormat singleLineStrikeThroughFormat; - QTextCharFormat multiLineCodeBlockFormat; - }; - - class StatusSyntaxHighlighterHelper : public QObject - { - Q_OBJECT - Q_PROPERTY(QQuickTextDocument* quickTextDocument READ quickTextDocument WRITE setQuickTextDocument NOTIFY - quickTextDocumentChanged) - public: - StatusSyntaxHighlighterHelper(QObject* parent = nullptr) - : QObject(parent) - , m_quicktextdocument(nullptr) - { } - - QQuickTextDocument* quickTextDocument() const; - void setQuickTextDocument(QQuickTextDocument* quickTextDocument); - signals: - void quickTextDocumentChanged(); - - private: - QQuickTextDocument* m_quicktextdocument; - }; -} diff --git a/src-cpp-structure/projects/App/Global/LocalAccountSettings.cpp b/src-cpp-structure/projects/App/Global/LocalAccountSettings.cpp deleted file mode 100644 index 08f15334c..000000000 --- a/src-cpp-structure/projects/App/Global/LocalAccountSettings.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "LocalAccountSettings.h" - -#include "../Common/Utils.h" - -using namespace Status; - -LocalAccountSettings& LocalAccountSettings::instance() -{ - static LocalAccountSettings laSettings; - - return laSettings; -} - -LocalAccountSettings::LocalAccountSettings() -{ -} - -LocalAccountSettings::~LocalAccountSettings() -{ -} - -void LocalAccountSettings::setFileName(const QString& fileName) -{ - auto filePath = Utils::qtDataDir() + QDir::separator() + fileName; - LocalAccountSettings::instance().settings.reset(new QSettings(filePath, QSettings::IniFormat)); -} - -bool LocalAccountSettings::containsKey(const QString& key) -{ - return LocalAccountSettings::instance().settings->contains(key); -} - -void LocalAccountSettings::removeKey(const QString& key) -{ - LocalAccountSettings::instance().settings->remove(key); -} - -QVariant LocalAccountSettingsKeys::getDefaultValue(const QString& key) { - static QMap defaults; - if (defaults.isEmpty()) - { - defaults.insert(storeToKeychain, LocalAccountSettingsPossibleValues::StoreToKeychain::NotNow); - defaults.insert(isKeycardEnabled, false); - } - - return defaults.value(key); -} diff --git a/src-cpp-structure/projects/App/Global/LocalAccountSettings.h b/src-cpp-structure/projects/App/Global/LocalAccountSettings.h deleted file mode 100644 index 7198bebfd..000000000 --- a/src-cpp-structure/projects/App/Global/LocalAccountSettings.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "SettingsProperties.h" - -#include - -namespace Status -{ - namespace LocalAccountSettingsKeys { - const QString storeToKeychain = "storeToKeychain"; - const QString isKeycardEnabled = "isKeycardEnabled"; - - QVariant getDefaultValue(const QString& key); - } - - namespace LocalAccountSettingsPossibleValues { - namespace StoreToKeychain { - const QString Store = "store"; - const QString NotNow = "notNow"; - const QString Never = "never"; - } - } - - class LocalAccountSettings final : public QObject - { - Q_OBJECT - - public: - - static LocalAccountSettings& instance(); - - void setFileName(const QString& fileName); - bool containsKey(const QString& key); - void removeKey(const QString& key); - - REGISTER_RW_PROPERTY(LocalAccountSettings, LocalAccountSettingsKeys, isKeycardEnabled, bool) - REGISTER_RW_PROPERTY(LocalAccountSettings, LocalAccountSettingsKeys, storeToKeychain, QString) - - private: - explicit LocalAccountSettings(); - ~LocalAccountSettings(); - - std::unique_ptr settings; - }; -} diff --git a/src-cpp-structure/projects/App/Global/LocalAppSettings.cpp b/src-cpp-structure/projects/App/Global/LocalAppSettings.cpp deleted file mode 100644 index 6870aa402..000000000 --- a/src-cpp-structure/projects/App/Global/LocalAppSettings.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "LocalAppSettings.h" - -#include "../Common/Utils.h" -#include "../Common/Constants.h" - -using namespace Status; - -LocalAppSettings& LocalAppSettings::instance() -{ - static LocalAppSettings laSettings; - - return laSettings; -} - -LocalAppSettings::LocalAppSettings() -{ - if(!settings) - { - auto filePath = Utils::qtDataDir() + QDir::separator() + Constants::GlobalSettingsFileName; - settings.reset(new QSettings(filePath, QSettings::IniFormat)); - } -} - -LocalAppSettings::~LocalAppSettings() -{ -} - -bool LocalAppSettings::containsKey(const QString& key) -{ - return LocalAppSettings::instance().settings->contains(key); -} - -void LocalAppSettings::removeKey(const QString& key) -{ - LocalAppSettings::instance().settings->remove(key); -} - -QVariant LocalAppSettingsKeys::getDefaultValue(const QString& key) { - static QMap defaults; - if (defaults.isEmpty()) - { - defaults.insert(locale, "en"); - defaults.insert(theme, 2); - defaults.insert(appWidth, 1232); - defaults.insert(appHeight, 770); - defaults.insert(appSizeInitialized, false); - } - - return defaults.value(key); -} diff --git a/src-cpp-structure/projects/App/Global/LocalAppSettings.h b/src-cpp-structure/projects/App/Global/LocalAppSettings.h deleted file mode 100644 index 8a6ba6f8a..000000000 --- a/src-cpp-structure/projects/App/Global/LocalAppSettings.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "SettingsProperties.h" - -#include - -namespace Status -{ - namespace LocalAppSettingsKeys { - const QString locale = "global/locale"; - const QString theme = "global/theme"; - const QString appWidth = "global/app_width"; - const QString appHeight = "global/app_height"; - const QString appSizeInitialized = "global/app_size_initialized"; - - QVariant getDefaultValue(const QString& key); - } - - class LocalAppSettings final : public QObject - { - Q_OBJECT - - public: - - static LocalAppSettings& instance(); - - bool containsKey(const QString& key); - void removeKey(const QString& key); - - REGISTER_RW_PROPERTY(LocalAppSettings, LocalAppSettingsKeys, locale, QString) - REGISTER_RW_PROPERTY(LocalAppSettings, LocalAppSettingsKeys, theme, int) - REGISTER_RW_PROPERTY(LocalAppSettings, LocalAppSettingsKeys, appWidth, int) - REGISTER_RW_PROPERTY(LocalAppSettings, LocalAppSettingsKeys, appHeight, int) - REGISTER_RW_PROPERTY(LocalAppSettings, LocalAppSettingsKeys, appSizeInitialized, bool) - - private: - explicit LocalAppSettings(); - ~LocalAppSettings(); - - std::unique_ptr settings; - }; -} diff --git a/src-cpp-structure/projects/App/Global/SettingsProperties.h b/src-cpp-structure/projects/App/Global/SettingsProperties.h deleted file mode 100644 index 60883a33e..000000000 --- a/src-cpp-structure/projects/App/Global/SettingsProperties.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -template -[[nodiscard]] T extractValue(const QVariant& value) -{ - if constexpr (std::is_same_v) - return value.toBool(); - else if constexpr (std::is_same_v) - return value.toString(); - else if constexpr (std::is_same_v) - return value.toInt(); - else if constexpr (std::is_same_v) - return value.toFloat(); - return T(); -} - -#define REGISTER_RW_PROPERTY(class, nspace, key, type) \ - Q_SIGNALS: \ - void key##Changed(); \ - public: \ - Q_PROPERTY(type key READ get_##key WRITE set_##key NOTIFY key##Changed) \ - [[nodiscard]] type get_##key() const \ - { \ - if(!class::instance().settings || \ - class::instance().settings->fileName().isEmpty()) \ - { \ - qFatal("Please set settings file name first"); \ - return type(); \ - } \ - auto def = nspace::getDefaultValue(nspace::key); \ - return extractValue(class::instance().settings->value(nspace::key, def)); \ - } \ - void set_##key(const type& value) \ - { \ - if(!class::instance().settings || \ - class::instance().settings->fileName().isEmpty()) \ - { \ - qFatal("Please set settings file name first"); \ - return; \ - } \ - class::instance().settings->setValue(nspace::key, value); \ - emit key##Changed(); \ - } diff --git a/src-cpp-structure/projects/App/Modules/Module.cpp b/src-cpp-structure/projects/App/Modules/Module.cpp deleted file mode 100644 index fca1b9304..000000000 --- a/src-cpp-structure/projects/App/Modules/Module.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "Module.h" - -#include "Startup/Module.h" - -using namespace Status::Modules; - -RootModule::RootModule(Startup::ModuleBuilder moduleBuilder) - : m_startupModuleBuilder(std::move(moduleBuilder)) -{ -} - -void RootModule::load() -{ - m_startupModule = m_startupModuleBuilder(shared_from_this()); - m_startupModule->load(); -} - -void RootModule::startupDidLoad() -{ - m_startupModule->emitStartUpUIRaisedSignal(); -} - -void RootModule::userLoggedIn() -{ - -} diff --git a/src-cpp-structure/projects/App/Modules/Module.h b/src-cpp-structure/projects/App/Modules/Module.h deleted file mode 100644 index 8bb50bd70..000000000 --- a/src-cpp-structure/projects/App/Modules/Module.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "ModuleInterface.h" -#include "Startup/ModuleBuilder.h" - -#include - -namespace Status::Modules -{ - - class RootModule final : public ModuleAccessInterface - , public Startup::ModuleDelegateInterface - , public std::enable_shared_from_this - { - public: - RootModule(Startup::ModuleBuilder moduleBuilder); - - void load() override; - void startupDidLoad() override; - void userLoggedIn() override; - - private: - - Startup::ModuleBuilder m_startupModuleBuilder; - std::shared_ptr m_startupModule; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/ModuleBuilder.cpp b/src-cpp-structure/projects/App/Modules/ModuleBuilder.cpp deleted file mode 100644 index 94d792450..000000000 --- a/src-cpp-structure/projects/App/Modules/ModuleBuilder.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "ModuleBuilder.h" - -#include "Module.h" - -using namespace Status::Modules; - -ModuleBuilder::ModuleBuilder(Startup::ModuleBuilder moduleBuilder) - : m_startupModuleBuilder(std::move(moduleBuilder)) -{ -} - -std::shared_ptr ModuleBuilder::operator()() -{ - return std::make_shared(m_startupModuleBuilder); -} diff --git a/src-cpp-structure/projects/App/Modules/ModuleBuilder.h b/src-cpp-structure/projects/App/Modules/ModuleBuilder.h deleted file mode 100644 index aec9be51c..000000000 --- a/src-cpp-structure/projects/App/Modules/ModuleBuilder.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "ModuleInterface.h" -#include "Startup/ModuleBuilder.h" - -#include - -namespace Status::Modules -{ - - class ModuleBuilder final - { - public: - ModuleBuilder(Startup::ModuleBuilder moduleBuilder); - - [[nodiscard]] std::shared_ptr operator()(); - - private: - Startup::ModuleBuilder m_startupModuleBuilder; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/ModuleInterface.h b/src-cpp-structure/projects/App/Modules/ModuleInterface.h deleted file mode 100644 index a65c2b61f..000000000 --- a/src-cpp-structure/projects/App/Modules/ModuleInterface.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -namespace Status::Modules -{ - class ModuleAccessInterface - { - public: - virtual ~ModuleAccessInterface() = default; - - virtual void load() = 0; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.cpp b/src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.cpp deleted file mode 100644 index d92411ee2..000000000 --- a/src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include - -#include "SectionItem.h" - -using namespace Status::Shared::Models; - -SectionItem::SectionItem(QObject* parent, - const QString& id, - SectionType sectionType, - const QString& name, - const QString& description, - const QString& image, - const QString& icon, - const QString& color, - bool active, - bool enabled, - bool amISectionAdmin, - bool hasNotification, - int notificationsCount, - bool isMember, - bool joined, - bool canJoin, - bool canManageUsers, - bool canRequestAccess, - int access, - bool ensOnly) - : QObject(parent) - , m_id(id) - , m_sectionType(sectionType) - , m_name(name) - , m_amISectionAdmin(amISectionAdmin) - , m_description(description) - , m_image(image) - , m_icon(icon) - , m_color(color) - , m_hasNotification(hasNotification) - , m_notificationsCount(notificationsCount) - , m_active(active) - , m_enabled(enabled) - , m_isMember(isMember) - , m_joined(joined) - , m_canJoin(canJoin) - , m_canManageUsers(canManageUsers) - , m_canRequestAccess(canRequestAccess) - , m_access(access) - , m_ensOnly(ensOnly) -{ } - -SectionType SectionItem::getSectionType() const -{ - return m_sectionType; -} - -const QString& SectionItem::getId() const -{ - return m_id; -} - -const QString& SectionItem::getName() const -{ - return m_name; -} - -bool SectionItem::getAmISectionAdmin() const -{ - return m_amISectionAdmin; -} - -const QString& SectionItem::getDescription() const -{ - return m_description; -} - -const QString& SectionItem::getImage() const -{ - return m_image; -} - -const QString& SectionItem::getIcon() const -{ - return m_icon; -} - -const QString& SectionItem::getColor() const -{ - return m_color; -} - -bool SectionItem::getHasNotification() const -{ - return m_hasNotification; -} - -int SectionItem::getNotificationsCount() const -{ - return m_notificationsCount; -} - -bool SectionItem::getIsActive() const -{ - return m_active; -} - -bool SectionItem::getIsEnabled() const -{ - return m_enabled; -} - -bool SectionItem::getIsMember() const -{ - return m_isMember; -} - -bool SectionItem::getHasJoined() const -{ - return m_joined; -} - -bool SectionItem::getCanJoin() const -{ - return m_canJoin; -} - -bool SectionItem::getCanManageUsers() const -{ - return m_canManageUsers; -} - -bool SectionItem::getCanRequestAccess() const -{ - return m_canRequestAccess; -} - -int SectionItem::getAccess() const -{ - return m_access; -} - -bool SectionItem::getIsEnsOnly() const -{ - return m_ensOnly; -} - -void SectionItem::setIsActive(bool isActive) -{ - if(m_active != isActive) - { - m_active = isActive; - activeChanged(); - } -} diff --git a/src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.h b/src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.h deleted file mode 100644 index bc9ef6c39..000000000 --- a/src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -#include - -namespace Status::Shared::Models -{ - enum SectionType - { - Unkown = -1, - Chat, - Community, - Wallet, - Browser, - ProfileSettings, - NodeManagement - }; - class SectionItem : public QObject - { - Q_OBJECT - Q_PROPERTY(QString id READ getId) - Q_PROPERTY(int sectionType READ getSectionType) - Q_PROPERTY(QString name READ getName) - Q_PROPERTY(bool amISectionAdmin READ getAmISectionAdmin) - Q_PROPERTY(QString description READ getDescription) - Q_PROPERTY(QString image READ getImage) - Q_PROPERTY(QString icon READ getIcon) - Q_PROPERTY(QString color READ getColor) - Q_PROPERTY(bool hasNotification READ getHasNotification) - Q_PROPERTY(int notificationsCount READ getNotificationsCount) - Q_PROPERTY(bool active READ getIsActive NOTIFY activeChanged) - Q_PROPERTY(bool enabled READ getIsEnabled) - Q_PROPERTY(bool joined READ getHasJoined) - Q_PROPERTY(bool isMember READ getIsMember) - Q_PROPERTY(bool canJoin READ getCanJoin) - Q_PROPERTY(bool canManageUsers READ getCanManageUsers) - Q_PROPERTY(bool canRequestAccess READ getCanRequestAccess) - Q_PROPERTY(int access READ getAccess) - Q_PROPERTY(bool ensOnly READ getIsEnsOnly) - - public: - SectionItem(QObject* parent = nullptr, - const QString& id = "", - SectionType sectionType = SectionType::Unkown, - const QString& name = "", - const QString& description = "", - const QString& image = "", - const QString& icon = "", - const QString& color = "", - bool active = false, - bool enabled = false, - bool amISectionAdmin = false, - bool hasNotification = false, - int notificationsCount = 0, - bool isMember = false, - bool joined = false, - bool canJoin = false, - bool canManageUsers = false, - bool canRequestAccess = false, - int access = 0, - bool ensOnly = false); - ~SectionItem() = default; - - // Getters - SectionType getSectionType() const; - const QString& getId() const; - const QString& getName() const; - bool getAmISectionAdmin() const; - const QString& getDescription() const; - const QString& getImage() const; - const QString& getIcon() const; - const QString& getColor() const; - bool getHasNotification() const; - int getNotificationsCount() const; - bool getIsActive() const; - bool getIsEnabled() const; - bool getIsMember() const; - bool getHasJoined() const; - bool getCanJoin() const; - bool getCanManageUsers() const; - bool getCanRequestAccess() const; - int getAccess() const; - bool getIsEnsOnly() const; - - // Setters - void setIsActive(bool isActive); - - signals: - void activeChanged(); - - private: - SectionType m_sectionType; - QString m_id; - QString m_name; - bool m_amISectionAdmin; - QString m_description; - QString m_image; - QString m_icon; - QString m_color; - bool m_hasNotification; - int m_notificationsCount; - bool m_active; - bool m_enabled; - bool m_isMember; - bool m_joined; - bool m_canJoin; - bool m_canManageUsers; - bool m_canRequestAccess; - int m_access; - bool m_ensOnly; - // membersModel: user_model.Model - // pendingRequestsToJoinModel: PendingRequestModel - }; -} diff --git a/src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.cpp b/src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.cpp deleted file mode 100644 index 5af9cdaad..000000000 --- a/src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "SectionModel.h" - -using namespace Status::Shared::Models; - -SectionModel::SectionModel(QObject* parent) - : QAbstractListModel(parent) -{ } - -QHash SectionModel::roleNames() const -{ - QHash roles; - - roles[Id] = "id"; - roles[SectionType] = "sectionType"; - roles[Name] = "name"; - roles[AmISectionAdmin] = "amISectionAdmin"; - roles[Description] = "description"; - roles[Image] = "image"; - roles[Icon] = "icon"; - roles[Color] = "color"; - roles[HasNotification] = "hasNotification"; - roles[NotificationsCount] = "notificationsCount"; - roles[Active] = "active"; - roles[Enabled] = "enabled"; - roles[Joined] = "joined"; - roles[IsMember] = "isMember"; - roles[CanJoin] = "canJoin"; - roles[CanManageUsers] = "canManageUsers"; - roles[CanRequestAccess] = "canRequestAccess"; - roles[Access] = "access"; - roles[EnsOnly] = "ensOnly"; - roles[MembersModel] = "members"; - roles[PendingRequestsToJoinModel] = "pendingRequestsToJoin"; - return roles; -} - -int SectionModel::rowCount(const QModelIndex& parent = QModelIndex()) const -{ - return m_items.size(); -} - -QVariant SectionModel::data(const QModelIndex& index, int role) const -{ - if(!index.isValid()) - { - return QVariant(); - } - - if(index.row() < 0 || index.row() >= m_items.size()) - { - return QVariant(); - } - - SectionItem* item = m_items.at(index.row()); - - switch(role) - { - case Id: return item->getId(); - case SectionType: return item->getSectionType(); - case Name: return item->getName(); - case AmISectionAdmin: return item->getAmISectionAdmin(); - case Description: return item->getDescription(); - case Image: return item->getImage(); - case Icon: return item->getIcon(); - case Color: return item->getColor(); - case HasNotification: return item->getHasNotification(); - case NotificationsCount: return item->getNotificationsCount(); - case Active: return item->getIsActive(); - case Enabled: return item->getIsEnabled(); - case Joined: return item->getHasJoined(); - case IsMember: return item->getIsMember(); - case CanJoin: return item->getCanJoin(); - case CanManageUsers: return item->getCanManageUsers(); - case CanRequestAccess: return item->getCanRequestAccess(); - case Access: return item->getAccess(); - case EnsOnly: return item->getIsEnsOnly(); - // To Do - case MembersModel: return QVariant(); - case PendingRequestsToJoinModel: return QVariant(); - } - - return QVariant(); -} - -void SectionModel::addItem(SectionItem* item) -{ - beginInsertRows(QModelIndex(), m_items.size(), m_items.size()); - m_items.append(item); - endInsertRows(); -} - -void SectionModel::setActiveSection(const QString& Id) -{ - - for(int i = 0; i < m_items.size(); ++i) - { - auto newIndex = createIndex(i, 0, nullptr); - if(m_items.at(i)->getIsActive()) - { - m_items.at(i)->setIsActive(false); - dataChanged(newIndex, newIndex, QVector(ModelRole::Active)); - } - if(m_items.at(i)->getId() == Id) - { - m_items.at(i)->setIsActive(true); - dataChanged(newIndex, newIndex, QVector(ModelRole::Active)); - } - } -} - -QPointer SectionModel::getActiveItem() -{ - SectionItem* activeItem = nullptr; - for(auto item : m_items) - { - if(item->getIsActive()) - { - activeItem = item; - break; - } - } - return activeItem; -} diff --git a/src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.h b/src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.h deleted file mode 100644 index f7af8e73a..000000000 --- a/src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "SectionItem.h" - -#include - -namespace Status::Shared::Models -{ - class SectionModel : public QAbstractListModel - { - Q_OBJECT - - public: - enum ModelRole - { - Id = Qt::UserRole + 1, - SectionType, - Name, - AmISectionAdmin, - Description, - Image, - Icon, - Color, - HasNotification, - NotificationsCount, - Active, - Enabled, - Joined, - IsMember, - CanJoin, - CanManageUsers, - CanRequestAccess, - Access, - EnsOnly, - MembersModel, - PendingRequestsToJoinModel - }; - - explicit SectionModel(QObject* parent = nullptr); - ~SectionModel() = default; - - QHash roleNames() const override; - int rowCount(const QModelIndex&) const override; - QVariant data(const QModelIndex& index, int role) const override; - - void addItem(SectionItem* item); - void setActiveSection(const QString& Id); - QPointer getActiveItem(); - - // To add other api's later as needed - - private: - QVector m_items; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Controller.cpp b/src-cpp-structure/projects/App/Modules/Startup/Controller.cpp deleted file mode 100644 index cbc0a4f30..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Controller.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "Controller.h" - -#include "../../Core/GlobalEvents.h" -#include "../../Common/Utils.h" - -#include - -using namespace Status::Modules::Startup; - -Controller::Controller(std::shared_ptr accountsService) - : QObject(nullptr) - , m_delegate(nullptr) - , m_accountsService(std::move(accountsService)) -{ -} - -void Controller::setDelegate(std::shared_ptr delegate) -{ - m_delegate = std::move(delegate); -} - -void Controller::init() -{ - m_accountsService->init(Utils::statusGoDataDir()); - - QObject::connect(&GlobalEvents::instance(), &GlobalEvents::nodeLogin, this, &Controller::onLogin); - QObject::connect(&GlobalEvents::instance(), &GlobalEvents::nodeStopped, this, &Controller::onNodeStopped); - QObject::connect(&GlobalEvents::instance(), &GlobalEvents::nodeReady, this, &Controller::onNodeReady); -} - -bool Controller::shouldStartWithOnboardingScreen() -{ - return m_accountsService->openedAccounts().isEmpty(); -} - -void Controller::onLogin(const QString& error) -{ - if(!error.isEmpty()) - { - qWarning() << error; - return; - } - - m_delegate->userLoggedIn(); -} - -void Controller::onNodeStopped(const QString& error) -{ - if(!error.isEmpty()) - { - qWarning() << error; - } - - m_accountsService->clear(); - m_delegate->emitLogOutSignal(); -} - -void Controller::onNodeReady(const QString& error) -{ - if(!error.isEmpty()) - { - qWarning() << error; - return; - } - - // In case of ready node we can do something here if needed. -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Controller.h b/src-cpp-structure/projects/App/Modules/Startup/Controller.h deleted file mode 100644 index bc5d91bf7..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Controller.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "ControllerInterface.h" - -#include - -namespace Status::Modules::Startup -{ - class Controller : public QObject, - public ControllerInterface - { - Q_OBJECT - - public: - explicit Controller(std::shared_ptr accountsService); - void setDelegate(std::shared_ptr delegate); - - // Controller Interface - void init() override; - bool shouldStartWithOnboardingScreen() override; - - private slots: - void onLogin(const QString& error); - void onNodeStopped(const QString& error); - void onNodeReady(const QString& error); - - private: - std::shared_ptr m_accountsService; - std::shared_ptr m_delegate; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/ControllerInterface.h b/src-cpp-structure/projects/App/Modules/Startup/ControllerInterface.h deleted file mode 100644 index e6be9ddd3..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/ControllerInterface.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -namespace Status::Modules::Startup -{ - class ControllerInterface - { - public: - virtual ~ControllerInterface() = default; - - virtual void init() = 0; - virtual bool shouldStartWithOnboardingScreen() = 0; - }; - - class ControllerDelegateInterface - { - public: - virtual void userLoggedIn() = 0; - virtual void emitLogOutSignal() = 0; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/Controller.cpp b/src-cpp-structure/projects/App/Modules/Startup/Login/Controller.cpp deleted file mode 100644 index 0212db4f8..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/Controller.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "Controller.h" - -#include "../../../Core/GlobalEvents.h" -#include "../../../Global/LocalAccountSettings.h" - -using namespace Status::Modules::Startup::Login; - -Controller::Controller(std::shared_ptr accountsService, - std::shared_ptr keychainService) - : QObject(nullptr) - , m_delegate(nullptr) - , m_accountsService(std::move(accountsService)) - , m_keychainService(std::move(keychainService)) -{ -} - -void Controller::setDelegate(std::shared_ptr delegate) -{ - m_delegate = std::move(delegate); -} - -void Controller::init() -{ - m_keychainService->subscribe(shared_from_this()); - - QObject::connect(&GlobalEvents::instance(), &GlobalEvents::nodeLogin, this, &Controller::onLogin); -} - -void Controller::onLogin(const QString& error) -{ - if(!error.isEmpty()) - { - m_delegate->emitAccountLoginError(error); - } -} - -QVector Controller::getOpenedAccounts() const -{ - return m_accountsService->openedAccounts(); -} - -Status::Accounts::AccountDto Controller::getSelectedAccount() const -{ - auto openedAccounts = getOpenedAccounts(); - foreach(const auto& acc, openedAccounts) - { - if(acc.keyUid == m_selectedAccountKeyUid) - { - return acc; - } - } - - // TODO: For situations like this, should be better to return a std::optional instead? - return Accounts::AccountDto(); -} - -void Controller::setSelectedAccountKeyUid(const QString& keyUid) -{ - m_selectedAccountKeyUid = keyUid; - -#ifdef Q_OS_MACOS - // Dealing with Keychain is the MacOS only feature - - auto selectedAccount = getSelectedAccount(); - LocalAccountSettings::instance().setFileName(selectedAccount.name); - - auto value = LocalAccountSettings::instance().get_storeToKeychain(); - if (value != LocalAccountSettingsPossibleValues::StoreToKeychain::Store) - return; - - m_keychainService->tryToObtainPassword(selectedAccount.name); -#endif -} - -void Controller::login(const QString& password) -{ - auto selectedAccount = Controller::getSelectedAccount(); - auto error = m_accountsService->login(selectedAccount, password); - if(!error.isEmpty()) - { - m_delegate->emitAccountLoginError(error); - } -} - -void Controller::onKeychainManagerError(const QString& errorType, const int errorCode, const QString& errorDescription) -{ - // We are notifying user only about keychain errors. - if (errorType == Keychain::ErrorTypeAuthentication) - return; - - LocalAccountSettings::instance().removeKey(LocalAccountSettingsKeys::storeToKeychain); - m_delegate->emitObtainingPasswordError(errorDescription); -} - -void Controller::onKeychainManagerSuccess(const QString& data) -{ - m_delegate->emitObtainingPasswordSuccess(data); -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/Controller.h b/src-cpp-structure/projects/App/Modules/Startup/Login/Controller.h deleted file mode 100644 index 18bce5c66..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/Controller.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "ControllerInterface.h" - -namespace Status::Modules::Startup::Login -{ - class Controller : public QObject - , public ControllerInterface - , public Keychain::Listener - , public std::enable_shared_from_this - { - Q_OBJECT - - public: - explicit Controller(std::shared_ptr accountsService, - std::shared_ptr keychainService); - void setDelegate(std::shared_ptr delegate); - - // Controller Interface - void init() override; - QVector getOpenedAccounts() const override; - void setSelectedAccountKeyUid(const QString& keyUid) override; - void login(const QString& password) override; - - // Listener Interface - void onKeychainManagerError(const QString& errorType, const int errorCode, - const QString& errorDescription) override; - void onKeychainManagerSuccess(const QString& data) override; - - private slots: - void onLogin(const QString& error); - - private: - Accounts::AccountDto getSelectedAccount() const; - - private: - std::shared_ptr m_accountsService; - std::shared_ptr m_keychainService; - std::shared_ptr m_delegate; - QString m_selectedAccountKeyUid; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/ControllerInterface.h b/src-cpp-structure/projects/App/Modules/Startup/Login/ControllerInterface.h deleted file mode 100644 index 810238d77..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/ControllerInterface.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace Status::Modules::Startup::Login -{ - class ControllerInterface - { - public: - virtual ~ControllerInterface() = default; - - virtual void init() = 0; - virtual QVector getOpenedAccounts() const = 0; - virtual void setSelectedAccountKeyUid(const QString& keyUid) = 0; - virtual void login(const QString& password) = 0; - }; - - class ControllerDelegateInterface - { - public: - virtual void emitAccountLoginError(const QString& error) = 0; - virtual void emitObtainingPasswordError(const QString& errorDescription) = 0; - virtual void emitObtainingPasswordSuccess(const QString& password) = 0; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/Item.cpp b/src-cpp-structure/projects/App/Modules/Startup/Login/Item.cpp deleted file mode 100644 index ad582c1c4..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/Item.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "Item.h" - -using namespace Status::Modules::Startup::Login; - -Item::Item(const QString& name, const QString& identicon, const QString& thumbnailImage, const QString& largeImage, - const QString& keyUid) - : m_name(name) - , m_identicon(identicon) - , m_thumbnailImage(thumbnailImage) - , m_largeImage(largeImage) - , m_keyUid(keyUid) -{ -} - -QString Item::getName() const -{ - return m_name; -} -QString Item::getIdenticon() const -{ - return m_identicon; -} -QString Item::getThumbnailImage() const -{ - return m_thumbnailImage; -} -QString Item::getLargeImage() const -{ - return m_largeImage; -} -QString Item::getKeyUid() const -{ - return m_keyUid; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/Item.h b/src-cpp-structure/projects/App/Modules/Startup/Login/Item.h deleted file mode 100644 index 7d6ead8d7..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/Item.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -namespace Status::Modules::Startup::Login -{ - class Item - { - public: - Item() {} - Item(const QString& name, const QString& identicon, const QString& thumbnailImage, const QString& largeImage, - const QString& keyUid); - QString getName() const; - QString getIdenticon() const; - QString getThumbnailImage() const; - QString getLargeImage() const; - QString getKeyUid() const; - - private: - QString m_name; - QString m_identicon; - QString m_thumbnailImage; - QString m_largeImage; - QString m_keyUid; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/Model.cpp b/src-cpp-structure/projects/App/Modules/Startup/Login/Model.cpp deleted file mode 100644 index e8672eaf5..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/Model.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "Model.h" - -using namespace Status::Modules::Startup::Login; - -Model::Model(QObject* parent) - : QAbstractListModel(parent) -{ -} - -QHash Model::roleNames() const -{ - QHash roles; - roles[Name] = "username"; - roles[Identicon] = "identicon"; - roles[ThumbnailImage] = "thumbnailImage"; - roles[LargeImage] = "largeImage"; - roles[KeyUid] = "keyUid"; - return roles; -} - -int Model::rowCount(const QModelIndex& parent) const -{ - Q_UNUSED(parent) - return m_items.size(); -} - -QVariant Model::data(const QModelIndex& index, int role) const -{ - if(!index.isValid()) - { - return QVariant(); - } - - if(index.row() < 0 || index.row() > m_items.size()) - { - return QVariant(); - } - - Item item = m_items[index.row()]; - - switch(role) - { - case Name: - return item.getName(); - case Identicon: - return item.getIdenticon(); - case ThumbnailImage: - return item.getThumbnailImage(); - case LargeImage: - return item.getLargeImage(); - case KeyUid: - return item.getKeyUid(); - } - - return QVariant(); -} - -void Model::setItems(QVector items) -{ - beginResetModel(); - m_items = std::move(items); - endResetModel(); -} - -Item Model::getItemAtIndex(const int index) const -{ - if(index < 0 || index >= m_items.size()) - { - return Item(); - } - - return m_items[index]; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/Model.h b/src-cpp-structure/projects/App/Modules/Startup/Login/Model.h deleted file mode 100644 index bcc42668b..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/Model.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "Item.h" - -namespace Status::Modules::Startup::Login -{ - class Model : public QAbstractListModel - { - Q_OBJECT - - public: - enum ModelRole - { - Name = Qt::UserRole + 1, - Identicon, - ThumbnailImage, - LargeImage, - KeyUid - }; - - explicit Model(QObject* parent = nullptr); - QHash roleNames() const; - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; - virtual QVariant data(const QModelIndex& index, int role) const; - void setItems(QVector items); - Item getItemAtIndex(const int index) const; - - private: - QVector m_items; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/Module.cpp b/src-cpp-structure/projects/App/Modules/Startup/Login/Module.cpp deleted file mode 100644 index c0ca48646..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/Module.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "Module.h" - -#include "Controller.h" -#include "View.h" - -#include "../../../Core/Engine.h" - -using namespace Status::Modules::Startup::Login; - -Module::Module(std::shared_ptr delegate, - std::shared_ptr controller, - std::shared_ptr view) - : m_delegate(std::move(delegate)) - , m_controller(std::move(controller)) - , m_view(std::move(view)) -{ -} - -void Module::load() -{ - Engine::instance()->rootContext()->setContextProperty("loginModule", m_view->getQObject()); - m_controller->init(); - m_view->load(); - - const QVector openedAccounts = m_controller->getOpenedAccounts(); - if(openedAccounts.size() > 0) - { - QVector items; - foreach(const Accounts::AccountDto& acc, openedAccounts) - { - QString thumbnailImage; - QString largeImage; - extractImages(acc, thumbnailImage, largeImage); - items << Item(acc.name, acc.identicon, thumbnailImage, largeImage, acc.keyUid); - } - - m_view->setModelItems(items); - - // set the first account as selected one - m_controller->setSelectedAccountKeyUid(items[0].getKeyUid()); - setSelectedAccount(items[0]); - } -} - -bool Module::isLoaded() -{ - return m_moduleLoaded; -} - -void Module::viewDidLoad() -{ - m_moduleLoaded = true; - m_delegate->loginDidLoad(); -} - -void Module::extractImages(const Accounts::AccountDto& account, QString& thumbnailImage, QString& largeImage) -{ - foreach(const Accounts::Image& img, account.images) - { - if(img.imgType == "thumbnail") - { - thumbnailImage = img.uri; - } - else if(img.imgType == "large") - { - largeImage = img.uri; - } - } -} - -void Module::setSelectedAccount(const Item& item) -{ - m_controller->setSelectedAccountKeyUid(item.getKeyUid()); - m_view->setSelectedAccount(item); -} - -void Module::login(const QString& password) -{ - m_controller->login(password); -} - -void Module::emitAccountLoginError(const QString& error) -{ - m_view->emitAccountLoginError(error); -} - -void Module::emitObtainingPasswordError(const QString& errorDescription) -{ - m_view->emitObtainingPasswordError(errorDescription); -} - -void Module::emitObtainingPasswordSuccess(const QString& password) -{ - m_view->emitObtainingPasswordSuccess(password); -} - diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/Module.h b/src-cpp-structure/projects/App/Modules/Startup/Login/Module.h deleted file mode 100644 index 0cd8f4cc2..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/Module.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "ModuleInterface.h" -#include "ControllerInterface.h" -#include "ViewInterface.h" - -namespace Status::Modules::Startup::Login -{ - - class Module final : public ModuleAccessInterface - , public ControllerDelegateInterface - , public ViewDelegateInterface - , public std::enable_shared_from_this - { - public: - Module(std::shared_ptr delegate, - std::shared_ptr controller, - std::shared_ptr view); - - // Module Access - void load() override; - bool isLoaded() override; - - // Controller Delegate - void emitAccountLoginError(const QString& error) override; - void emitObtainingPasswordError(const QString& errorDescription) override; - void emitObtainingPasswordSuccess(const QString& password) override; - - // View Delegate - void viewDidLoad() override; - void setSelectedAccount(const Item& item) override; - void login(const QString& password) override; - - private: - void checkIfModuleDidLoad(); - void extractImages(const Accounts::AccountDto& account, QString& thumbnailImage, QString& largeImage); - - private: - std::shared_ptr m_delegate; - std::shared_ptr m_controller; - std::shared_ptr m_view; - bool m_moduleLoaded {false}; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.cpp b/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.cpp deleted file mode 100644 index 316c66b39..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "ModuleBuilder.h" - -#include "Module.h" -#include "Controller.h" -#include "View.h" - -using namespace Status::Modules::Startup::Login; - -ModuleBuilder::ModuleBuilder(std::shared_ptr accountsService, - std::shared_ptr keychainService) - : m_accountsService(std::move(accountsService)) - , m_keychainService(std::move(keychainService)) -{ -} - -std::shared_ptr ModuleBuilder::operator()(std::shared_ptr delegate) { - - auto controller = std::make_shared(m_accountsService, m_keychainService); - auto view = std::make_shared(); - - auto module = std::make_shared(delegate, controller, view); - - controller->setDelegate(module); - view->setDelegate(module); - - return module; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.h b/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.h deleted file mode 100644 index 8a146a5ef..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "ModuleInterface.h" - -#include -#include - -#include - -namespace Status::Modules::Startup::Login -{ - class ModuleBuilder final - { - public: - ModuleBuilder(std::shared_ptr accountsService, - std::shared_ptr keychainService); - - [[nodiscard]] std::shared_ptr operator()(std::shared_ptr delegate); - - private: - std::shared_ptr m_accountsService; - std::shared_ptr m_keychainService; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleInterface.h b/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleInterface.h deleted file mode 100644 index 54b385bba..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleInterface.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -namespace Status::Modules::Startup::Login -{ - class ModuleAccessInterface - { - public: - virtual ~ModuleAccessInterface() = default; - - virtual void load() = 0; - virtual bool isLoaded() = 0; - }; - - class ModuleDelegateInterface - { - public: - virtual void loginDidLoad() = 0; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.cpp b/src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.cpp deleted file mode 100644 index 21420e3bf..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "SelectedAccount.h" - -using namespace Status::Modules::Startup::Login; - -SelectedAccount::SelectedAccount(QObject* parent) - : QObject(parent) -{ -} - -void SelectedAccount::setSelectedAccountData(const Item& item) -{ - m_item = item; -} - -QString SelectedAccount::getName() -{ - return m_item.getName(); -} - -QString SelectedAccount::getIdenticon() -{ - return m_item.getIdenticon(); -} - -QString SelectedAccount::getKeyUid() -{ - return m_item.getKeyUid(); -} - -QString SelectedAccount::getThumbnailImage() -{ - return m_item.getThumbnailImage(); -} - -QString SelectedAccount::getLargeImage() -{ - return m_item.getLargeImage(); -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.h b/src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.h deleted file mode 100644 index d46445313..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "Item.h" - -#include - -namespace Status::Modules::Startup::Login -{ - class SelectedAccount : public QObject - { - Q_OBJECT - - Q_PROPERTY(QString username READ getName CONSTANT) - Q_PROPERTY(QString identicon READ getIdenticon CONSTANT) - Q_PROPERTY(QString keyUid READ getKeyUid CONSTANT) - Q_PROPERTY(QString thumbnailImage READ getThumbnailImage CONSTANT) - Q_PROPERTY(QString largeImage READ getLargeImage CONSTANT) - - public: - explicit SelectedAccount(QObject* parent = nullptr); - - void setSelectedAccountData(const Item& item); - - QString getName(); - QString getIdenticon(); - QString getKeyUid(); - QString getThumbnailImage(); - QString getLargeImage(); - - private: - Item m_item; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/View.cpp b/src-cpp-structure/projects/App/Modules/Startup/Login/View.cpp deleted file mode 100644 index a0ddda722..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/View.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "View.h" - -#include "../../../Core/Engine.h" - -using namespace Status::Modules::Startup::Login; - -View::View() : QObject(nullptr) - , m_model(new Model(this)) - , m_selectedAccount(new SelectedAccount(this)) -{ -} - -void View::setDelegate(std::shared_ptr delegate) -{ - m_delegate = std::move(delegate); -} - -QObject* View::getQObject() -{ - Engine::instance()->setObjectOwnership(this, QQmlEngine::CppOwnership); - return this; -} - -void View::load() -{ - m_delegate->viewDidLoad(); -} - -Model* View::getModel() -{ - Engine::instance()->setObjectOwnership(m_model, QQmlEngine::CppOwnership); - return m_model; -} - -SelectedAccount* View::getSelectedAccount() -{ - Engine::instance()->setObjectOwnership(m_selectedAccount, QQmlEngine::CppOwnership); - return m_selectedAccount; -} - -void View::setModelItems(QVector accounts) -{ - m_model->setItems(std::move(accounts)); - emit modelChanged(); -} - -void View::setSelectedAccount(const Item& item) -{ - m_selectedAccount->setSelectedAccountData(item); - emit selectedAccountChanged(); -} - -void View::emitAccountLoginError(const QString& error) -{ - emit accountLoginError(error); -} - -void View::emitObtainingPasswordError(const QString& errorDescription) -{ - emit obtainingPasswordError(errorDescription); -} - -void View::emitObtainingPasswordSuccess(const QString& password) -{ - emit obtainingPasswordSuccess(password); -} - -void View::setSelectedAccountByIndex(const int index) -{ - Item item = m_model->getItemAtIndex(index); - m_delegate->setSelectedAccount(item); -} - -void View::login(const QString& password) -{ - m_delegate->login(password); -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/View.h b/src-cpp-structure/projects/App/Modules/Startup/Login/View.h deleted file mode 100644 index a7526dd12..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/View.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "ViewInterface.h" - -namespace Status::Modules::Startup::Login -{ - class View final : public QObject - , public ViewInterface - { - Q_OBJECT - - Q_PROPERTY(SelectedAccount* selectedAccount READ getSelectedAccount NOTIFY selectedAccountChanged) - Q_PROPERTY(Model* accountsModel READ getModel NOTIFY modelChanged) - - public: - explicit View(); - void setDelegate(std::shared_ptr delegate); - - // View Interface - QObject* getQObject() override; - void load() override; - Model* getModel() override; - void setModelItems(QVector accounts) override; - void setSelectedAccount(const Item& item) override; - void emitAccountLoginError(const QString& error) override; - void emitObtainingPasswordError(const QString& errorDescription) override; - void emitObtainingPasswordSuccess(const QString& password) override; - - Q_INVOKABLE void setSelectedAccountByIndex(const int index); - Q_INVOKABLE void login(const QString& password); - - signals: - void selectedAccountChanged(); - void modelChanged(); - void accountLoginError(const QString& error); - void obtainingPasswordError(const QString& errorDescription); - void obtainingPasswordSuccess(const QString& password); - - private: - SelectedAccount* getSelectedAccount(); - - private: - std::shared_ptr m_delegate; - Model* m_model {nullptr}; - SelectedAccount* m_selectedAccount {nullptr}; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Login/ViewInterface.h b/src-cpp-structure/projects/App/Modules/Startup/Login/ViewInterface.h deleted file mode 100644 index 56fa16711..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Login/ViewInterface.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "Model.h" -#include "SelectedAccount.h" - -#include - -#include - -namespace Status::Modules::Startup::Login -{ - class ViewInterface - { - public: - virtual ~ViewInterface() = default; - - virtual QObject* getQObject() = 0; - virtual void load() = 0; - virtual Model* getModel() = 0; - virtual void setModelItems(QVector accounts) = 0; - virtual void setSelectedAccount(const Item& item) = 0; - virtual void emitAccountLoginError(const QString& error) = 0; - virtual void emitObtainingPasswordError(const QString& errorDescription) = 0; - virtual void emitObtainingPasswordSuccess(const QString& password) = 0; - }; - - class ViewDelegateInterface - { - public: - virtual void viewDidLoad() = 0; - virtual void setSelectedAccount(const Item& item) = 0; - virtual void login(const QString& password) = 0; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Module.cpp b/src-cpp-structure/projects/App/Modules/Startup/Module.cpp deleted file mode 100644 index 01e5e43a8..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Module.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "Module.h" - -#include "Controller.h" -#include "View.h" - -#include "../../Core/Engine.h" - -using namespace Status::Modules::Startup; - -Module::Module(std::shared_ptr delegate, - std::shared_ptr controller, - std::shared_ptr view, - Onboarding::ModuleBuilder onboardingModuleBuilder, - Login::ModuleBuilder loginModuleBuilder) - : m_delegate(std::move(delegate)) - , m_controller(std::move(controller)) - , m_view(std::move(view)) - , m_onboardingModuleBuilder(std::move(onboardingModuleBuilder)) - , m_loginModuleBuilder(std::move(loginModuleBuilder)) -{ -} - -void Module::load() -{ - Engine::instance()->rootContext()->setContextProperty("startupModule", m_view->getQObject()); - m_controller->init(); - m_view->load(); -} - -void Module::checkIfModuleDidLoad() -{ - if(!m_onboardingModule->isLoaded()) - { - return; - } - - if(!m_loginModule->isLoaded()) - { - return; - } - - m_delegate->startupDidLoad(); -} - -void Module::viewDidLoad() -{ - AppState initialAppState(AppState::OnboardingState); - if(!m_controller->shouldStartWithOnboardingScreen()) - { - initialAppState = AppState::LoginState; - } - - m_view->setAppState(initialAppState); - - - m_onboardingModule = m_onboardingModuleBuilder(shared_from_this()); - m_loginModule = m_loginModuleBuilder(shared_from_this()); - - m_onboardingModule->load(); - m_loginModule->load(); - - checkIfModuleDidLoad(); -} - -void Module::onboardingDidLoad() -{ - checkIfModuleDidLoad(); -} - -void Module::loginDidLoad() -{ - checkIfModuleDidLoad(); -} - -void Module::userLoggedIn() -{ - m_delegate->userLoggedIn(); -} - -void Module::moveToAppState() -{ - m_view->setAppState(AppState::MainAppState); -} - -void Module::emitLogOutSignal() -{ - m_view->emitLogOut(); -} - -void Module::emitStartUpUIRaisedSignal() -{ - m_view->emitStartUpUIRaised(); -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Module.h b/src-cpp-structure/projects/App/Modules/Startup/Module.h deleted file mode 100644 index 0ca835a97..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Module.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "ModuleInterface.h" -#include "ControllerInterface.h" -#include "ViewInterface.h" - -#include "Onboarding/ModuleBuilder.h" -#include "Onboarding/ModuleInterface.h" -#include "Login/ModuleBuilder.h" -#include "Login/ModuleInterface.h" - -#include - -namespace Status::Modules::Startup -{ - - class Module final : public ModuleAccessInterface - , public ControllerDelegateInterface - , public ViewDelegateInterface - , public std::enable_shared_from_this - , public Onboarding::ModuleDelegateInterface - , public Login::ModuleDelegateInterface - { - public: - Module(std::shared_ptr delegate, - std::shared_ptr controller, - std::shared_ptr view, - Onboarding::ModuleBuilder onboardingModuleBuilder, - Login::ModuleBuilder loginModuleBuilder); - - // Module Access - void load() override; - void moveToAppState() override; - void emitStartUpUIRaisedSignal() override; - - // Controller Delegate - void userLoggedIn() override; - void emitLogOutSignal() override; - - // View Delegate - void viewDidLoad() override; - - // Onboarding Module Delegate - void onboardingDidLoad() override; - - // Login Module Delegate - void loginDidLoad() override; - - private: - void checkIfModuleDidLoad(); - - private: - std::shared_ptr m_delegate; - std::shared_ptr m_controller; - std::shared_ptr m_view; - - Onboarding::ModuleBuilder m_onboardingModuleBuilder; - std::shared_ptr m_onboardingModule; - - Login::ModuleBuilder m_loginModuleBuilder; - std::shared_ptr m_loginModule; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.cpp b/src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.cpp deleted file mode 100644 index 53722bcbe..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "ModuleBuilder.h" - -#include "Module.h" -#include "Controller.h" -#include "View.h" - -using namespace Status::Modules::Startup; - -ModuleBuilder::ModuleBuilder(std::shared_ptr accountsService, - Onboarding::ModuleBuilder onboardingModuleBuilder, - Login::ModuleBuilder loginModuleBuilder) - : m_accountsService(std::move(accountsService)) - , m_onboardingModuleBuilder(std::move(onboardingModuleBuilder)) - , m_loginModuleBuilder(std::move(loginModuleBuilder)) -{ -} - -std::shared_ptr ModuleBuilder::operator()(std::shared_ptr delegate) { - - auto controller = std::make_shared(m_accountsService); - auto view = std::make_shared(); - - auto module = std::make_shared(delegate, controller, view, - m_onboardingModuleBuilder, m_loginModuleBuilder); - - controller->setDelegate(module); - view->setDelegate(module); - - return module; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.h b/src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.h deleted file mode 100644 index 42214a889..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "ModuleInterface.h" -#include "Onboarding/ModuleBuilder.h" -#include "Login/ModuleBuilder.h" - -#include - -#include - -namespace Status::Modules::Startup -{ - class ModuleBuilder final - { - public: - ModuleBuilder(std::shared_ptr accountsService, - Onboarding::ModuleBuilder onboardingModuleBuilder, - Login::ModuleBuilder loginModuleBuilder); - - [[nodiscard]] std::shared_ptr operator()(std::shared_ptr delegate); - - private: - std::shared_ptr m_accountsService; - Onboarding::ModuleBuilder m_onboardingModuleBuilder; - Login::ModuleBuilder m_loginModuleBuilder; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/ModuleInterface.h b/src-cpp-structure/projects/App/Modules/Startup/ModuleInterface.h deleted file mode 100644 index 2293548aa..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/ModuleInterface.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -namespace Status::Modules::Startup -{ - class ModuleAccessInterface - { - public: - virtual ~ModuleAccessInterface() = default; - - virtual void load() = 0; - virtual void moveToAppState() = 0; - virtual void emitStartUpUIRaisedSignal() = 0; - }; - - class ModuleDelegateInterface - { - public: - virtual void startupDidLoad() = 0; - virtual void userLoggedIn() = 0; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.cpp b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.cpp deleted file mode 100644 index af89f8436..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "Controller.h" - -#include "../../../Core/GlobalEvents.h" - -using namespace Status::Modules::Startup::Onboarding; - -Controller::Controller(std::shared_ptr accountsService) - : QObject(nullptr) - , m_delegate(nullptr) - , m_accountsService(std::move(accountsService)) -{ -} - -void Controller::setDelegate(std::shared_ptr delegate) -{ - m_delegate = std::move(delegate); -} - -void Controller::init() -{ - QObject::connect(&GlobalEvents::instance(), &GlobalEvents::nodeLogin, this, &Controller::onLogin); -} - -void Controller::onLogin(const QString& error) -{ - if(!error.isEmpty()) - { - m_delegate->setupAccountError(); - } -} - -const QVector& Controller::getGeneratedAccounts() const -{ - return m_accountsService->generatedAccounts(); -} - -const Status::Accounts::GeneratedAccountDto& Controller::getImportedAccount() const -{ - return m_accountsService->getImportedAccount(); -} - -void Controller::setSelectedAccountByIndex(const int index) -{ - auto accounts = getGeneratedAccounts(); - m_selectedAccountId = accounts[index].id; -} - -void Controller::storeSelectedAccountAndLogin(const QString& password) -{ - if(!m_accountsService->setupAccount(m_selectedAccountId, password)) - { - m_delegate->setupAccountError(); - } -} - -QString Controller::validateMnemonic(const QString& mnemonic) -{ - return m_accountsService->validateMnemonic(mnemonic); -} - -void Controller::importMnemonic(const QString& mnemonic) -{ - if(m_accountsService->importMnemonic(mnemonic)) - { - m_selectedAccountId = getImportedAccount().id; - m_delegate->importAccountSuccess(); - } - else - { - m_delegate->importAccountError(); - } -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.h b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.h deleted file mode 100644 index 1e7c8b1d4..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "ControllerInterface.h" - -namespace Status::Modules::Startup::Onboarding -{ - class Controller : public QObject, - public ControllerInterface - { - Q_OBJECT - - public: - explicit Controller(std::shared_ptr accountsService); - void setDelegate(std::shared_ptr delegate); - - // Controller Interface - void init() override; - const QVector& getGeneratedAccounts() const override; - const Accounts::GeneratedAccountDto& getImportedAccount() const override; - void setSelectedAccountByIndex(const int index) override; - void storeSelectedAccountAndLogin(const QString& password) override; - QString validateMnemonic(const QString& mnemonic) override; - void importMnemonic(const QString& mnemonic) override; - - private slots: - void onLogin(const QString& error); - - private: - std::shared_ptr m_accountsService; - std::shared_ptr m_delegate; - QString m_selectedAccountId; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ControllerInterface.h b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ControllerInterface.h deleted file mode 100644 index 201b33c2d..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ControllerInterface.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include - -#include - -namespace Status::Modules::Startup::Onboarding -{ - class ControllerInterface - { - public: - virtual ~ControllerInterface() = default; - - virtual void init() = 0; - virtual const QVector& getGeneratedAccounts() const = 0; - virtual void setSelectedAccountByIndex(const int index) = 0; - virtual void storeSelectedAccountAndLogin(const QString& password) = 0; - virtual const Accounts::GeneratedAccountDto& getImportedAccount() const = 0; - virtual QString validateMnemonic(const QString& mnemonic) = 0; - virtual void importMnemonic(const QString& mnemonic) = 0; - }; - - class ControllerDelegateInterface - { - public: - virtual void importAccountError() = 0; - virtual void setupAccountError() = 0; - virtual void importAccountSuccess() = 0; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.cpp b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.cpp deleted file mode 100644 index 7ba2ed735..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "Item.h" - -using namespace Status::Modules::Startup::Onboarding; - -Item::Item(const QString& id, const QString& alias, const QString& identicon, const QString& address, - const QString& keyUid) - : m_id(id) - , m_alias(alias) - , m_identicon(identicon) - , m_address(address) - , m_keyUid(keyUid) -{ -} - -QString Item::getId() const -{ - return m_id; -} - -QString Item::getAlias() const -{ - return m_alias; -} - -QString Item::getIdenticon() const -{ - return m_identicon; -} - -QString Item::getAddress() const -{ - return m_address; -} - -QString Item::getKeyUid() const -{ - return m_keyUid; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.h b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.h deleted file mode 100644 index 75b8b42ea..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -namespace Status::Modules::Startup::Onboarding -{ - class Item - { - public: - Item(const QString& id, const QString& alias, const QString& identicon, const QString& address, - const QString& keyUid); - - QString getId() const; - QString getAlias() const; - QString getIdenticon() const; - QString getAddress() const; - QString getKeyUid() const; - - private: - QString m_id; - QString m_alias; - QString m_identicon; - QString m_address; - QString m_keyUid; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.cpp b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.cpp deleted file mode 100644 index 31a722df7..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "Model.h" - -using namespace Status::Modules::Startup::Onboarding; - -Model::Model(QObject* parent) - : QAbstractListModel(parent) -{ -} - -QHash Model::roleNames() const -{ - QHash roles; - roles[Id] = "accountId"; - roles[Alias] = "username"; - roles[Identicon] = "identicon"; - roles[Address] = "address"; - roles[KeyUid] = "keyUid"; - return roles; -} - -int Model::rowCount(const QModelIndex& parent) const -{ - Q_UNUSED(parent) - return m_items.size(); -} - -QVariant Model::data(const QModelIndex& index, int role) const -{ - if(!index.isValid()) - { - return QVariant(); - } - - if(index.row() < 0 || index.row() > m_items.size()) - { - return QVariant(); - } - - Item item = m_items[index.row()]; - - switch(role) - { - case Id: - return item.getId(); - case Alias: - return item.getAlias(); - case Identicon: - return item.getIdenticon(); - case Address: - return item.getAddress(); - case KeyUid: - return item.getKeyUid(); - } - - return QVariant(); -} - -void Model::setItems(QVector items) -{ - beginResetModel(); - m_items = std::move(items); - endResetModel(); -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.h b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.h deleted file mode 100644 index 3e8ee92cd..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "Item.h" - -namespace Status::Modules::Startup::Onboarding -{ - class Model : public QAbstractListModel - { - Q_OBJECT - - public: - enum ModelRole - { - Id = Qt::UserRole + 1, - Alias, - Identicon, - Address, - KeyUid - }; - - explicit Model(QObject* parent = nullptr); - QHash roleNames() const; - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; - virtual QVariant data(const QModelIndex& index, int role) const; - void setItems(QVector items); - - private: - QVector m_items; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.cpp b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.cpp deleted file mode 100644 index e932a1cc6..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "Module.h" - -#include "Controller.h" -#include "View.h" - -#include "../../../Core/Engine.h" - -using namespace Status::Modules::Startup::Onboarding; - -Module::Module(std::shared_ptr delegate, - std::shared_ptr controller, - std::shared_ptr view) - : m_delegate(std::move(delegate)) - , m_controller(std::move(controller)) - , m_view(std::move(view)) -{ -} - -void Module::load() -{ - Engine::instance()->rootContext()->setContextProperty("onboardingModule", m_view->getQObject()); - m_controller->init(); - m_view->load(); - - const QVector& gAcc = m_controller->getGeneratedAccounts(); - QVector accounts; - foreach(const Accounts::GeneratedAccountDto& acc, gAcc) - { - accounts << Item(acc.id, acc.alias, acc.identicon, acc.address, acc.keyUid); - } - - m_view->setAccountList(accounts); -} - -bool Module::isLoaded() -{ - return m_moduleLoaded; -} - -void Module::viewDidLoad() -{ - m_moduleLoaded = true; - m_delegate->onboardingDidLoad(); -} - -void Module::setSelectedAccountByIndex(const int index) -{ - m_controller->setSelectedAccountByIndex(index); -} - -void Module::storeSelectedAccountAndLogin(const QString& password) -{ - m_controller->storeSelectedAccountAndLogin(password); -} -void Module::setupAccountError() -{ - m_view->setupAccountError(); -} - -const Status::Accounts::GeneratedAccountDto& Module::getImportedAccount() const -{ - return m_controller->getImportedAccount(); -} - -QString Module::validateMnemonic(const QString& mnemonic) -{ - return m_controller->validateMnemonic(mnemonic); -} - -void Module::importMnemonic(const QString& mnemonic) -{ - m_controller->importMnemonic(mnemonic); -} - -void Module::importAccountError() -{ - m_view->importAccountError(); -} - -void Module::importAccountSuccess() -{ - m_view->importAccountSuccess(); -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.h b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.h deleted file mode 100644 index f00441be5..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "ModuleInterface.h" -#include "ControllerInterface.h" -#include "ViewInterface.h" - -namespace Status::Modules::Startup::Onboarding -{ - - class Module final : public ModuleAccessInterface - , public ControllerDelegateInterface - , public ViewDelegateInterface - , public std::enable_shared_from_this - { - public: - Module(std::shared_ptr delegate, - std::shared_ptr controller, - std::shared_ptr view); - - // Module Access - void load() override; - bool isLoaded() override; - - // Controller Delegate - void importAccountError() override; - void setupAccountError() override; - void importAccountSuccess() override; - - // View Delegate - void viewDidLoad() override; - void setSelectedAccountByIndex(const int index) override; - void storeSelectedAccountAndLogin(const QString& password) override; - const Accounts::GeneratedAccountDto& getImportedAccount() const override; - QString validateMnemonic(const QString& mnemonic) override; - void importMnemonic(const QString& mnemonic) override; - - private: - void checkIfModuleDidLoad(); - - private: - std::shared_ptr m_delegate; - std::shared_ptr m_controller; - std::shared_ptr m_view; - bool m_moduleLoaded {false}; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.cpp b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.cpp deleted file mode 100644 index a0dce8143..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "ModuleBuilder.h" - -#include "Module.h" -#include "Controller.h" -#include "View.h" - -using namespace Status::Modules::Startup::Onboarding; - -ModuleBuilder::ModuleBuilder(std::shared_ptr accountsService) - : m_accountsService(std::move(accountsService)) -{ -} - -std::shared_ptr ModuleBuilder::operator()(std::shared_ptr delegate) { - - auto controller = std::make_shared(m_accountsService); - auto view = std::make_shared(); - - auto module = std::make_shared(delegate, controller, view); - - controller->setDelegate(module); - view->setDelegate(module); - - return module; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.h b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.h deleted file mode 100644 index 920f6783c..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "ModuleInterface.h" - -#include - -#include - -namespace Status::Modules::Startup::Onboarding -{ - class ModuleBuilder final - { - public: - ModuleBuilder(std::shared_ptr accountsService); - - [[nodiscard]] std::shared_ptr operator()(std::shared_ptr delegate); - - private: - std::shared_ptr m_accountsService; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleInterface.h b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleInterface.h deleted file mode 100644 index 06194d369..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleInterface.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -namespace Status::Modules::Startup::Onboarding -{ - class ModuleAccessInterface - { - public: - virtual ~ModuleAccessInterface() = default; - - virtual void load() = 0; - virtual bool isLoaded() = 0; - }; - - class ModuleDelegateInterface - { - public: - virtual void onboardingDidLoad() = 0; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.cpp b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.cpp deleted file mode 100644 index 66fe51b37..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "View.h" - -#include "../../../Core/Engine.h" - -using namespace Status::Modules::Startup::Onboarding; - -View::View() : QObject(nullptr) - , m_model(new Model(this)) -{ -} - -void View::setDelegate(std::shared_ptr delegate) -{ - m_delegate = std::move(delegate); -} - -QObject* View::getQObject() -{ - Engine::instance()->setObjectOwnership(this, QQmlEngine::CppOwnership); - return this; -} - -void View::load() -{ - m_delegate->viewDidLoad(); -} - -Model* View::getModel() -{ - Engine::instance()->setObjectOwnership(m_model, QQmlEngine::CppOwnership); - return m_model; -} - -void View::setAccountList(QVector accounts) -{ - m_model->setItems(std::move(accounts)); - emit modelChanged(); -} - -QString View::getImportedAccountIdenticon() const -{ - return m_delegate->getImportedAccount().identicon; -} - -QString View::getImportedAccountAlias() const -{ - return m_delegate->getImportedAccount().alias; -} - -QString View::getImportedAccountAddress() const -{ - return m_delegate->getImportedAccount().address; -} - -void View::setSelectedAccountByIndex(const int index) -{ - m_delegate->setSelectedAccountByIndex(index); -} - -void View::storeSelectedAccountAndLogin(const QString& password) -{ - m_delegate->storeSelectedAccountAndLogin(password); -} - -void View::setupAccountError() -{ - emit accountSetupError(); -} - -QString View::validateMnemonic(const QString& mnemonic) -{ - return m_delegate->validateMnemonic(mnemonic); -} - -void View::importMnemonic(const QString& mnemonic) -{ - m_delegate->importMnemonic(mnemonic); -} - -void View::importAccountError() -{ - // In QML we can connect to this signal and notify a user - // before refactoring we didn't have this signal - emit accountImportError(); -} - -void View::importAccountSuccess() -{ - emit importedAccountChanged(); -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.h b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.h deleted file mode 100644 index 07319aae1..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "ViewInterface.h" - -namespace Status::Modules::Startup::Onboarding -{ - class View final : public QObject - , public ViewInterface - { - Q_OBJECT - - Q_PROPERTY(Model* accountsModel READ getModel NOTIFY modelChanged) - Q_PROPERTY(QString importedAccountIdenticon READ getImportedAccountIdenticon NOTIFY importedAccountChanged) - Q_PROPERTY(QString importedAccountAlias READ getImportedAccountAlias NOTIFY importedAccountChanged) - Q_PROPERTY(QString importedAccountAddress READ getImportedAccountAddress NOTIFY importedAccountChanged) - - public: - explicit View(); - void setDelegate(std::shared_ptr delegate); - - // View Interface - QObject* getQObject() override; - void load() override; - Model* getModel() override; - void setAccountList(QVector accounts) override; - void importAccountError() override; - void setupAccountError() override; - void importAccountSuccess() override; - - QString getImportedAccountIdenticon() const; - QString getImportedAccountAlias() const; - QString getImportedAccountAddress() const; - - Q_INVOKABLE void setSelectedAccountByIndex(const int index); - Q_INVOKABLE void storeSelectedAccountAndLogin(const QString& password); - Q_INVOKABLE QString validateMnemonic(const QString& mnemonic); - Q_INVOKABLE void importMnemonic(const QString& mnemonic); - - signals: - void modelChanged(); - void importedAccountChanged(); - void accountSetupError(); - void accountImportError(); - - private: - std::shared_ptr m_delegate; - Model* m_model {nullptr}; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ViewInterface.h b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ViewInterface.h deleted file mode 100644 index 7e7b4d6e4..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ViewInterface.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "Model.h" - -#include - -#include - -namespace Status::Modules::Startup::Onboarding -{ - class ViewInterface - { - public: - virtual ~ViewInterface() = default; - - virtual QObject* getQObject() = 0; - virtual void load() = 0; - virtual Model* getModel() = 0; - virtual void setAccountList(QVector accounts) = 0; - virtual void importAccountError() = 0; - virtual void setupAccountError() = 0; - virtual void importAccountSuccess() = 0; - }; - - class ViewDelegateInterface - { - public: - virtual void viewDidLoad() = 0; - virtual void setSelectedAccountByIndex(const int index) = 0; - virtual void storeSelectedAccountAndLogin(const QString& password) = 0; - virtual const Accounts::GeneratedAccountDto& getImportedAccount() const = 0; - virtual QString validateMnemonic(const QString& mnemonic) = 0; - virtual void importMnemonic(const QString& mnemonic) = 0; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/View.cpp b/src-cpp-structure/projects/App/Modules/Startup/View.cpp deleted file mode 100644 index cca04b4b5..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/View.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "View.h" - -#include "../../Core/Engine.h" - -using namespace Status::Modules::Startup; - -View::View() : QObject(nullptr) - , m_appState(AppState::OnboardingState) -{ -} - -void View::setDelegate(std::shared_ptr delegate) -{ - m_delegate = std::move(delegate); -} - -QObject* View::getQObject() -{ - Engine::instance()->setObjectOwnership(this, QQmlEngine::CppOwnership); - return this; -} - -void View::load() -{ - m_delegate->viewDidLoad(); -} - -int View::getAppState() -{ - return static_cast(m_appState); -} - -void View::setAppState(AppState state) -{ - if(m_appState == state) - { - return; - } - - m_appState = state; - emit appStateChanged(m_appState); -} - -void View::emitLogOut() -{ - emit logOut(); -} - -void View::emitStartUpUIRaised() -{ - emit startUpUIRaised(); -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/View.h b/src-cpp-structure/projects/App/Modules/Startup/View.h deleted file mode 100644 index 088e0126a..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/View.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "ViewInterface.h" - -namespace Status::Modules::Startup -{ - class View final : public QObject - , public ViewInterface - { - Q_OBJECT - - Q_PROPERTY(int appState READ getAppState NOTIFY appStateChanged) - - public: - explicit View(); - void setDelegate(std::shared_ptr delegate); - - // View Interface - QObject* getQObject() override; - void emitLogOut() override; - void emitStartUpUIRaised() override; - void setAppState(AppState state) override; - void load() override; - - public slots: - int getAppState(); - - signals: - void appStateChanged(int state); - void logOut(); - void startUpUIRaised(); - - private: - std::shared_ptr m_delegate; - AppState m_appState; - }; -} diff --git a/src-cpp-structure/projects/App/Modules/Startup/ViewInterface.h b/src-cpp-structure/projects/App/Modules/Startup/ViewInterface.h deleted file mode 100644 index ec1d89896..000000000 --- a/src-cpp-structure/projects/App/Modules/Startup/ViewInterface.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -namespace Status::Modules::Startup -{ - enum AppState - { - OnboardingState = 0, - LoginState = 1, - MainAppState = 2 - // TODO: is Pending - }; - - class ViewInterface - { - public: - virtual ~ViewInterface() = default; - - virtual QObject* getQObject() = 0; - virtual void emitLogOut() = 0; - virtual void emitStartUpUIRaised() = 0; - virtual void setAppState(AppState state) = 0; - virtual void load() = 0; - }; - - class ViewDelegateInterface - { - public: - virtual void viewDidLoad() = 0; - }; -} diff --git a/src-cpp-structure/projects/Backend/CMakeLists.txt b/src-cpp-structure/projects/Backend/CMakeLists.txt deleted file mode 100644 index 54fc92deb..000000000 --- a/src-cpp-structure/projects/Backend/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -cmake_minimum_required(VERSION 3.18 FATAL_ERROR) - -project(Status.Backend DESCRIPTION "Status project used to integrate with status-go") - -set(CMAKE_DEBUG_POSTFIX d) -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -add_library(${PROJECT_NAME} SHARED) - -add_dependencies(${PROJECT_NAME} ${STATUS_GO_LIB}) - -file( - GLOB_RECURSE SOURCES - "*.h" - "*.cpp" -) - -target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) - -target_include_directories(${PROJECT_NAME} PUBLIC include) - -target_link_libraries( - ${PROJECT_NAME} - Qt5::Core - ${STATUS_GO_LIB} -) diff --git a/src-cpp-structure/projects/Backend/include/StatusBackend/Types.h b/src-cpp-structure/projects/Backend/include/StatusBackend/Types.h deleted file mode 100644 index a4ef6f8b9..000000000 --- a/src-cpp-structure/projects/Backend/include/StatusBackend/Types.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -namespace Backend -{ - // Used in calls where we don't have version and id returned from `status-go` - static QString DefaultVersion = "2.0"; - static constexpr int DefaultId = 0; - - struct RpcError - { - int code; - QString message; - }; - - template - struct RpcResponse - { - T result; - QString jsonRpcVersion; - int id; - RpcError error; - - RpcResponse(T result, QString version = DefaultVersion, int id = DefaultId, - RpcError error = RpcError{-1, QString()}) - : result(result) - , jsonRpcVersion(version) - , id(id) - , error(error) - { } - - bool containsError() const { - return !error.message.isEmpty(); - } - }; -} diff --git a/src-cpp-structure/projects/Backend/include/StatusBackend/Utils.h b/src-cpp-structure/projects/Backend/include/StatusBackend/Utils.h deleted file mode 100644 index 7ce2bb718..000000000 --- a/src-cpp-structure/projects/Backend/include/StatusBackend/Utils.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include "Types.h" -#include "libstatus.h" - -#include - -namespace Backend -{ - namespace Param { - const QString Id = "id"; - const QString JsonRpc = "jsonrpc"; - const QString Result = "result"; - const QString Error = "error"; - const QString ErrorMessage = "message"; - const QString ErrorCode = "code"; - } - - class Utils - { - public: - template static QByteArray jsonToByteArray(const T& json){ - if constexpr (std::is_same_v || - std::is_same_v) - { - return QJsonDocument(json).toJson(QJsonDocument::Compact); - } - - return QByteArray(); - } - - static QJsonArray toJsonArray(const QVector& value){ - QJsonArray array; - for(auto& v : value) - array << v; - return array; - } - - template static bool checkReceivedResponse(const QString& response, T& json) - { - QJsonParseError error; - auto jsonDocument = QJsonDocument::fromJson(response.toUtf8(), &error); - - if (error.error != QJsonParseError::NoError) - return false; - - if constexpr (std::is_same_v) - { - json = jsonDocument.object(); - return true; - } - else if constexpr (std::is_same_v) - { - json = jsonDocument.array(); - return true; - } - - return false; - } - - template static RpcResponse buildJsonRpcResponse(const T& json) { - auto response = RpcResponse(T()); - - if constexpr (std::is_same_v) - { - if (!json[Param::Id].isNull() && !json[Param::Id].isUndefined()) - response.id = json[Param::Id].toInt(); - - if (!json[Param::JsonRpc].isNull() && !json[Param::JsonRpc].isUndefined()) - response.jsonRpcVersion = json[Param::JsonRpc].toString(); - - if (!json[Param::Error].isNull() && !json[Param::Error].isUndefined()) - { - auto errObj = json[Param::Id].toObject(); - if (!errObj[Param::ErrorCode].isNull() && !errObj[Param::ErrorCode].isUndefined()) - response.error.code = errObj[Param::ErrorCode].toInt(); - if (!errObj[Param::ErrorMessage].isNull() && !errObj[Param::ErrorMessage].isUndefined()) - response.error.message = errObj[Param::ErrorMessage].toString(); - } - - if (!json[Param::Result].isNull() && !json[Param::Result].isUndefined()) - response.result = json[Param::Result].toObject(); - } - else if constexpr (std::is_same_v) - { - response.result = json; - } - - return response; - } - - template static RpcResponse callPrivateRpc(const QByteArray& payload) - { - try - { - auto result = CallPrivateRPC(const_cast(payload).data()); - T jsonResult; - if(!Utils::checkReceivedResponse(result, jsonResult)) - { - auto msg = QObject::tr("parsing response failed"); - throw std::domain_error(msg.toStdString()); - } - - return Utils::buildJsonRpcResponse(jsonResult); - } - catch (std::exception& e) - { - auto response = RpcResponse(T()); - response.error.message = QObject::tr("an error executing rpc call occurred, msg: %1").arg(e.what()); - return response; - } - catch (...) - { - auto response = RpcResponse(T()); - response.error.message = QObject::tr("an error executing rpc call"); - return response; - } - } - - static QString hashString(QString str) { - return "0x" + QString::fromUtf8(QCryptographicHash::hash(str.toUtf8(), - QCryptographicHash::Keccak_256).toHex()); - } - }; -} diff --git a/src-cpp-structure/projects/Backend/include/StatusBackend/WalletAccounts.h b/src-cpp-structure/projects/Backend/include/StatusBackend/WalletAccounts.h deleted file mode 100644 index 28b7d81ef..000000000 --- a/src-cpp-structure/projects/Backend/include/StatusBackend/WalletAccounts.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "Types.h" - -#include - -namespace Backend::Wallet::Accounts -{ - RpcResponse getAccounts(); - - RpcResponse generateNewAccount(const QString& password, const QString& accountName, - const QString& color); - - RpcResponse addAccountsFromPrivateKey(const QString& privateKey, const QString& password, - const QString& accountName, const QString& color); - - RpcResponse addAccountsFromSeed(const QString& seedPhrase, const QString& password, - const QString& accountName, const QString& color); - - RpcResponse addWatchOnlyAccount(const QString& address, const QString& accountName, - const QString& color); - - RpcResponse deleteAccount(const QString& address); -} diff --git a/src-cpp-structure/projects/Backend/src/WalletAccounts.cpp b/src-cpp-structure/projects/Backend/src/WalletAccounts.cpp deleted file mode 100644 index 5e52849e2..000000000 --- a/src-cpp-structure/projects/Backend/src/WalletAccounts.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "StatusBackend/WalletAccounts.h" - -#include "StatusBackend/Utils.h" - -using namespace Backend; - -RpcResponse Wallet::Accounts::getAccounts() -{ - QJsonObject payload{ - {"jsonrpc", "2.0"}, - {"method", "accounts_getAccounts"}, - {"params", QJsonValue()} - }; - - return Utils::callPrivateRpc(Utils::jsonToByteArray(std::move(payload))); -} - -RpcResponse Wallet::Accounts::generateNewAccount(const QString& password, const QString& accountName, const QString& color) -{ - QString hashedPassword(Utils::hashString(password)); - QJsonArray params = { - hashedPassword, - accountName, - color - }; - - QJsonObject payload{ - {"jsonrpc", "2.0"}, - {"method", "accounts_generateAccount"}, - {"params", params} - }; - - return Utils::callPrivateRpc(Utils::jsonToByteArray(std::move(payload))); -} - -RpcResponse Wallet::Accounts::addAccountsFromPrivateKey(const QString& privateKey, const QString& password, - const QString& accountName, const QString& color) -{ - QString hashedPassword(Utils::hashString(password)); - QJsonArray params = { - privateKey, - hashedPassword, - accountName, - color - }; - - QJsonObject payload{ - {"jsonrpc", "2.0"}, - {"method", "accounts_addAccountWithMnemonic"}, - {"params", params} - }; - - return Utils::callPrivateRpc(Utils::jsonToByteArray(std::move(payload))); -} - -RpcResponse Wallet::Accounts::addAccountsFromSeed(const QString& seedPhrase, const QString& password, const QString& accountName, const QString& color) -{ - QString hashedPassword(Utils::hashString(password)); - - QJsonArray params = { - seedPhrase, - hashedPassword, - accountName, - color - }; - - QJsonObject payload { - {"jsonrpc", "2.0"}, - {"method", "accounts_addAccountWithPrivateKey"}, - {"params", params} - }; - - return Utils::callPrivateRpc(Utils::jsonToByteArray(std::move(payload))); -} - -RpcResponse Wallet::Accounts::addWatchOnlyAccount(const QString& address, const QString& accountName , const QString& color) -{ - QJsonArray params = { - address, - accountName, - color - }; - - QJsonObject payload { - {"jsonrpc", "2.0"}, - {"method", "accounts_addAccountWatch"}, - {"params", params} - }; - - return Utils::callPrivateRpc(Utils::jsonToByteArray(std::move(payload))); -} - -RpcResponse Wallet::Accounts::deleteAccount(const QString& address) -{ - QJsonArray params = { - address - }; - - QJsonObject payload { - {"jsonrpc", "2.0"}, - {"method", "accounts_deleteAccount"}, - {"params", params} - }; - - return Utils::callPrivateRpc(Utils::jsonToByteArray(std::move(payload))); -} diff --git a/src-cpp-structure/projects/CMakeLists.txt b/src-cpp-structure/projects/CMakeLists.txt deleted file mode 100644 index 70300dce9..000000000 --- a/src-cpp-structure/projects/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -cmake_minimum_required(VERSION 3.18 FATAL_ERROR) - -add_subdirectory(App) -add_subdirectory(Services) -add_subdirectory(Backend) -add_subdirectory(ServicesTest) diff --git a/src-cpp-structure/projects/Services/CMakeLists.txt b/src-cpp-structure/projects/Services/CMakeLists.txt deleted file mode 100644 index e4df2973a..000000000 --- a/src-cpp-structure/projects/Services/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -cmake_minimum_required(VERSION 3.18 FATAL_ERROR) - -project(Status.Services DESCRIPTION "Status services") - -set(CMAKE_DEBUG_POSTFIX d) -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -add_library(${PROJECT_NAME} SHARED) - -# Platform specific stuff are place in the corresponding .cmake file. -if (WIN32) - include("${CMAKE_SOURCE_DIR}/cmake/services-win.cmake") -elseif (APPLE) - include("${CMAKE_SOURCE_DIR}/cmake/services-mac.cmake") -elseif(UNIX) - include("${CMAKE_SOURCE_DIR}/cmake/services-linux.cmake") -endif () - -add_dependencies(${PROJECT_NAME} Status.Backend) - -target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) - -target_include_directories(${PROJECT_NAME} PUBLIC include) - -# default token is a free-tier token with limited capabilities and usage -# limits; setup your own infura key with -# cmake -DINFURA_KEY=infura_key_goes_here .. -if( "${INFURA_KEY}" STREQUAL "") - message("-- Using default Infura key") - file (STRINGS ${CMAKE_SOURCE_DIR}/../resources/infura_key INFURA_KEY) -else() - message("-- Using custom Infura key") -endif() - -# Build constants -target_compile_definitions(${PROJECT_NAME} PRIVATE INFURA_KEY="${INFURA_KEY}") diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/AccountDto.h b/src-cpp-structure/projects/Services/include/StatusServices/Accounts/AccountDto.h deleted file mode 100644 index f599af0d5..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/AccountDto.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include - -#include - -namespace Status::Accounts -{ - struct Image - { - QString keyUid; - QString imgType; - QString uri; - int width; - int height; - int fileSize; - int resizeTarget; - - static Image toImage(const QJsonObject& jsonObj) - { - auto result = Image(); - - try - { - result.keyUid = Json::getProp(jsonObj, "keyUid")->toString(); - result.imgType = Json::getProp(jsonObj, "type")->toString(); - result.uri = Json::getProp(jsonObj, "uri")->toString(); - result.width = Json::getProp(jsonObj, "width")->toInt(); - result.height = Json::getProp(jsonObj, "height")->toInt(); - result.fileSize = Json::getProp(jsonObj, "fileSize")->toInt(); - result.resizeTarget = Json::getProp(jsonObj, "resizeTarget")->toInt(); - } - catch (std::exception e) - { - qWarning() << QObject::tr("Mapping Image failed: %1").arg(e.what()); - } - - return result; - } - }; - - struct AccountDto - { - QString name; - long timestamp; - QString identicon; - QString keycardPairing; - QString keyUid; - QVector images; - - bool isValid() const - { - return !(name.isEmpty() || keyUid.isEmpty()); - } - - static AccountDto toAccountDto(const QJsonObject& jsonObj) - { - auto result = AccountDto(); - - try - { - result.name = Json::getMandatoryProp(jsonObj, "name")->toString(); - bool ok; - result.timestamp = Json::getMandatoryProp(jsonObj, "timestamp")->toString().toLong(&ok); - result.identicon = Json::getMandatoryProp(jsonObj, "identicon")->toString(); - result.keycardPairing = Json::getMandatoryProp(jsonObj, "keycard-pairing")->toString(); - result.keyUid = Json::getMandatoryProp(jsonObj, "key-uid")->toString(); - - foreach(const auto& value, jsonObj["images"].toArray()) - { - result.images << Image::toImage(value.toObject()); - } - } - catch (std::exception e) - { - qWarning() << QObject::tr("Mapping AccountDto failed: %1").arg(e.what()); - } - - return result; - } - }; - - - - -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/GeneratedAccountDto.h b/src-cpp-structure/projects/Services/include/StatusServices/Accounts/GeneratedAccountDto.h deleted file mode 100644 index 756fb372b..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/GeneratedAccountDto.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include - -#include - -namespace Status::Accounts -{ - struct DerivedAccountDetails - { - QString publicKey; - QString address; - QString derivationPath; - - static DerivedAccountDetails toDerivedAccountDetails(const QJsonObject& jsonObj, const QString& derivationPath) - { - // Mapping this DTO is not strightforward since only keys are used for id. We - // handle it a bit different. - auto result = DerivedAccountDetails(); - - try - { - result.derivationPath = derivationPath; - result.publicKey = Json::getMandatoryProp(jsonObj, "publicKey")->toString(); - result.address = Json::getMandatoryProp(jsonObj, "address")->toString(); - } - catch (std::exception e) - { - qWarning() << QObject::tr("Mapping DerivedAccountDetails failed: %1").arg(e.what()); - } - - return result; - } - }; - - struct DerivedAccounts - { - DerivedAccountDetails whisper; - DerivedAccountDetails walletRoot; - DerivedAccountDetails defaultWallet; - DerivedAccountDetails eip1581; - - static DerivedAccounts toDerivedAccounts(const QJsonObject& jsonObj) - { - auto result = DerivedAccounts(); - - foreach(const auto& derivationPath, jsonObj.keys()) - { - auto derivedObj = jsonObj.value(derivationPath).toObject(); - if(derivationPath == Constants::General::PathWhisper) - { - result.whisper = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); - } - else if(derivationPath == Constants::General::PathWalletRoot) - { - result.walletRoot = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); - } - else if(derivationPath == Constants::General::PathDefaultWallet) - { - result.defaultWallet = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); - } - else if(derivationPath == Constants::General::PathEIP1581) - { - result.eip1581 = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); - } - } - - return result; - } - }; - - struct GeneratedAccountDto - { - QString id; - QString publicKey; - QString address; - QString keyUid; - QString mnemonic; - DerivedAccounts derivedAccounts; - - // The following two are set additionally. - QString alias; - QString identicon; - - bool isValid() const - { - return !(id.isEmpty() || publicKey.isEmpty() || address.isEmpty() || keyUid.isEmpty()); - } - - static GeneratedAccountDto toGeneratedAccountDto(const QJsonObject& jsonObj) - { - auto result = GeneratedAccountDto(); - - try - { - result.id = Json::getMandatoryProp(jsonObj, "id")->toString(); - result.address = Json::getMandatoryProp(jsonObj, "address")->toString(); - result.keyUid = Json::getMandatoryProp(jsonObj, "keyUid")->toString(); - result.mnemonic = Json::getMandatoryProp(jsonObj, "mnemonic")->toString(); - result.publicKey = Json::getMandatoryProp(jsonObj, "publicKey")->toString(); - - auto derivedObj = Json::getProp(jsonObj, "derived")->toObject(); - if(!derivedObj.isEmpty()) - { - result.derivedAccounts = DerivedAccounts::toDerivedAccounts(derivedObj); - } - } - catch (std::exception e) - { - qWarning() << QObject::tr("Mapping GeneratedAccountDto failed: %1").arg(e.what()); - } - - return result; - } - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/Service.h b/src-cpp-structure/projects/Services/include/StatusServices/Accounts/Service.h deleted file mode 100644 index b3a44fb58..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/Service.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include "ServiceInterface.h" - -namespace Status::Accounts -{ - - class Service : public ServiceInterface - { - public: - Service(); - - void init(const QString& statusgoDataDir) override; - - [[nodiscard]] QVector openedAccounts() override; - - [[nodiscard]] const QVector& generatedAccounts() const override; - - bool setupAccount(const QString& accountId, const QString& password) override; - - [[nodiscard]] const AccountDto& getLoggedInAccount() const override; - - [[nodiscard]] const GeneratedAccountDto& getImportedAccount() const override; - - [[nodiscard]] bool isFirstTimeAccountLogin() const override; - - QString validateMnemonic(const QString& mnemonic) override; - - bool importMnemonic(const QString& mnemonic) override; - - QString login(AccountDto account, const QString& password) override; - - void clear() override; - - QString generateAlias(const QString& publicKey) override; - - QString generateIdenticon(const QString& publicKey) override; - - bool verifyAccountPassword(const QString& account, const QString& password) override; - - private: - QJsonObject prepareAccountJsonObject(const GeneratedAccountDto& account) const; - - DerivedAccounts storeDerivedAccounts(const QString& accountId, const QString& hashedPassword, - const QVector& paths); - - AccountDto saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, - const QJsonArray& subaccounts, const QJsonObject& settings, - const QJsonObject& config); - - QJsonObject getAccountDataForAccountId(const QString& accountId) const; - - QJsonArray prepareSubaccountJsonObject(const GeneratedAccountDto& account) const; - - QJsonArray getSubaccountDataForAccountId(const QString& accountId) const; - - QString generateSigningPhrase(const int count) const; - - QJsonObject prepareAccountSettingsJsonObject(const GeneratedAccountDto& account, - const QString& installationId) const; - - QJsonObject getAccountSettings(const QString& accountId, const QString& installationId) const; - - QJsonObject getDefaultNodeConfig(const QString& installationId) const; - - private: - QVector m_generatedAccounts; - - QString m_statusgoDataDir; - bool m_isFirstTimeAccountLogin; - AccountDto m_loggedInAccount; - GeneratedAccountDto m_importedAccount; - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/ServiceInterface.h b/src-cpp-structure/projects/Services/include/StatusServices/Accounts/ServiceInterface.h deleted file mode 100644 index 019a40c5b..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/ServiceInterface.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "AccountDto.h" -#include "GeneratedAccountDto.h" - -namespace Status::Accounts -{ - class ServiceInterface - { - public: - - virtual ~ServiceInterface() = default; - - virtual void init(const QString& statusgoDataDir) = 0; - - [[nodiscard]] virtual QVector openedAccounts() = 0; - - [[nodiscard]] virtual const QVector& generatedAccounts() const = 0; - - virtual bool setupAccount(const QString& accountId, const QString& password) = 0; - - [[nodiscard]] virtual const AccountDto& getLoggedInAccount() const = 0; - - [[nodiscard]] virtual const GeneratedAccountDto& getImportedAccount() const = 0; - - [[nodiscard]] virtual bool isFirstTimeAccountLogin() const = 0; - - virtual QString validateMnemonic(const QString& mnemonic) = 0; - - virtual bool importMnemonic(const QString& mnemonic) = 0; - - virtual QString login(AccountDto account, const QString& password) = 0; - - virtual void clear() = 0; - - virtual QString generateAlias(const QString& publicKey) = 0; - - virtual QString generateIdenticon(const QString& publicKey) = 0; - - virtual bool verifyAccountPassword(const QString& account, const QString& password) = 0; - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/ServiceMock.h b/src-cpp-structure/projects/Services/include/StatusServices/Accounts/ServiceMock.h deleted file mode 100644 index 83eee124e..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Accounts/ServiceMock.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "ServiceInterface.h" - -#include - -namespace Status::Test -{ - class AccountsServiceMock final : public Accounts::ServiceInterface - { - public: - virtual ~AccountsServiceMock() override {}; - - MOCK_METHOD(void, init, (const QString&), (override)); - MOCK_METHOD(QVector, openedAccounts, (), (override)); - MOCK_METHOD(const QVector&, generatedAccounts, (), (const, override)); - MOCK_METHOD(bool, setupAccount, (const QString&, const QString&), (override)); - MOCK_METHOD(const Accounts::AccountDto&, getLoggedInAccount, (), (const, override)); - MOCK_METHOD(const Accounts::GeneratedAccountDto&, getImportedAccount, (), (const, override)); - MOCK_METHOD(bool, isFirstTimeAccountLogin, (), (const, override)); - MOCK_METHOD(QString, validateMnemonic, (const QString&), (override)); - MOCK_METHOD(bool, importMnemonic, (const QString&), (override)); - MOCK_METHOD(QString, login, (Accounts::AccountDto, const QString&), (override)); - MOCK_METHOD(void, clear, (), (override)); - MOCK_METHOD(QString, generateAlias, (const QString&), (override)); - MOCK_METHOD(QString, generateIdenticon, (const QString&), (override)); - MOCK_METHOD(bool, verifyAccountPassword, (const QString&, const QString&), (override)); - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/AccountsService b/src-cpp-structure/projects/Services/include/StatusServices/AccountsService deleted file mode 100644 index b10bbb741..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/AccountsService +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "Accounts/AccountDto.h" -#include "Accounts/GeneratedAccountDto.h" -#include "Accounts/ServiceInterface.h" \ No newline at end of file diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Common/Constants.h b/src-cpp-structure/projects/Services/include/StatusServices/Common/Constants.h deleted file mode 100644 index 55f209af4..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Common/Constants.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -namespace Status::Constants -{ - namespace Fleet - { - const QString Prod = "eth.prod"; - const QString Staging = "eth.staging"; - const QString Test = "eth.test"; - const QString WakuV2Prod = "wakuv2.prod"; - const QString WakuV2Test = "wakuv2.test"; - const QString GoWakuTest = "go-waku.test"; - } - - namespace FleetNodes - { - const QString Bootnodes = "boot"; - const QString Mailservers = "mail"; - const QString Rendezvous = "rendezvous"; - const QString Whisper = "whisper"; - const QString Waku = "waku"; - const QString LibP2P = "libp2p"; - const QString Websocket = "websocket"; - } - - namespace General - { - const QString DefaultNetworkName = "mainnet_rpc"; - //const DEFAULT_NETWORKS_IDS* = @["mainnet_rpc", "testnet_rpc", "rinkeby_rpc", "goerli_rpc", "xdai_rpc", "poa_rpc" ] - - const QString ZeroAddress = "0x0000000000000000000000000000000000000000"; - - const QString PathWalletRoot = "m/44'/60'/0'/0"; - // EIP1581 Root Key, the extended key from which any whisper key/encryption key can be derived - const QString PathEIP1581 = "m/43'/60'/1581'"; - // BIP44-0 Wallet key, the default wallet key - const QString PathDefaultWallet = PathWalletRoot + "/0"; - // EIP1581 Chat Key 0, the default whisper key - const QString PathWhisper = PathEIP1581 + "/0'/0"; - - const QVector AccountDefaultPaths {PathWalletRoot, PathEIP1581, PathWhisper, PathDefaultWallet}; - } -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Common/Json.h b/src-cpp-structure/projects/Services/include/StatusServices/Common/Json.h deleted file mode 100644 index 8cca78ea2..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Common/Json.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -namespace Status -{ - - class Json - { - public: - static QJsonObject::const_iterator getProp(const QJsonObject& object, const QString& field) - { - const auto it = object.constFind(field); - return it; - } - - static QJsonObject::const_iterator getMandatoryProp(const QJsonObject& object, const QString& field) - { - const auto it = getProp(object, field); - - if (it == object.constEnd()) - { - throw std::logic_error(QString("No field `%1`").arg(field).toStdString()); - } - - return it; - } - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Common/Logger.h b/src-cpp-structure/projects/Services/include/StatusServices/Common/Logger.h deleted file mode 100644 index e9f972b96..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Common/Logger.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -namespace Status -{ - // This logger class should be done far better, but this is like a first aid. :) - // We should improve this class using `qInstallMessageHandler` and `fmt` library for example - // and make custom logging in the form and place/file we want. - - class Logger final { - public: - - static void init() - { - qInstallMessageHandler(messageHandler); - } - - static void messageHandler(const QtMsgType type, const QMessageLogContext &context, const QString &msg) - { - const char* time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz").toLocal8Bit().constData(); - const char* localMsg = msg.toLocal8Bit().constData(); - const char* file = context.file? context.file : ""; - const char* function = context.function? context.function : ""; - - switch (type) { - case QtDebugMsg: - fprintf(stderr, "Status-DEB [%s] (%s:%u, %s)\nMSG: %s\n", time, file, context.line, function, localMsg); - break; - case QtInfoMsg: - fprintf(stderr, "Status-INF [%s] (%s:%u, %s)\nMSG: %s\n", time, file, context.line, function, localMsg); - break; - case QtWarningMsg: - fprintf(stderr, "Status-WRN [%s] (%s:%u, %s)\nMSG: %s\n", time, file, context.line, function, localMsg); - break; - case QtCriticalMsg: - fprintf(stderr, "Status-CRT [%s] (%s:%u, %s)\nMSG: %s\n", time, file, context.line, function, localMsg); - break; - case QtFatalMsg: - fprintf(stderr, "Status-FAT [%s] (%s:%u, %s)\nMSG: %s\n", time, file, context.line, function, localMsg); - break; - } - } - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Common/SigningPhrases.h b/src-cpp-structure/projects/Services/include/StatusServices/Common/SigningPhrases.h deleted file mode 100644 index 23f95dafe..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Common/SigningPhrases.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include - -namespace Status::Constants -{ - const std::array SigningPhrases { - "area", "army", "atom", "aunt", "babe", "baby", "back", "bail", "bait", "bake", "ball", "band", "bank", "barn", - "base", "bass", "bath", "bead", "beak", "beam", "bean", "bear", "beat", "beef", "beer", "beet", "bell", "belt", - "bend", "bike", "bill", "bird", "bite", "blow", "blue", "boar", "boat", "body", "bolt", "bomb", "bone", "book", - "boot", "bore", "boss", "bowl", "brow", "bulb", "bull", "burn", "bush", "bust", "cafe", "cake", "calf", "call", - "calm", "camp", "cane", "cape", "card", "care", "carp", "cart", "case", "cash", "cast", "cave", "cell", "cent", - "chap", "chef", "chin", "chip", "chop", "chub", "chug", "city", "clam", "clef", "clip", "club", "clue", "coal", - "coat", "code", "coil", "coin", "coke", "cold", "colt", "comb", "cone", "cook", "cope", "copy", "cord", "cork", - "corn", "cost", "crab", "craw", "crew", "crib", "crop", "crow", "curl", "cyst", "dame", "dare", "dark", "dart", - "dash", "data", "date", "dead", "deal", "dear", "debt", "deck", "deep", "deer", "desk", "dhow", "diet", "dill", - "dime", "dirt", "dish", "disk", "dock", "doll", "door", "dory", "drag", "draw", "drop", "drug", "drum", "duck", - "dump", "dust", "duty", "ease", "east", "eave", "eddy", "edge", "envy", "epee", "exam", "exit", "face", "fact", - "fail", "fall", "fame", "fang", "farm", "fawn", "fear", "feed", "feel", "feet", "file", "fill", "film", "find", - "fine", "fire", "fish", "flag", "flat", "flax", "flow", "foam", "fold", "font", "food", "foot", "fork", "form", - "fort", "fowl", "frog", "fuel", "full", "gain", "gale", "galn", "game", "garb", "gate", "gear", "gene", "gift", - "girl", "give", "glad", "glen", "glue", "glut", "goal", "goat", "gold", "golf", "gong", "good", "gown", "grab", - "gram", "gray", "grey", "grip", "grit", "gyro", "hail", "hair", "half", "hall", "hand", "hang", "harm", "harp", - "hate", "hawk", "head", "heat", "heel", "hell", "helo", "help", "hemp", "herb", "hide", "high", "hill", "hire", - "hive", "hold", "hole", "home", "hood", "hoof", "hook", "hope", "hops", "horn", "hose", "host", "hour", "hunt", - "hurt", "icon", "idea", "inch", "iris", "iron", "item", "jail", "jeep", "jeff", "joey", "join", "joke", "judo", - "jump", "junk", "jury", "jute", "kale", "keep", "kick", "kill", "kilt", "kind", "king", "kiss", "kite", "knee", - "knot", "lace", "lack", "lady", "lake", "lamb", "lamp", "land", "lark", "lava", "lawn", "lead", "leaf", "leek", - "lier", "life", "lift", "lily", "limo", "line", "link", "lion", "lisa", "list", "load", "loaf", "loan", "lock", - "loft", "long", "look", "loss", "lout", "love", "luck", "lung", "lute", "lynx", "lyre", "maid", "mail", "main", - "make", "male", "mall", "manx", "many", "mare", "mark", "mask", "mass", "mate", "math", "meal", "meat", "meet", - "menu", "mess", "mice", "midi", "mile", "milk", "mime", "mind", "mine", "mini", "mint", "miss", "mist", "moat", - "mode", "mole", "mood", "moon", "most", "moth", "move", "mule", "mutt", "nail", "name", "neat", "neck", "need", - "neon", "nest", "news", "node", "nose", "note", "oboe", "okra", "open", "oval", "oven", "oxen", "pace", "pack", - "page", "pail", "pain", "pair", "palm", "pard", "park", "part", "pass", "past", "path", "peak", "pear", "peen", - "peer", "pelt", "perp", "pest", "pick", "pier", "pike", "pile", "pimp", "pine", "ping", "pink", "pint", "pipe", - "piss", "pith", "plan", "play", "plot", "plow", "poem", "poet", "pole", "polo", "pond", "pony", "poof", "pool", - "port", "post", "prow", "pull", "puma", "pump", "pupa", "push", "quit", "race", "rack", "raft", "rage", "rail", - "rain", "rake", "rank", "rate", "read", "rear", "reef", "rent", "rest", "rice", "rich", "ride", "ring", "rise", - "risk", "road", "robe", "rock", "role", "roll", "roof", "room", "root", "rope", "rose", "ruin", "rule", "rush", - "ruth", "sack", "safe", "sage", "sail", "sale", "salt", "sand", "sari", "sash", "save", "scow", "seal", "seat", - "seed", "self", "sell", "shed", "shin", "ship", "shoe", "shop", "shot", "show", "sick", "side", "sign", "silk", - "sill", "silo", "sing", "sink", "site", "size", "skin", "sled", "slip", "smog", "snob", "snow", "soap", "sock", - "soda", "sofa", "soft", "soil", "song", "soot", "sort", "soup", "spot", "spur", "stag", "star", "stay", "stem", - "step", "stew", "stop", "stud", "suck", "suit", "swan", "swim", "tail", "tale", "talk", "tank", "tard", "task", - "taxi", "team", "tear", "teen", "tell", "temp", "tent", "term", "test", "text", "thaw", "tile", "till", "time", - "tire", "toad", "toga", "togs", "tone", "tool", "toot", "tote", "tour", "town", "tram", "tray", "tree", "trim", - "trip", "tuba", "tube", "tuna", "tune", "turn", "tutu", "twig", "type", "unit", "user", "vane", "vase", "vast", - "veal", "veil", "vein", "vest", "vibe", "view", "vise", "wait", "wake", "walk", "wall", "wash", "wasp", "wave", - "wear", "weed", "week", "well", "west", "whip", "wife", "will", "wind", "wine", "wing", "wire", "wish", "wolf", - "wood", "wool", "word", "work", "worm", "wrap", "wren", "yard", "yarn", "yawl", "year", "yoga", "yoke", "yurt", - "zinc", "zone"}; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/CommonService b/src-cpp-structure/projects/Services/include/StatusServices/CommonService deleted file mode 100644 index 6e9d44630..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/CommonService +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include "Common/Constants.h" -#include "Common/SigningPhrases.h" -#include "Common/Json.h" -#include "Common/Logger.h" \ No newline at end of file diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Keychain/Service.h b/src-cpp-structure/projects/Services/include/StatusServices/Keychain/Service.h deleted file mode 100644 index f286ff775..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Keychain/Service.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "ServiceInterface.h" -#include "../src/Keychain/KeychainManager.h" - -namespace Status::Keychain -{ - class Service : public QObject, - public ServiceInterface - { - Q_OBJECT - - public: - explicit Service(); - - void storePassword(const QString& username, const QString& password) override; - - void tryToObtainPassword(const QString& username) override; - - void subscribe(std::shared_ptr listener) override; - - private slots: - void onKeychainManagerError(const QString& errorType, const int errorCode, const QString& errorDescription); - void onKeychainManagerSuccess(const QString& data); - - private: - QVector> m_listeners; - std::unique_ptr m_keychainManager; - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/Keychain/ServiceInterface.h b/src-cpp-structure/projects/Services/include/StatusServices/Keychain/ServiceInterface.h deleted file mode 100644 index 279798292..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/Keychain/ServiceInterface.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -namespace Status::Keychain -{ - const QString ErrorTypeAuthentication = "authentication"; - const QString ErrorTypeKeychain = "keychain"; - - class Listener - { - public: - - virtual ~Listener() = default; - - virtual void onKeychainManagerError(const QString& errorType, const int errorCode, const QString& errorDescription) = 0; - virtual void onKeychainManagerSuccess(const QString& data) = 0; - }; - - class ServiceInterface - { - public: - - virtual ~ServiceInterface() = default; - - virtual void storePassword(const QString& username, const QString& password) = 0; - - virtual void tryToObtainPassword(const QString& username) = 0; - - virtual void subscribe(std::shared_ptr listener) = 0; - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/KeychainService b/src-cpp-structure/projects/Services/include/StatusServices/KeychainService deleted file mode 100644 index edca1d59e..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/KeychainService +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "Keychain/ServiceInterface.h" \ No newline at end of file diff --git a/src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/Service.h b/src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/Service.h deleted file mode 100644 index 4cf4752cb..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/Service.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "ServiceInterface.h" - -namespace Status::WalletAccount -{ - class Service : public ServiceInterface - { - public: - Service(); - - void init() override; - - [[nodiscard]] QList getWalletAccounts() override; - - QString generateNewAccount(const QString& password, const QString& accountName, const QString& color) override; - - QString addAccountsFromPrivateKey(const QString& privateKey, const QString& password, - const QString& accountName, const QString& color) override; - - QString addAccountsFromSeed(const QString& seedPhrase, const QString& password, const QString& accountName, - const QString& color) override; - - QString addWatchOnlyAccount(const QString& address, const QString& accountName , const QString& color) override; - - void deleteAccount(const QString& address) override; - - private: - void fetchAccounts(); - void refreshAccounts(); - - private: - QMap m_walletAccounts; - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/ServiceInterface.h b/src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/ServiceInterface.h deleted file mode 100644 index 6d081b280..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/ServiceInterface.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "WalletAccountDto.h" - -namespace Status::WalletAccount -{ - class ServiceInterface - { - public: - - virtual ~ServiceInterface() = default; - - virtual void init() = 0; - - [[nodiscard]] virtual QList getWalletAccounts() = 0; - - virtual QString generateNewAccount(const QString& password, const QString& accountName, - const QString& color) = 0; - - virtual QString addAccountsFromPrivateKey(const QString& privateKey, const QString& password, - const QString& accountName, const QString& color) = 0; - - virtual QString addAccountsFromSeed(const QString& seedPhrase, const QString& password, - const QString& accountName, const QString& color) = 0; - - virtual QString addWatchOnlyAccount(const QString& address, const QString& accountName , - const QString& color) = 0; - - virtual void deleteAccount(const QString& address) = 0; - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/WalletAccountDto.h b/src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/WalletAccountDto.h deleted file mode 100644 index bc6bf3b80..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/WalletAccounts/WalletAccountDto.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include - -#include - -namespace Status::WalletAccount -{ - struct WalletToken - { - QString name; - QString address; - QString symbol; - int decimals; - bool hasIcon; - QString color; - bool isCustom; - float balance; - float currencyBalance; - }; - - struct WalletAccountDto - { - QString name; - QString address; - QString path; - QString color; - QString publicKey; - QString walletType; - bool isWallet; - bool isChat; - QVector tokens; // this is not set by mapping remote DTO, but built on the app side (set later) - - static WalletAccountDto toWalletAccountDto(const QJsonObject& jsonObj) - { - auto result = WalletAccountDto(); - - try - { - result.name = Json::getMandatoryProp(jsonObj, "name")->toString(); - result.address = Json::getMandatoryProp(jsonObj, "address")->toString(); - result.path = Json::getMandatoryProp(jsonObj, "path")->toString(); - result.color = Json::getMandatoryProp(jsonObj, "color")->toString(); - result.publicKey = Json::getMandatoryProp(jsonObj, "public-key")->toString(); - result.walletType = Json::getMandatoryProp(jsonObj, "type")->toString(); - result.isWallet = Json::getMandatoryProp(jsonObj, "wallet")->toBool(); - result.isChat = Json::getMandatoryProp(jsonObj, "chat")->toBool(); - } - catch (std::exception e) - { - qWarning() << QObject::tr("Mapping WalletAccountDto failed: %1").arg(e.what()); - } - - return result; - } - }; -} diff --git a/src-cpp-structure/projects/Services/include/StatusServices/WalletAccountsService b/src-cpp-structure/projects/Services/include/StatusServices/WalletAccountsService deleted file mode 100644 index c4990a27f..000000000 --- a/src-cpp-structure/projects/Services/include/StatusServices/WalletAccountsService +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include "WalletAccounts/WalletAccountDto.h" -#include "WalletAccounts/ServiceInterface.h" \ No newline at end of file diff --git a/src-cpp-structure/projects/Services/src/Accounts/Service.cpp b/src-cpp-structure/projects/Services/src/Accounts/Service.cpp deleted file mode 100644 index 3a0c499c1..000000000 --- a/src-cpp-structure/projects/Services/src/Accounts/Service.cpp +++ /dev/null @@ -1,403 +0,0 @@ -#include "StatusServices/Accounts/Service.h" - -#include "StatusBackend/Accounts.h" -#include "StatusBackend/Utils.h" - -using namespace Status::Accounts; - -Service::Service() - : m_isFirstTimeAccountLogin(false) -{ -} - -void Service::init(const QString& statusgoDataDir) -{ - m_statusgoDataDir = statusgoDataDir; - auto response = Backend::Accounts::generateAddresses(Constants::General::AccountDefaultPaths); - if(response.containsError()) - { - qWarning() << response.error.message; - return; - } - - foreach(const auto& genAddressObj, response.result) - { - auto gAcc = GeneratedAccountDto::toGeneratedAccountDto(genAddressObj.toObject()); - gAcc.alias = generateAlias(gAcc.derivedAccounts.whisper.publicKey); - gAcc.identicon = generateIdenticon(gAcc.derivedAccounts.whisper.publicKey); - m_generatedAccounts.append(std::move(gAcc)); - } -} - -QVector Service::openedAccounts() -{ - auto response = Backend::Accounts::openAccounts(m_statusgoDataDir); - if(response.containsError()) - { - qWarning() << response.error.message; - return QVector(); - } - - QJsonArray multiAccounts = response.result; - QVector result; - foreach(const auto& value, multiAccounts) - { - result << AccountDto::toAccountDto(value.toObject()); - } - return result; -} - -const QVector& Service::generatedAccounts() const -{ - return m_generatedAccounts; -} - -bool Service::setupAccount(const QString& accountId, const QString& password) -{ - QString installationId(QUuid::createUuid().toString(QUuid::WithoutBraces)); - QJsonObject accountData(getAccountDataForAccountId(accountId)); - QJsonArray subAccountData(getSubaccountDataForAccountId(accountId)); - QJsonObject settings(getAccountSettings(accountId, installationId)); - QJsonObject nodeConfig(getDefaultNodeConfig(installationId)); - - QString hashedPassword(Backend::Utils::hashString(password)); - - Service::storeDerivedAccounts(accountId, hashedPassword, Constants::General::AccountDefaultPaths); - - m_loggedInAccount = saveAccountAndLogin(hashedPassword, accountData, subAccountData, settings, nodeConfig); - - return getLoggedInAccount().isValid(); -} - -const AccountDto& Service::getLoggedInAccount() const -{ - return m_loggedInAccount; -} - -const GeneratedAccountDto& Service::getImportedAccount() const -{ - return m_importedAccount; -} - -bool Service::isFirstTimeAccountLogin() const -{ - return m_isFirstTimeAccountLogin; -} - -QString Service::validateMnemonic(const QString& mnemonic) -{ - // TODO: - return ""; -} - -bool Service::importMnemonic(const QString& mnemonic) -{ - // TODO: - return false; -} - -QString Service::login(AccountDto account, const QString& password) -{ - QString hashedPassword(Backend::Utils::hashString(password)); - - QString thumbnailImage; - QString largeImage; - - foreach(const Image& img, account.images) - { - if(img.imgType == "thumbnail") - { - thumbnailImage = img.uri; - } - else if(img.imgType == "large") - { - largeImage = img.uri; - } - } - - auto response = Backend::Accounts::login(account.name, account.keyUid, hashedPassword, account.identicon, - thumbnailImage, largeImage); - if(response.containsError()) - { - qWarning() << response.error.message; - return QString(); - } - - qInfo() << "Account logged in"; - - m_loggedInAccount = std::move(account); - - return QString(); -} - -void Service::clear() -{ - m_generatedAccounts.clear(); - m_loggedInAccount = AccountDto(); - m_importedAccount = GeneratedAccountDto(); - m_isFirstTimeAccountLogin = false; -} - -QString Service::generateAlias(const QString& publicKey) -{ - auto response = Backend::Accounts::generateAlias(publicKey); - if(response.containsError()) - { - qWarning() << response.error.message; - return QString(); - } - - return response.result; -} - -QString Service::generateIdenticon(const QString& publicKey) -{ - auto response = Backend::Accounts::generateIdenticon(publicKey); - if(response.containsError()) - { - qWarning() << response.error.message; - return QString(); - } - - return response.result; -} - -bool Service::verifyAccountPassword(const QString& account, const QString& password) -{ - // TODO: - return false; -} - -DerivedAccounts Service::storeDerivedAccounts(const QString& accountId, const QString& hashedPassword, - const QVector& paths) -{ - auto response = Backend::Accounts::storeDerivedAccounts(accountId, hashedPassword, paths); - if(response.containsError()) - { - qWarning() << response.error.message; - return DerivedAccounts(); - } - return DerivedAccounts::toDerivedAccounts(response.result); -} - -AccountDto Service::saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, - const QJsonArray& subaccounts, const QJsonObject& settings, - const QJsonObject& config) -{ - auto response = Backend::Accounts::saveAccountAndLogin(hashedPassword, account, subaccounts, settings, config); - if(response.containsError()) - { - qWarning() << response.error.message; - return AccountDto(); - } - - m_isFirstTimeAccountLogin = true; - return AccountDto::toAccountDto(response.result); -} - -QJsonObject Service::prepareAccountJsonObject(const GeneratedAccountDto& account) const -{ - return QJsonObject{{"name", account.alias}, - {"address", account.address}, - {"photo-path", account.identicon}, - {"identicon", account.identicon}, - {"key-uid", account.keyUid}, - {"keycard-pairing", QJsonValue()}}; -} - -QJsonObject Service::getAccountDataForAccountId(const QString& accountId) const -{ - - foreach(const GeneratedAccountDto& acc, m_generatedAccounts) - { - if(acc.id == accountId) - { - return Service::prepareAccountJsonObject(acc); - } - } - - if(m_importedAccount.isValid()) - { - if(m_importedAccount.id == accountId) - { - return Service::prepareAccountJsonObject(m_importedAccount); - } - } - - qDebug() << "account not found"; - return QJsonObject(); -} - -QJsonArray Service::prepareSubaccountJsonObject(const GeneratedAccountDto& account) const -{ - return QJsonArray{ - QJsonObject{ - {"public-key", account.derivedAccounts.defaultWallet.publicKey}, - {"address", account.derivedAccounts.defaultWallet.address}, - {"color", "#4360df"}, - {"wallet", true}, - {"path", Constants::General::PathDefaultWallet}, - {"name", "Status account"} - }, - QJsonObject{ - {"public-key", account.derivedAccounts.whisper.publicKey}, - {"address", account.derivedAccounts.whisper.address}, - {"path", Constants::General::PathWhisper}, - {"name", account.alias}, - {"identicon", account.identicon}, - {"chat", true} - } - }; -} - -QJsonArray Service::getSubaccountDataForAccountId(const QString& accountId) const -{ - foreach(const GeneratedAccountDto& acc, m_generatedAccounts) - { - if(acc.id == accountId) - { - return prepareSubaccountJsonObject(acc); - } - } - if(m_importedAccount.isValid()) - { - if(m_importedAccount.id == accountId) - { - return prepareSubaccountJsonObject(m_importedAccount); - } - } - - qDebug() << "account not found"; - return QJsonArray(); -} - -QString Service::generateSigningPhrase(const int count) const -{ - QStringList words; - for(int i = 0; i < count; i++) - { - words.append(Constants::SigningPhrases[QRandomGenerator::global()->bounded( - static_cast(Constants::SigningPhrases.size()))]); - } - return words.join(" "); -} - -QJsonObject Service::prepareAccountSettingsJsonObject(const GeneratedAccountDto& account, - const QString& installationId) const -{ - QFile defaultNetworks(":/resources/default-networks.json"); - if(!defaultNetworks.open(QIODevice::ReadOnly)) - { - qDebug() << "unable to open `default-networks.json` for reading"; - return QJsonObject(); - } - - QByteArray readData = defaultNetworks.readAll(); - defaultNetworks.close(); - - QString defaultNetworksContent = readData.replace("%INFURA_KEY%", INFURA_KEY); - QJsonArray defaultNetworksJson = QJsonDocument::fromJson(defaultNetworksContent.toUtf8()).array(); - - return QJsonObject{ - {"key-uid", account.keyUid}, - {"mnemonic", account.mnemonic}, - {"public-key", account.derivedAccounts.whisper.publicKey}, - {"name", account.alias}, - {"address", account.address}, - {"eip1581-address", account.derivedAccounts.eip1581.address}, - {"dapps-address", account.derivedAccounts.defaultWallet.address}, - {"wallet-root-address", account.derivedAccounts.walletRoot.address}, - {"preview-privacy?", true}, - {"signing-phrase", generateSigningPhrase(3)}, - {"log-level", "INFO"}, - {"latest-derived-path", 0}, - {"networks/networks", defaultNetworksJson}, - {"currency", "usd"}, - {"identicon", account.identicon}, - {"waku-enabled", true}, - {"wallet/visible-tokens", { - {Constants::General::DefaultNetworkName, QJsonArray{"SNT"}} - } - }, - {"appearance", 0}, - {"networks/current-network", Constants::General::DefaultNetworkName}, - {"installation-id", installationId} - }; -} - -QJsonObject Service::getAccountSettings(const QString& accountId, const QString& installationId) const -{ - foreach(const GeneratedAccountDto& acc, m_generatedAccounts) - - if(acc.id == accountId) - { - return Service::prepareAccountSettingsJsonObject(acc, installationId); - } - - if(m_importedAccount.isValid()) - { - if(m_importedAccount.id == accountId) - { - return Service::prepareAccountSettingsJsonObject(m_importedAccount, installationId); - } - } - - qDebug() << "account not found"; - return QJsonObject(); -} - -QJsonArray getNodes(const QJsonObject& fleet, const QString& nodeType) -{ - auto nodes = fleet[nodeType].toObject(); - QJsonArray result; - for(auto it = nodes.begin(); it != nodes.end(); ++it) - result << *it; - return result; -} - -QJsonObject Service::getDefaultNodeConfig(const QString& installationId) const -{ - QFile nodeConfig(":/resources/node-config.json"); - if(!nodeConfig.open(QIODevice::ReadOnly)) - { - qDebug() << "unable to open `node-config.json` for reading"; - return QJsonObject(); - } - - QString nodeConfigContent = nodeConfig.readAll(); - nodeConfig.close(); - - - QFile fleets(":/resources/fleets.json"); - if(!fleets.open(QIODevice::ReadOnly)) - { - qDebug() << "unable to open `fleets.json` for reading"; - return QJsonObject(); - } - - QByteArray readFleetData = fleets.readAll(); - fleets.close(); - - - nodeConfigContent = nodeConfigContent.replace("%INSTALLATIONID%", installationId); - nodeConfigContent = nodeConfigContent.replace("%INFURA_KEY%", INFURA_KEY); - QJsonObject nodeConfigJson = QJsonDocument::fromJson(nodeConfigContent.toUtf8()).object(); - QJsonObject clusterConfig = nodeConfigJson["ClusterConfig"].toObject(); - - QJsonObject fleetsJson = QJsonDocument::fromJson(readFleetData).object()["fleets"].toObject(); - auto fleet = fleetsJson[Constants::Fleet::Prod].toObject(); - - clusterConfig["Fleet"] = Constants::Fleet::Prod; - clusterConfig["BootNodes"] = getNodes(fleet, Constants::FleetNodes::Bootnodes); - clusterConfig["TrustedMailServers"] = getNodes(fleet, Constants::FleetNodes::Mailservers); - clusterConfig["StaticNodes"] = getNodes(fleet, Constants::FleetNodes::Whisper); - clusterConfig["RendezvousNodes"] = getNodes(fleet, Constants::FleetNodes::Rendezvous); - clusterConfig["RelayNodes"] = getNodes(fleet, Constants::FleetNodes::Waku); - clusterConfig["StoreNodes"] = getNodes(fleet, Constants::FleetNodes::Waku); - clusterConfig["FilterNodes"] = getNodes(fleet, Constants::FleetNodes::Waku); - clusterConfig["LightpushNodes"] = getNodes(fleet, Constants::FleetNodes::Waku); - - nodeConfigJson["ClusterConfig"] = clusterConfig; - - return nodeConfigJson; -} diff --git a/src-cpp-structure/projects/Services/src/Keychain/Keychain.h b/src-cpp-structure/projects/Services/src/Keychain/Keychain.h deleted file mode 100644 index 6d24266a0..000000000 --- a/src-cpp-structure/projects/Services/src/Keychain/Keychain.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -namespace Status::Keychain -{ - class Keychain : public QObject - { - Q_OBJECT - - public: - - enum Error { - NoError=0, - EntryNotFound, - CouldNotDeleteEntry, - AccessDeniedByUser, - AccessDenied, - NoBackendAvailable, - NotImplemented, - OtherError - }; - - Keychain(const QString& service, QObject *parent = nullptr); - - void readItem(const QString& key); - void writeItem(const QString& key, const QString& data); - void deleteItem(const QString& key); - - signals: - void success(QString data); - void error(int error, const QString& errorString); - - private: - QString m_service; - }; -} diff --git a/src-cpp-structure/projects/Services/src/Keychain/Keychain.mm b/src-cpp-structure/projects/Services/src/Keychain/Keychain.mm deleted file mode 100644 index 3654f3b88..000000000 --- a/src-cpp-structure/projects/Services/src/Keychain/Keychain.mm +++ /dev/null @@ -1,162 +0,0 @@ -#include "Keychain.h" - -#import -#import - -using namespace Status::Keychain; - -struct ErrorDescription -{ - Keychain::Error code; - QString message; - - ErrorDescription(Keychain::Error code, const QString &message) - : code(code) - , message(message) - {} - - static ErrorDescription fromStatus(OSStatus status) - { - switch(status) { - case errSecSuccess: - return ErrorDescription(Keychain::NoError, - "No error"); - case errSecItemNotFound: - return ErrorDescription(Keychain::EntryNotFound, - "The specified item could not be found in the keychain"); - case errSecUserCanceled: - return ErrorDescription(Keychain::AccessDeniedByUser, - "User canceled the operation"); - case errSecInteractionNotAllowed: - return ErrorDescription(Keychain::AccessDenied, - "User interaction is not allowed"); - case errSecNotAvailable: - return ErrorDescription(Keychain::AccessDenied, - "No keychain is available. You may need to restart your computer"); - case errSecAuthFailed: - return ErrorDescription(Keychain::AccessDenied, - "The user name or passphrase you entered is not correct"); - case errSecVerifyFailed: - return ErrorDescription(Keychain::AccessDenied, - "A cryptographic verification failure has occurred"); - case errSecUnimplemented: - return ErrorDescription(Keychain::NotImplemented, - "Function or operation not implemented"); - case errSecIO: - return ErrorDescription(Keychain::OtherError, - "I/O error"); - case errSecOpWr: - return ErrorDescription(Keychain::OtherError, - "Already open with with write permission"); - case errSecParam: - return ErrorDescription(Keychain::OtherError, - "Invalid parameters passed to a function"); - case errSecAllocate: - return ErrorDescription(Keychain::OtherError, - "Failed to allocate memory"); - case errSecBadReq: - return ErrorDescription(Keychain::OtherError, - "Bad parameter or invalid state for operation"); - case errSecInternalComponent: - return ErrorDescription(Keychain::OtherError, - "An internal component failed"); - case errSecDuplicateItem: - return ErrorDescription(Keychain::OtherError, - "The specified item already exists in the keychain"); - case errSecDecode: - return ErrorDescription(Keychain::OtherError, - "Unable to decode the provided data"); - } - - return ErrorDescription(Keychain::OtherError, "Unknown error"); - } -}; - -Keychain::Keychain(const QString& service, QObject *parent) - : QObject(parent) - , m_service(service) -{} - -void Keychain::readItem(const QString& key) -{ - NSDictionary *const query = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) m_service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - (__bridge id) kSecReturnData: @YES, - }; - - CFTypeRef dataRef = nil; - const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef); - - if (status == errSecSuccess) { - QByteArray data; - if (dataRef) - data = QByteArray::fromCFData((CFDataRef) dataRef); - - emit success(QString::fromUtf8(data)); - } else { - const ErrorDescription ed = ErrorDescription::fromStatus(status); - emit error(ed.code, - QString("Could not retrieve private key from keystore: %1").arg(ed.message)); - } - - if (dataRef) - [dataRef release]; -} - -void Keychain::writeItem(const QString& key, const QString& data) -{ - NSDictionary *const query = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) m_service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - }; - - OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil); - - QByteArray baData = data.toUtf8(); - if (status == errSecSuccess) { - NSDictionary *const update = @{ - (__bridge id) kSecValueData: (__bridge NSData *) baData.toCFData(), - }; - - status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) update); - } else { - NSDictionary *const insert = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) m_service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - (__bridge id) kSecValueData: (__bridge NSData *) baData.toCFData(), - }; - - status = SecItemAdd((__bridge CFDictionaryRef) insert, nil); - } - - if (status == errSecSuccess) { - emit success(QString()); - } else { - const ErrorDescription ed = ErrorDescription::fromStatus(status); - emit error(ed.code, - QString("Could not store data in settings: %1").arg(ed.message)); - } -} - -void Keychain::deleteItem(const QString& key) -{ - const NSDictionary *const query = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) m_service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - }; - - const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query); - - if (status == errSecSuccess) { - emit success(QString()); - } else { - const ErrorDescription ed = ErrorDescription::fromStatus(status); - emit error(ed.code, - QString("Could not remove private key from keystore: %1").arg(ed.message)); - } -} diff --git a/src-cpp-structure/projects/Services/src/Keychain/KeychainManager.cpp b/src-cpp-structure/projects/Services/src/Keychain/KeychainManager.cpp deleted file mode 100644 index af5662c9a..000000000 --- a/src-cpp-structure/projects/Services/src/Keychain/KeychainManager.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "KeychainManager.h" - -using namespace Status::Keychain; - -KeychainManager::KeychainManager(const QString& service, - const QString& authenticationReason, QObject* parent) - : QObject(parent) -{ -#ifdef Q_OS_MACOS - m_authenticationReason = authenticationReason; - m_localAuth = std::unique_ptr(new LocalAuthentication()); - m_keychain = std::unique_ptr(new Keychain(service)); - - connect(m_localAuth.get(), &LocalAuthentication::error, [this](int code, const QString& errorString){ - emit error("authentication", code, errorString); - }); - - connect(m_keychain.get(), &Keychain::success, this, &KeychainManager::success); - connect(m_keychain.get(), &Keychain::error, [this](int code, const QString& errorString){ - emit error("keychain", code, errorString); - }); -#else - // Marked params unused until we need them for Win/Linux. - Q_UNUSED(authenticationReason); - Q_UNUSED(service); -#endif -} - -QString KeychainManager::readDataSync(const QString& key) const -{ -#ifdef Q_OS_MACOS - return readDataSyncMacOs(key); -#endif - - return QString(); -} - -void KeychainManager::readDataAsync(const QString& key) -{ -#ifdef Q_OS_MACOS - readDataAsyncMacOs(key); -#endif -} - -void KeychainManager::storeDataAsync(const QString& key, const QString& data) -{ -#ifdef Q_OS_MACOS - storeDataAsyncMacOs(key, data); -#endif -} - -void KeychainManager::deleteDataAsync(const QString& key) -{ -#ifdef Q_OS_MACOS - deleteDataAsyncMacOs(key); -#endif -} diff --git a/src-cpp-structure/projects/Services/src/Keychain/KeychainManager.h b/src-cpp-structure/projects/Services/src/Keychain/KeychainManager.h deleted file mode 100644 index c5eb4f5b9..000000000 --- a/src-cpp-structure/projects/Services/src/Keychain/KeychainManager.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include "Keychain.h" -#include "LocalAuthentication.h" - -#include - -namespace Status::Keychain -{ - class KeychainManager : public QObject - { - Q_OBJECT - - public: - - /*! - * Constructor defining name of the service for storing in the Keychain - * and the reason for requesting authorisation via touch id. - * - * @param service Service name used in Keychain. - * @param authenticationReason Reason for requestion touch id authorization. - */ - KeychainManager(const QString& service, - const QString& authenticationReason, QObject* parent = nullptr); - - /*! - * Synchronously reads @data stored in the Keychain under the @key and - * returns stored @data. In case of any error an empty string will be - * returned and error signal will be emitted. - * - * @param key Key which is stored in the Keychain. - */ - QString readDataSync(const QString& key) const; - - /*! - * Asynchronously reads @data stored in the Keychain under the @key. - * Onces it's read success signal will be emitted containing read data, - * otherwise error signal will be emitted. - * - * @param key Key which is stored in the Keychain. - */ - void readDataAsync(const QString& key); - /*! - * Asynchronously stores @data under the @key in the Keychain. - * Onces @data is stored success signal will be emitted, otherwise error - * signal will be emitted. - * - * @param key Key which is stored in the Keychain. - * @param data Data which is stored in the Keychain. - */ - void storeDataAsync(const QString& key, const QString& data); - /*! - * Asynchronously deletes @data stored in the Keychain under the @key. - * Onces it's deleted success signal will be emitted, otherwise error - * signal will be emitted. - * - * @param key Key which is stored in the Keychain. - */ - void deleteDataAsync(const QString& key); - - signals: - /*! - * Notifies that action was performed successfully and in case of asyc - * read contains read @data, in other cases @data param is empty. - * - * @param data Data read from the Keychain. - */ - void success(const QString& data); - /*! - * Notifies that an error with @error code and @errorString description - * occured. - * - * @param type Determins origin of the error ("authentication" or "keychain") - * @param code Error code. - * @param errorString Error description. - */ - void error(const QString& type, const int code, const QString& errorString); - -#ifdef Q_OS_MACOS - private: - void process(const std::function action); - - QString readDataSyncMacOs(const QString& key) const; - void readDataAsyncMacOs(const QString& key); - void storeDataAsyncMacOs(const QString& key, const QString& data); - void deleteDataAsyncMacOs(const QString& key); - - private: - QString m_authenticationReason; - std::unique_ptr m_localAuth; - std::unique_ptr m_keychain; - QMetaObject::Connection m_actionConnection; -#endif - }; -} diff --git a/src-cpp-structure/projects/Services/src/Keychain/KeychainManager.mm b/src-cpp-structure/projects/Services/src/Keychain/KeychainManager.mm deleted file mode 100644 index 28698238a..000000000 --- a/src-cpp-structure/projects/Services/src/Keychain/KeychainManager.mm +++ /dev/null @@ -1,74 +0,0 @@ -#include "KeychainManager.h" - -using namespace Status::Keychain; - -QString KeychainManager::readDataSyncMacOs(const QString& key) const -{ - QString storedData; - QEventLoop loop; - - auto onAuthenticationSuccess = [this, &key](){ - m_keychain->readItem(key); - }; - - auto onReadItemSuccess = [&loop, &storedData](QString data){ - storedData = data; - loop.quit(); - }; - - connect(m_localAuth.get(), &LocalAuthentication::success, onAuthenticationSuccess); - - connect(m_keychain.get(), &Keychain::success, onReadItemSuccess); - - connect(m_localAuth.get(), &LocalAuthentication::error, [this, &loop](int code, const QString& errorString){ - Q_UNUSED(code) - Q_UNUSED(errorString) - loop.quit(); - }); - - connect(m_keychain.get(), &Keychain::error, [this, &loop](int code, const QString& errorString){ - Q_UNUSED(code) - Q_UNUSED(errorString) - loop.quit(); - }); - - m_localAuth->runAuthentication(m_authenticationReason); - loop.exec(); - - return storedData; -} - -void KeychainManager::readDataAsyncMacOs(const QString& key) -{ - auto readAction = [this, key](){ - m_keychain->readItem(key); - }; - - process(readAction); -} - -void KeychainManager::storeDataAsyncMacOs(const QString& key, const QString& data) -{ - auto writeAction = [this, key, data](){ - m_keychain->writeItem(key, data); - }; - - process(writeAction); -} - -void KeychainManager::deleteDataAsyncMacOs(const QString& key) -{ - auto deleteAction = [this, key](){ - m_keychain->deleteItem(key); - }; - - process(deleteAction); -} - -void KeychainManager::process(const std::function action) -{ - disconnect(m_actionConnection); - m_actionConnection = connect(m_localAuth.get(), &LocalAuthentication::success, action); - - m_localAuth->runAuthentication(m_authenticationReason); -} diff --git a/src-cpp-structure/projects/Services/src/Keychain/LocalAuthentication.h b/src-cpp-structure/projects/Services/src/Keychain/LocalAuthentication.h deleted file mode 100644 index 11fea9ed7..000000000 --- a/src-cpp-structure/projects/Services/src/Keychain/LocalAuthentication.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include - -namespace Status::Keychain -{ - class LocalAuthentication : public QObject - { - Q_OBJECT - - public: - - enum Error { - Domain=0, - AppCanceled, - SystemCanceled, - UserCanceled, - TouchIdNotAvailable, - TouchIdNotConfigured, - WrongCredentials, - OtherError - }; - - void runAuthentication(const QString& authenticationReason); - - signals: - void success(); - void error(int error, const QString& errorString); - }; -} diff --git a/src-cpp-structure/projects/Services/src/Keychain/LocalAuthentication.mm b/src-cpp-structure/projects/Services/src/Keychain/LocalAuthentication.mm deleted file mode 100644 index 03eaf6b16..000000000 --- a/src-cpp-structure/projects/Services/src/Keychain/LocalAuthentication.mm +++ /dev/null @@ -1,152 +0,0 @@ -#include "LocalAuthentication.h" - -#include -#import - -using namespace Status::Keychain; - -struct ErrorDescription -{ - LocalAuthentication::Error code; - QString message; - - ErrorDescription(LocalAuthentication::Error code, const QString &message) - : code(code) - , message(message) - {} - - static ErrorDescription fromLAError(NSInteger err) - { - NSProcessInfo *pInfo = [NSProcessInfo processInfo]; - NSOperatingSystemVersion info = [pInfo operatingSystemVersion]; - -#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED -#if defined MAC_OS_VERSION_11_2 \ - && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_2 - switch(err) { - case LAErrorBiometryDisconnected: /* MacOs 11.2+ */ - return ErrorDescription(LocalAuthentication::OtherError, - "The device supports biometry only using a removable accessory, but the paired accessory isn’t connected"); - case LAErrorBiometryNotPaired: /* MacOs 11.2+ */ - return ErrorDescription(LocalAuthentication::OtherError, - "The device supports biometry only using a removable accessory, but no accessory is paired"); - } -#endif - -#if defined MAC_OS_X_VERSION_10_15 \ - && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 - switch(err) { - case LAErrorWatchNotAvailable: /* MacOs 10.15+ */ - return ErrorDescription(LocalAuthentication::OtherError, - "An attempt to authenticate with Apple Watch failed"); - } -#endif - -#if defined MAC_OS_X_VERSION_10_13 \ - && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - switch(err) { - case LAErrorBiometryLockout: /* MacOs 10.13+ */ - return ErrorDescription(LocalAuthentication::OtherError, - "Biometry is locked because there were too many failed attempts"); - case LAErrorBiometryNotAvailable: /* MacOs 10.13+ */ - return ErrorDescription(LocalAuthentication::TouchIdNotAvailable, - "Biometry is not available on the device"); - case LAErrorBiometryNotEnrolled: /* MacOs 10.13+ */ - return ErrorDescription(LocalAuthentication::TouchIdNotConfigured, - "The user has no enrolled biometric identities"); - } -#endif - -#if defined MAC_OS_X_VERSION_10_11 \ - && defined MAC_OS_X_VERSION_10_13 \ - && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11 \ - && MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_13 - switch(err) { - case LAErrorTouchIDLockout: /* MacOs 10.11 - 10.13 */ - return ErrorDescription(LocalAuthentication::OtherError, - "Touch ID is locked because there were too many failed attempts"); - } -#endif - -#if defined MAC_OS_X_VERSION_10_10 \ - && defined MAC_OS_X_VERSION_10_13 \ - && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 \ - && MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_13 - switch(err) { - case LAErrorTouchIDNotAvailable: /* MacOs 10.10 - 10.13 */ - return ErrorDescription(LocalAuthentication::TouchIdNotAvailable, - "Touch ID is not available on the device"); - case LAErrorTouchIDNotEnrolled: /* MacOs 10.10 - 10.13 */ - return ErrorDescription(LocalAuthentication::TouchIdNotConfigured, - "The user has no enrolled Touch ID fingers"); - } -#endif - -#if defined MAC_OS_X_VERSION_10_11 \ - && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11 - switch(err) { - case LAErrorInvalidContext: /* MacOs 10.11+ */ - return ErrorDescription(LocalAuthentication::OtherError, - "The context was previously invalidated"); - } -#endif - -#if defined MAC_OS_X_VERSION_10_10 \ - && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - switch(err) { - case LAErrorSystemCancel: /* MacOs 10.10+ */ - return ErrorDescription(LocalAuthentication::SystemCanceled, - "The system canceled authentication"); - case LAErrorUserCancel: /* MacOs 10.10+ */ - return ErrorDescription(LocalAuthentication::UserCanceled, - "The user tapped the cancel button in the authentication dialog"); - case LAErrorAuthenticationFailed: /* MacOs 10.10+ */ - return ErrorDescription(LocalAuthentication::WrongCredentials, - "The user failed to provide valid credentials"); - case LAErrorNotInteractive: /* MacOs 10.10+ */ - return ErrorDescription(LocalAuthentication::OtherError, - "Displaying the required authentication user interface is forbidden"); - case LAErrorPasscodeNotSet: /* MacOs 10.10+ */ - return ErrorDescription(LocalAuthentication::OtherError, - "A passcode isn’t set on the device"); - case LAErrorUserFallback: /* MacOs 10.10+ */ - return ErrorDescription(LocalAuthentication::OtherError, - "The user tapped the fallback button in the authentication dialog, but no fallback is available for the authentication policy"); - } -#endif - -#endif - - return ErrorDescription(LocalAuthentication::OtherError, "Unknown error"); - } -}; - -void LocalAuthentication::runAuthentication(const QString& authenticationReason) -{ - LAContext *laContext = [[LAContext alloc] init]; - NSError *authError = nil; - NSString *localizedReasonString = authenticationReason.toNSString(); - - if ([laContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError]) - { - [laContext evaluatePolicy:LAPolicyDeviceOwnerAuthentication - localizedReason:localizedReasonString - reply:^(BOOL authenticated, NSError *err) - { - if (authenticated) - { - emit success(); - } - else - { - const ErrorDescription ed = ErrorDescription::fromLAError([err code]); - emit error(ed.code, QString("User did not authenticate successfully: %1").arg(ed.message)); - } - }]; - } - else - { - const ErrorDescription ed = ErrorDescription::fromLAError([authError code]); - emit error(ed.code, QString("Could not evaluate policy: %1").arg(ed.message)); - } -} diff --git a/src-cpp-structure/projects/Services/src/Keychain/Service.cpp b/src-cpp-structure/projects/Services/src/Keychain/Service.cpp deleted file mode 100644 index 8299788d5..000000000 --- a/src-cpp-structure/projects/Services/src/Keychain/Service.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "StatusServices/Keychain/Service.h" - -#include "KeychainManager.h" - -using namespace Status::Keychain; - -Service::Service() : QObject(nullptr) - , m_keychainManager(std::make_unique("StatusDesktop", "authenticate you")) -{ - connect(m_keychainManager.get(), &KeychainManager::success, this, &Service::onKeychainManagerSuccess, Qt::QueuedConnection); - connect(m_keychainManager.get(), &KeychainManager::error, this, &Service::onKeychainManagerError, Qt::QueuedConnection); -} - -void Service::storePassword(const QString& username, const QString& password) -{ - m_keychainManager->storeDataAsync(username, password); -} - -void Service::tryToObtainPassword(const QString& username) -{ - m_keychainManager->readDataAsync(username); -} - -void Service::subscribe(std::shared_ptr listener) -{ - m_listeners.append(std::move(listener)); -} - -void Service::onKeychainManagerError(const QString& errorType, const int errorCode, const QString& errorDescription) -{ - // This slot is called in case an error occured while we're dealing with - // KeychainManager. So far we're just logging the error. - qWarning() << "KeychainManager stopped, code: " << errorCode << " desc: " << errorDescription; - - for(const auto& it : m_listeners) - { - it->onKeychainManagerError(errorType, errorCode, errorDescription); - } -} - -void Service::onKeychainManagerSuccess(const QString& data) -{ - for(const auto& it : m_listeners) - { - it->onKeychainManagerSuccess(data); - } -} diff --git a/src-cpp-structure/projects/Services/src/WalletAccounts/Service.cpp b/src-cpp-structure/projects/Services/src/WalletAccounts/Service.cpp deleted file mode 100644 index 2a8e59578..000000000 --- a/src-cpp-structure/projects/Services/src/WalletAccounts/Service.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "StatusServices/WalletAccounts/Service.h" - -#include "StatusBackend/WalletAccounts.h" - -using namespace Status::WalletAccount; - -Service::Service() -{ -} - -void Service::init() -{ - fetchAccounts(); -} - -void Service::fetchAccounts() -{ - auto response = Backend::Wallet::Accounts::getAccounts(); - if(response.containsError()) - { - qWarning() << response.error.message; - return; - } - - foreach(const auto& value, response.result) - { - auto account = WalletAccountDto::toWalletAccountDto(value.toObject()); - if(!account.isChat) - { - m_walletAccounts[account.address] = std::move(account); - } - } -} - -void Service::refreshAccounts() -{ - fetchAccounts(); -} - -QList Service::getWalletAccounts() -{ - return m_walletAccounts.values(); -} - -QString Service::generateNewAccount(const QString& password, const QString& accountName, const QString& color) -{ - auto response = Backend::Wallet::Accounts::generateNewAccount(password, accountName, color); - if(response.containsError()) - { - qWarning() << response.error.message; - return response.error.message; - } - - refreshAccounts(); - return QString(); -} - -QString Service::addAccountsFromPrivateKey(const QString& privateKey, const QString& password, - const QString& accountName, const QString& color) -{ - auto response = Backend::Wallet::Accounts::addAccountsFromPrivateKey(privateKey, password, accountName, color); - if(response.containsError()) - { - qWarning() << response.error.message; - return response.error.message; - } - - refreshAccounts(); - return QString(); -} - -QString Service::addAccountsFromSeed(const QString& seedPhrase, const QString& password, const QString& accountName, const QString& color) -{ - auto response = Backend::Wallet::Accounts::addAccountsFromSeed(seedPhrase, password, accountName, color); - if(response.containsError()) - { - qWarning() << response.error.message; - return response.error.message; - } - - refreshAccounts(); - return QString(); -} - -QString Service::addWatchOnlyAccount(const QString& address, const QString& accountName , const QString& color) -{ - auto response = Backend::Wallet::Accounts::addWatchOnlyAccount(address, accountName, color); - if(response.containsError()) - { - qWarning() << response.error.message; - return response.error.message; - } - - refreshAccounts(); - return QString(); -} - -void Service::deleteAccount(const QString& address) -{ - auto response = Backend::Wallet::Accounts::deleteAccount(address); - if(response.containsError()) - { - qWarning() << response.error.message; - return; - } - - refreshAccounts(); -} diff --git a/src-cpp-structure/projects/ServicesTest/CMakeLists.txt b/src-cpp-structure/projects/ServicesTest/CMakeLists.txt deleted file mode 100644 index 7ca3ec35b..000000000 --- a/src-cpp-structure/projects/ServicesTest/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -cmake_minimum_required(VERSION 3.18 FATAL_ERROR) - -project(Status.ServicesTest DESCRIPTION "Test Status Services Project") - -SET(STATUS_RCC ${CMAKE_SOURCE_DIR}/../resources.rcc) -SET(STATUS_QRC ${CMAKE_SOURCE_DIR}/../ui/resources.qrc) -SET(STATUS_RESOURCES_QRC ${CMAKE_SOURCE_DIR}/../resources/resources.qrc) - -conan_basic_setup(KEEP_RPATHS) - -add_executable(${PROJECT_NAME}) - -add_dependencies(${PROJECT_NAME} Status.Services) - -target_link_libraries( - ${PROJECT_NAME} - Qt5::Core - ${CONAN_LIBS_GTEST} - ${FOUNDATION_FRAMEWORK} - ${IO_KIT_FRAMEWORK} - Status.Services - ) - -file(GLOB_RECURSE SOURCES - "*.h" - "*.cpp" - ${STATUS_RCC} - ${STATUS_RESOURCES_QRC} - ${STATUS_QRC} - ) - -target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/src-cpp-structure/projects/ServicesTest/src/AccountsService.cpp b/src-cpp-structure/projects/ServicesTest/src/AccountsService.cpp deleted file mode 100644 index cb90505b1..000000000 --- a/src-cpp-structure/projects/ServicesTest/src/AccountsService.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include - -#include "Utils.h" -#include "StatusTestEnvironment.h" - -#include -#include - -using namespace Status::Test; - -class AccountsServices : public ::testing::Test { - -public: - static std::shared_ptr AccountsServiceInst; - -protected: - - static void SetUpTestSuite() - { - init(); - } - - static void init() { - AccountsServices::AccountsServiceInst = std::make_shared(); - if(!AccountsServices::initialized) - { - AccountsServices::AccountsServiceInst->init(Utils::statusGoDataDir()); - initialized = true; - } - } - - static bool initialized; -}; - -bool AccountsServices::initialized = false; -std::shared_ptr AccountsServices::AccountsServiceInst = nullptr; - -TEST_F(AccountsServices, InitService) -{ - EXPECT_CALL(*StatusTestEnvironment::AccountsServiceMockInst, init(Utils::statusGoDataDir())).Times(1); - - StatusTestEnvironment::AccountsServiceMockInst->init(Utils::statusGoDataDir()); - - EXPECT_TRUE(::testing::Mock::VerifyAndClearExpectations(StatusTestEnvironment::AccountsServiceMockInst.get())); -} - -TEST_F(AccountsServices, GeneratedAccounts) -{ - EXPECT_CALL(::testing::Const(*StatusTestEnvironment::AccountsServiceMockInst), generatedAccounts()).WillRepeatedly( - ::testing::Invoke(AccountsServices::AccountsServiceInst.get(), &Status::Accounts::Service::generatedAccounts)); - - auto genAccounts = StatusTestEnvironment::AccountsServiceMockInst->generatedAccounts(); - - ASSERT_EQ(5, genAccounts.size()); - - for(const auto& acc : genAccounts) - { - ASSERT_STRNE(qUtf8Printable(acc.id), ""); - ASSERT_STRNE(qUtf8Printable(acc.publicKey), ""); - ASSERT_STRNE(qUtf8Printable(acc.address), ""); - ASSERT_STRNE(qUtf8Printable(acc.keyUid), ""); - } -} - -TEST_F(AccountsServices, DISABLED_GenerateAlias) // temporary disabled till we see what's happening on the status-go side since it doesn't return aliases for any pk -{ - QString testPubKey = "0x04487f44bac3e90825bfa9720148308cb64835bebb7e888f519cebc127223187067629f8b70d0661a35d4af6516b225286"; - - QString alias; - EXPECT_CALL(*StatusTestEnvironment::AccountsServiceMockInst, generateAlias(testPubKey)).WillRepeatedly(::testing::ReturnPointee(&alias)); - - ASSERT_STRNE(qUtf8Printable(alias), ""); - ASSERT_EQ(alias, AccountsServices::AccountsServiceInst->generateAlias(testPubKey)); -} diff --git a/src-cpp-structure/projects/ServicesTest/src/Constants.h b/src-cpp-structure/projects/ServicesTest/src/Constants.h deleted file mode 100644 index b4543a384..000000000 --- a/src-cpp-structure/projects/ServicesTest/src/Constants.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -const QString UserDataDirName = "StatusTest"; -const QString StatusGoDataDirName = "data"; -const QString TmpDataDirName = "tmp"; -const QString LogsDataDirName = "logs"; -const QString QtDataDirName = "qt"; -const QString KeystoreDataDirName = "keystore"; -const QString GlobalSettingsFileName = "global"; diff --git a/src-cpp-structure/projects/ServicesTest/src/StatusTestEnvironment.cpp b/src-cpp-structure/projects/ServicesTest/src/StatusTestEnvironment.cpp deleted file mode 100644 index 5f3f26799..000000000 --- a/src-cpp-structure/projects/ServicesTest/src/StatusTestEnvironment.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "StatusTestEnvironment.h" - -using namespace Status::Test; - -std::shared_ptr StatusTestEnvironment::AccountsServiceMockInst = nullptr; - - StatusTestEnvironment::~StatusTestEnvironment() -{ -} - -void StatusTestEnvironment::SetUp() -{ - init(); -} - -void StatusTestEnvironment::TearDown() -{ -} - -void StatusTestEnvironment::init() { - StatusTestEnvironment::AccountsServiceMockInst = std::make_shared(); -} - - diff --git a/src-cpp-structure/projects/ServicesTest/src/StatusTestEnvironment.h b/src-cpp-structure/projects/ServicesTest/src/StatusTestEnvironment.h deleted file mode 100644 index 34f845819..000000000 --- a/src-cpp-structure/projects/ServicesTest/src/StatusTestEnvironment.h +++ /dev/null @@ -1,21 +0,0 @@ -#include - -#include "Utils.h" - -#include - -using namespace Status::Test; - -class StatusTestEnvironment : public ::testing::Environment { -public: - ~StatusTestEnvironment() override; - - void SetUp() override; - - void TearDown() override; - - static std::shared_ptr AccountsServiceMockInst; - -protected: - void init(); -}; diff --git a/src-cpp-structure/projects/ServicesTest/src/Utils.h b/src-cpp-structure/projects/ServicesTest/src/Utils.h deleted file mode 100644 index 8c493ed67..000000000 --- a/src-cpp-structure/projects/ServicesTest/src/Utils.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "Constants.h" - -#include - -class Utils final { -public: - - static QString defaultDataDir() - { - auto d = QDir(); - if(STATUS_DEVELOPMENT){ - d = QDir(STATUS_SOURCE_DIR); - d.cdUp(); - } - else { - // We should handle paths for different platforms here in case of non development - } - - return d.absolutePath() + QDir::separator() + UserDataDirName; - } - - static QString statusGoDataDir() - { - return defaultDataDir() + QDir::separator() + StatusGoDataDirName; - } - - static QString keystoreDataDir() - { - return statusGoDataDir() + QDir::separator() + KeystoreDataDirName; - } - - static QString tmpDataDir() - { - return defaultDataDir() + QDir::separator() + TmpDataDirName; - } - - static QString logsDataDir() - { - return defaultDataDir() + QDir::separator() + LogsDataDirName; - } - - static QString qtDataDir() - { - return defaultDataDir() + QDir::separator() + QtDataDirName; - } - - static void ensureDirectories() - { - QDir d; - d.mkpath(defaultDataDir()); - d.mkpath(statusGoDataDir()); - d.mkpath(keystoreDataDir()); - d.mkpath(tmpDataDir()); - d.mkpath(logsDataDir()); - d.mkpath(qtDataDir()); - } -}; diff --git a/src-cpp-structure/projects/ServicesTest/src/main.cpp b/src-cpp-structure/projects/ServicesTest/src/main.cpp deleted file mode 100644 index b9fdbce19..000000000 --- a/src-cpp-structure/projects/ServicesTest/src/main.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - -#include "Utils.h" -#include "StatusTestEnvironment.h" - -#include - -int main(int argc, char *argv[]) -{ - Utils::ensureDirectories(); - - ::testing::InitGoogleTest(&argc, argv); - - StatusTestEnvironment* const env = new StatusTestEnvironment(); - if (::testing::AddGlobalTestEnvironment(env) != env) { - qWarning() << "FAILED: AddGlobalTestEnvironment() should return its argument\n"; - ::testing::internal::posix::Abort(); - } - - return RUN_ALL_TESTS(); -} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e69de29bb..f7921bdf6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(libs) \ No newline at end of file diff --git a/test/libs/CMakeLists.txt b/test/libs/CMakeLists.txt new file mode 100644 index 000000000..fc7b11f4c --- /dev/null +++ b/test/libs/CMakeLists.txt @@ -0,0 +1,3 @@ +# Libs integration tests +# +add_subdirectory(StatusGoQt) diff --git a/test/libs/StatusGoQt/CMakeLists.txt b/test/libs/StatusGoQt/CMakeLists.txt new file mode 100644 index 000000000..8b1b3064d --- /dev/null +++ b/test/libs/StatusGoQt/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.21) + +project(TestStatusGoQt VERSION 0.1.0 LANGUAGES CXX) + +set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true) + +find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Core REQUIRED) +qt6_standard_project_setup() + +find_package(GTest REQUIRED) + +enable_testing() + +add_executable(${PROJECT_NAME} + test_accounts.cpp + test_messaging.cpp +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + Qt6::Core + + GTest::gtest + GTest::gmock + GTest::gtest_main + + Status::TestHelpers + Status::OnboardingTestHelpers + + Status::Onboarding + + Status::StatusGoQt +) + +include(GoogleTest) +gtest_add_tests( + TARGET ${PROJECT_NAME} +WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} +) diff --git a/test/libs/StatusGoQt/test_accounts.cpp b/test/libs/StatusGoQt/test_accounts.cpp new file mode 100644 index 000000000..c3789c8dc --- /dev/null +++ b/test/libs/StatusGoQt/test_accounts.cpp @@ -0,0 +1,34 @@ +#include + +#include + +#include + +namespace Accounts = Status::StatusGo::Accounts; +namespace StatusGo = Status::StatusGo; + +namespace fs = std::filesystem; + +namespace Status::Testing { + +TEST(Onboarding, TestOpenAccountsNoDataFails) { + AutoCleanTempTestDir fusedTestFolder{test_info_->name()}; + + auto response = Accounts::openAccounts(fusedTestFolder.tempFolder().c_str()); + EXPECT_FALSE(response.containsError()); + EXPECT_EQ(response.result.count(), 0); +} + +TEST(Onboarding, TestOpenAccountsNoDataDoesNotCreateFiles) { + AutoCleanTempTestDir fusedTestFolder{test_info_->name()}; + + auto response = Accounts::openAccounts(fusedTestFolder.tempFolder().c_str()); + EXPECT_FALSE(response.containsError()); + + int fileCount = 0; + for (const auto & file : fs::directory_iterator(fusedTestFolder.tempFolder())) + fileCount++; + EXPECT_EQ(fileCount, 0); +} + +} diff --git a/test/libs/StatusGoQt/test_messaging.cpp b/test/libs/StatusGoQt/test_messaging.cpp new file mode 100644 index 000000000..8927605c7 --- /dev/null +++ b/test/libs/StatusGoQt/test_messaging.cpp @@ -0,0 +1,42 @@ +#include + +#include + +#include + +#include + +#include +#include + + +namespace fs = std::filesystem; + +namespace Status::Testing { + +/// This is an integration test to check that status-go doesn't crash on apple silicon when starting Me +/// \warning the test depends on IO and it is not deterministic, fast, focused or reliable. It is here for validation only +/// \todo fin a way to test the integration within a test environment. Also how about reusing an existing account +TEST(OnboardingModule, TestStartMessaging) +{ + bool nodeReady = false; + QObject::connect(StatusGo::SignalsManager::instance(), &StatusGo::SignalsManager::nodeReady, [&nodeReady](const QString& error) { + if(error.isEmpty()) { + if(nodeReady) { + nodeReady = false; + } else + nodeReady = true; + } + }); + + ScopedTestAccount testAccount(test_info_->name()); + + ASSERT_TRUE(StatusGo::Messenger::startMessenger()); + + testAccount.processMessages(1000, [nodeReady]() { + return !nodeReady; + }); + ASSERT_TRUE(nodeReady); +} + +} // namespace diff --git a/ui/generate-rcc.go b/ui/generate-rcc.go index f33813b77..c4a1a9826 100644 --- a/ui/generate-rcc.go +++ b/ui/generate-rcc.go @@ -52,10 +52,6 @@ func main() { if err != nil { return err } - - // clean relative path - *sourceDirName, _ = filepath.Abs(*sourceDirName) - if !info.IsDir() { ext := filepath.Ext(path) base := filepath.Base(path) diff --git a/ui/main.qml b/ui/main.qml index 9598a368f..131d0d6bf 100644 --- a/ui/main.qml +++ b/ui/main.qml @@ -174,10 +174,7 @@ StatusWindow { } Connections { - // This handling should be part of backend code, but because of compatibility with the current Nim App - // since c++ and Nim app are sharing the same/identical qml code we are still not allowed to remove this - // completely, and that's why we have this `target` set to null in case of c++ app. - target: !Constants.isCppApp? singleInstance : null + target: singleInstance onSecondInstanceDetected: { console.log("User attempted to run the second instance of the application") diff --git a/vendor/conan-configs/linux.ini b/vendor/conan-configs/linux.ini index ea09335c9..d0c1b9113 100644 --- a/vendor/conan-configs/linux.ini +++ b/vendor/conan-configs/linux.ini @@ -7,5 +7,3 @@ os=Linux build_type=Release [env] -CC=/usr/bin/gcc-10 -CXX=/usr/bin/g++-10 \ No newline at end of file