diff --git a/.gitignore b/.gitignore
index 716b8fd1d1..9fb72ceb94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,3 +90,9 @@ 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/src-cpp-structure/CMakeLists.txt b/src-cpp-structure/CMakeLists.txt
new file mode 100644
index 0000000000..c328671d21
--- /dev/null
+++ b/src-cpp-structure/CMakeLists.txt
@@ -0,0 +1,27 @@
+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/cmake/app-linux.cmake b/src-cpp-structure/cmake/app-linux.cmake
new file mode 100644
index 0000000000..982302420a
--- /dev/null
+++ b/src-cpp-structure/cmake/app-linux.cmake
@@ -0,0 +1,22 @@
+add_executable(${PROJECT_NAME})
+
+target_link_libraries(
+ ${PROJECT_NAME}
+ Qt5::Core
+ Qt5::Gui
+ Qt5::Widgets
+ Qt5::Quick
+ Qt5::Qml
+ Qt5::Quick
+ Qt5::QuickControls2
+ Qt5::QuickTemplates2
+ Qt5::Multimedia
+ )
+
+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
new file mode 100644
index 0000000000..a1eb707116
--- /dev/null
+++ b/src-cpp-structure/cmake/app-mac.cmake
@@ -0,0 +1,30 @@
+add_executable(${PROJECT_NAME} MACOSX_BUNDLE)
+
+find_library(FOUNDATION_FRAMEWORK Foundation)
+find_library(IO_KIT_FRAMEWORK IOKit)
+
+target_link_libraries(
+ ${PROJECT_NAME}
+ 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
+ )
+
+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
new file mode 100644
index 0000000000..1b92ff1073
--- /dev/null
+++ b/src-cpp-structure/cmake/app-win.cmake
@@ -0,0 +1,22 @@
+add_executable(${PROJECT_NAME} WIN32)
+
+target_link_libraries(
+ ${PROJECT_NAME}
+ Qt5::Core
+ Qt5::Gui
+ Qt5::Widgets
+ Qt5::Quick
+ Qt5::Qml
+ Qt5::Quick
+ Qt5::QuickControls2
+ Qt5::QuickTemplates2
+ Qt5::Multimedia
+ )
+
+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
new file mode 100644
index 0000000000..8ad2539de0
--- /dev/null
+++ b/src-cpp-structure/cmake/conan.cmake
@@ -0,0 +1,27 @@
+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
new file mode 100644
index 0000000000..45428d0235
--- /dev/null
+++ b/src-cpp-structure/cmake/project-config.cmake
@@ -0,0 +1,16 @@
+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()
+
+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
new file mode 100644
index 0000000000..784ecfba54
--- /dev/null
+++ b/src-cpp-structure/cmake/services-linux.cmake
@@ -0,0 +1,10 @@
+target_link_libraries(
+ ${PROJECT_NAME}
+ 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
new file mode 100644
index 0000000000..04c570fe52
--- /dev/null
+++ b/src-cpp-structure/cmake/services-mac.cmake
@@ -0,0 +1,22 @@
+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}
+ 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
new file mode 100644
index 0000000000..784ecfba54
--- /dev/null
+++ b/src-cpp-structure/cmake/services-win.cmake
@@ -0,0 +1,10 @@
+target_link_libraries(
+ ${PROJECT_NAME}
+ 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
new file mode 100644
index 0000000000..0c8ccbf59b
--- /dev/null
+++ b/src-cpp-structure/cmake/translation.cmake
@@ -0,0 +1,36 @@
+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
new file mode 100644
index 0000000000..2aea5b36dc
--- /dev/null
+++ b/src-cpp-structure/conan-debug-profile
@@ -0,0 +1,4 @@
+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
new file mode 100644
index 0000000000..e5cd0fbd07
--- /dev/null
+++ b/src-cpp-structure/conan-release-profile
@@ -0,0 +1,4 @@
+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
new file mode 100644
index 0000000000..9e555dd450
--- /dev/null
+++ b/src-cpp-structure/conanfile.py
@@ -0,0 +1,10 @@
+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
new file mode 100644
index 0000000000..7efcfee094
--- /dev/null
+++ b/src-cpp-structure/projects/App/Boot/AppController.cpp
@@ -0,0 +1,100 @@
+#include "AppController.h"
+
+#include "DI.h"
+#include "../Core/Engine.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 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;
+
+// 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");
+
+ 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 rootModule = Injector.create()();
+ rootModule->load();
+
+ registerResources();
+
+ QString qmlFile = QStringLiteral("qrc:/main.qml");
+ Engine::create(qmlFile);
+ QObject::connect(Engine::instance(), &Engine::objectCreated, &app,
+ [url = qmlFile](QObject* obj, const QUrl& objUrl) {
+ if(!obj && url == objUrl.toString())
+ {
+ auto err = "Failed to create: " + url;
+ throw std::runtime_error(err.toStdString());
+ }
+ });
+
+ 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
new file mode 100644
index 0000000000..d02daf72d6
--- /dev/null
+++ b/src-cpp-structure/projects/App/Boot/AppController.h
@@ -0,0 +1,12 @@
+#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
new file mode 100644
index 0000000000..070f3cc2f9
--- /dev/null
+++ b/src-cpp-structure/projects/App/Boot/AppWindow.cpp
@@ -0,0 +1,50 @@
+#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::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
new file mode 100644
index 0000000000..f81493dee7
--- /dev/null
+++ b/src-cpp-structure/projects/App/Boot/AppWindow.h
@@ -0,0 +1,49 @@
+#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;
+
+ 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
new file mode 100644
index 0000000000..d606d08963
--- /dev/null
+++ b/src-cpp-structure/projects/App/Boot/AppWindow.mm
@@ -0,0 +1,37 @@
+#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
new file mode 100644
index 0000000000..0597dad61d
--- /dev/null
+++ b/src-cpp-structure/projects/App/Boot/DI.h
@@ -0,0 +1,18 @@
+#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
new file mode 100644
index 0000000000..dcaf640088
--- /dev/null
+++ b/src-cpp-structure/projects/App/Boot/main.cpp
@@ -0,0 +1,6 @@
+#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
new file mode 100644
index 0000000000..fe737c0f13
--- /dev/null
+++ b/src-cpp-structure/projects/App/CMakeLists.txt
@@ -0,0 +1,37 @@
+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()
+
+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
new file mode 100644
index 0000000000..23260405c2
--- /dev/null
+++ b/src-cpp-structure/projects/App/Common/Constants.h
@@ -0,0 +1,14 @@
+#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
new file mode 100644
index 0000000000..3515cef99b
--- /dev/null
+++ b/src-cpp-structure/projects/App/Common/Utils.h
@@ -0,0 +1,67 @@
+#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
new file mode 100644
index 0000000000..7198dda046
--- /dev/null
+++ b/src-cpp-structure/projects/App/Core/Engine.cpp
@@ -0,0 +1,60 @@
+#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
new file mode 100644
index 0000000000..e077b605d3
--- /dev/null
+++ b/src-cpp-structure/projects/App/Core/Engine.h
@@ -0,0 +1,25 @@
+#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
new file mode 100644
index 0000000000..5d9d467faf
--- /dev/null
+++ b/src-cpp-structure/projects/App/Core/GlobalEvents.cpp
@@ -0,0 +1,18 @@
+#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
new file mode 100644
index 0000000000..fce4755ab3
--- /dev/null
+++ b/src-cpp-structure/projects/App/Core/GlobalEvents.h
@@ -0,0 +1,26 @@
+#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
new file mode 100644
index 0000000000..3b8876e136
--- /dev/null
+++ b/src-cpp-structure/projects/App/Core/SignalsManager.cpp
@@ -0,0 +1,96 @@
+#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
new file mode 100644
index 0000000000..739da91006
--- /dev/null
+++ b/src-cpp-structure/projects/App/Core/SignalsManager.h
@@ -0,0 +1,36 @@
+#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/Global/LocalAccountSettings.cpp b/src-cpp-structure/projects/App/Global/LocalAccountSettings.cpp
new file mode 100644
index 0000000000..08f15334c6
--- /dev/null
+++ b/src-cpp-structure/projects/App/Global/LocalAccountSettings.cpp
@@ -0,0 +1,47 @@
+#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
new file mode 100644
index 0000000000..7198bebfd7
--- /dev/null
+++ b/src-cpp-structure/projects/App/Global/LocalAccountSettings.h
@@ -0,0 +1,45 @@
+#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
new file mode 100644
index 0000000000..6870aa402a
--- /dev/null
+++ b/src-cpp-structure/projects/App/Global/LocalAppSettings.cpp
@@ -0,0 +1,50 @@
+#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
new file mode 100644
index 0000000000..8a6ba6f8af
--- /dev/null
+++ b/src-cpp-structure/projects/App/Global/LocalAppSettings.h
@@ -0,0 +1,42 @@
+#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
new file mode 100644
index 0000000000..60883a33e2
--- /dev/null
+++ b/src-cpp-structure/projects/App/Global/SettingsProperties.h
@@ -0,0 +1,45 @@
+#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
new file mode 100644
index 0000000000..fca1b9304f
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Module.cpp
@@ -0,0 +1,26 @@
+#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
new file mode 100644
index 0000000000..8bb50bd701
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Module.h
@@ -0,0 +1,27 @@
+#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
new file mode 100644
index 0000000000..94d7924506
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/ModuleBuilder.cpp
@@ -0,0 +1,15 @@
+#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
new file mode 100644
index 0000000000..aec9be51c5
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/ModuleBuilder.h
@@ -0,0 +1,21 @@
+#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
new file mode 100644
index 0000000000..a65c2b61fe
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/ModuleInterface.h
@@ -0,0 +1,12 @@
+#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
new file mode 100644
index 0000000000..d92411ee2d
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.cpp
@@ -0,0 +1,151 @@
+#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
new file mode 100644
index 0000000000..bc9ef6c390
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/SharedModels/SectionItem.h
@@ -0,0 +1,113 @@
+#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
new file mode 100644
index 0000000000..5af9cdaad1
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.cpp
@@ -0,0 +1,123 @@
+#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
new file mode 100644
index 0000000000..f7af8e73a7
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/SharedModels/SectionModel.h
@@ -0,0 +1,55 @@
+#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
new file mode 100644
index 0000000000..cbc0a4f301
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Controller.cpp
@@ -0,0 +1,67 @@
+#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
new file mode 100644
index 0000000000..bc5d91bf7e
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Controller.h
@@ -0,0 +1,31 @@
+#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
new file mode 100644
index 0000000000..e6be9ddd38
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/ControllerInterface.h
@@ -0,0 +1,22 @@
+#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
new file mode 100644
index 0000000000..0212db4f8e
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/Controller.cpp
@@ -0,0 +1,98 @@
+#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
new file mode 100644
index 0000000000..18bce5c66c
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/Controller.h
@@ -0,0 +1,42 @@
+#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
new file mode 100644
index 0000000000..810238d77a
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/ControllerInterface.h
@@ -0,0 +1,28 @@
+#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
new file mode 100644
index 0000000000..ad582c1c4c
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/Item.cpp
@@ -0,0 +1,34 @@
+#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
new file mode 100644
index 0000000000..7d6ead8d77
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/Item.h
@@ -0,0 +1,26 @@
+#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
new file mode 100644
index 0000000000..e8672eaf57
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/Model.cpp
@@ -0,0 +1,73 @@
+#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
new file mode 100644
index 0000000000..bcc42668b0
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/Model.h
@@ -0,0 +1,31 @@
+#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
new file mode 100644
index 0000000000..c0ca48646e
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/Module.cpp
@@ -0,0 +1,96 @@
+#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
new file mode 100644
index 0000000000..0cd8f4cc2f
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/Module.h
@@ -0,0 +1,44 @@
+#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
new file mode 100644
index 0000000000..316c66b398
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.cpp
@@ -0,0 +1,27 @@
+#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
new file mode 100644
index 0000000000..8a146a5ef3
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleBuilder.h
@@ -0,0 +1,24 @@
+#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
new file mode 100644
index 0000000000..54b385bba8
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/ModuleInterface.h
@@ -0,0 +1,19 @@
+#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
new file mode 100644
index 0000000000..21420e3bfe
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.cpp
@@ -0,0 +1,38 @@
+#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
new file mode 100644
index 0000000000..d46445313c
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/SelectedAccount.h
@@ -0,0 +1,33 @@
+#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
new file mode 100644
index 0000000000..a0ddda722b
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/View.cpp
@@ -0,0 +1,77 @@
+#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
new file mode 100644
index 0000000000..a7526dd121
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/View.h
@@ -0,0 +1,47 @@
+#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
new file mode 100644
index 0000000000..56fa16711e
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Login/ViewInterface.h
@@ -0,0 +1,34 @@
+#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
new file mode 100644
index 0000000000..01e5e43a84
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Module.cpp
@@ -0,0 +1,93 @@
+#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
new file mode 100644
index 0000000000..0ca835a974
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Module.h
@@ -0,0 +1,63 @@
+#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
new file mode 100644
index 0000000000..53722bcbee
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.cpp
@@ -0,0 +1,30 @@
+#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
new file mode 100644
index 0000000000..42214a889e
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/ModuleBuilder.h
@@ -0,0 +1,27 @@
+#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
new file mode 100644
index 0000000000..2293548aa8
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/ModuleInterface.h
@@ -0,0 +1,21 @@
+#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
new file mode 100644
index 0000000000..af89f8436f
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.cpp
@@ -0,0 +1,72 @@
+#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
new file mode 100644
index 0000000000..1e7c8b1d4a
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Controller.h
@@ -0,0 +1,33 @@
+#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
new file mode 100644
index 0000000000..201b33c2d3
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ControllerInterface.h
@@ -0,0 +1,30 @@
+#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
new file mode 100644
index 0000000000..7ba2ed7357
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.cpp
@@ -0,0 +1,38 @@
+#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
new file mode 100644
index 0000000000..75b8b42ea8
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Item.h
@@ -0,0 +1,26 @@
+#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
new file mode 100644
index 0000000000..31a722df7f
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.cpp
@@ -0,0 +1,63 @@
+#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
new file mode 100644
index 0000000000..3e8ee92cdd
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Model.h
@@ -0,0 +1,30 @@
+#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
new file mode 100644
index 0000000000..e932a1cc62
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.cpp
@@ -0,0 +1,83 @@
+#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
new file mode 100644
index 0000000000..f00441be51
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/Module.h
@@ -0,0 +1,46 @@
+#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
new file mode 100644
index 0000000000..a0dce8143e
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.cpp
@@ -0,0 +1,25 @@
+#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
new file mode 100644
index 0000000000..920f6783cb
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleBuilder.h
@@ -0,0 +1,21 @@
+#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
new file mode 100644
index 0000000000..06194d3697
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ModuleInterface.h
@@ -0,0 +1,19 @@
+#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
new file mode 100644
index 0000000000..66fe51b37c
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.cpp
@@ -0,0 +1,90 @@
+#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
new file mode 100644
index 0000000000..07319aae1a
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/View.h
@@ -0,0 +1,49 @@
+#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
new file mode 100644
index 0000000000..7e7b4d6e40
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/Onboarding/ViewInterface.h
@@ -0,0 +1,35 @@
+#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
new file mode 100644
index 0000000000..cca04b4b5f
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/View.cpp
@@ -0,0 +1,52 @@
+#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
new file mode 100644
index 0000000000..088e0126a1
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/View.h
@@ -0,0 +1,37 @@
+#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
new file mode 100644
index 0000000000..ec1d898960
--- /dev/null
+++ b/src-cpp-structure/projects/App/Modules/Startup/ViewInterface.h
@@ -0,0 +1,32 @@
+#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
new file mode 100644
index 0000000000..6de5ed1fd2
--- /dev/null
+++ b/src-cpp-structure/projects/Backend/CMakeLists.txt
@@ -0,0 +1,25 @@
+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)
+
+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/Accounts.h b/src-cpp-structure/projects/Backend/include/StatusBackend/Accounts.h
new file mode 100644
index 0000000000..ab2e2567d4
--- /dev/null
+++ b/src-cpp-structure/projects/Backend/include/StatusBackend/Accounts.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "Types.h"
+
+#include
+
+namespace Backend::Accounts
+{
+ RpcResponse generateAddresses(const QVector& paths);
+
+ RpcResponse generateIdenticon(const QString& publicKey);
+
+ RpcResponse generateAlias(const QString& publicKey);
+
+ RpcResponse storeDerivedAccounts(const QString& accountId, const QString& hashedPassword,
+ const QVector& paths);
+
+ RpcResponse saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account,
+ const QJsonArray& subaccounts, const QJsonObject& settings,
+ const QJsonObject& nodeConfig);
+
+ RpcResponse openAccounts(const QString& path);
+
+ RpcResponse login(const QString& name, const QString& keyUid, const QString& hashedPassword,
+ const QString& identicon, const QString& thumbnail, const QString& large);
+}
diff --git a/src-cpp-structure/projects/Backend/include/StatusBackend/Types.h b/src-cpp-structure/projects/Backend/include/StatusBackend/Types.h
new file mode 100644
index 0000000000..a4ef6f8b92
--- /dev/null
+++ b/src-cpp-structure/projects/Backend/include/StatusBackend/Types.h
@@ -0,0 +1,37 @@
+#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
new file mode 100644
index 0000000000..7ce2bb7180
--- /dev/null
+++ b/src-cpp-structure/projects/Backend/include/StatusBackend/Utils.h
@@ -0,0 +1,125 @@
+#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
new file mode 100644
index 0000000000..28b7d81ef6
--- /dev/null
+++ b/src-cpp-structure/projects/Backend/include/StatusBackend/WalletAccounts.h
@@ -0,0 +1,24 @@
+#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