feat: base_cpp

This commit is contained in:
Richard Ramos 2022-01-06 15:29:19 -04:00
parent ad7f48d6ac
commit 4183069a9f
104 changed files with 5142 additions and 190 deletions

128
.clang-format Normal file
View File

@ -0,0 +1,128 @@
# Checkout config tool: https://zed0.co.uk/clang-format-configurator/
# Or http://cf.monofraps.net/
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
# https://github.com/01org/parameter-framework/blob/master/.clang-format
# Tested on: clang-format version 6.0.1
# Common settings
BasedOnStyle: WebKit
TabWidth: 4
IndentWidth: 4
UseTab: Always
ColumnLimit: 120
# Other languages JavaScript, Proto
---
Language: Cpp
# http://releases.llvm.org/6.0.1/tools/clang/docs/ClangFormatStyleOptions.html#disabling-formatting-on-a-piece-of-code
# int formatted_code;
# // clang-format off
# void unformatted_code ;
# // clang-format on
# void formatted_code_again;
DisableFormat: false
Standard: Cpp11
AccessModifierOffset: -4
AlignAfterOpenBracket: true
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
# Configure each individual brace in BraceWrapping
BreakBeforeBraces: Custom
# Control of individual brace wrapping cases
BraceWrapping: {
AfterClass: 'true'
AfterControlStatement: 'true'
AfterEnum : 'true'
AfterFunction : 'true'
AfterNamespace : 'true'
AfterStruct : 'true'
AfterUnion : 'true'
BeforeCatch : 'true'
BeforeElse : 'true'
IndentBraces : 'false'
AfterExternBlock : 'true'
SplitEmptyFunction : 'false'
SplitEmptyRecord : 'false'
SplitEmptyNamespace : 'true'
}
BreakAfterJavaFieldAnnotations: true
BreakBeforeInheritanceComma: false
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BreakStringLiterals: true
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
SpaceBeforeCpp11BracedList: false
DerivePointerAlignment: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IndentCaseLabels: false
FixNamespaceComments: true
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
JavaScriptQuotes: Double
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: Never
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceAfterTemplateKeyword: true
SpaceBeforeInheritanceColon: true
SortUsingDeclarations: true
SortIncludes: true
# Comments are for developers, they should arrange them
ReflowComments: false
IncludeBlocks: Preserve
IndentPPDirectives: AfterHash

55
.gitignore vendored
View File

@ -31,4 +31,57 @@ status-react-translations/
/.update.timestamp
notarization.log
status-desktop.log
nim_status_client.log
nim_status_client.log
# CPP app =====================================================================
# https://github.com/github/gitignore/blob/master/CMake.gitignore
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
# https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

135
CMakeLists.txt Normal file
View File

@ -0,0 +1,135 @@
cmake_minimum_required(VERSION 3.14)
project(status-desktop
VERSION 0.1.0
LANGUAGES CXX)
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(
FATAL_ERROR
"In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there."
)
endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
message(STATUS "Started CMake for ${PROJECT_NAME} v${PROJECT_VERSION}...\n")
include(ExternalProject)
# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check https://doc.qt.io/qt/deployment-android.html for more information.
# They need to be set before the find_package(...) calls below.
#if(ANDROID)
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
# if (ANDROID_ABI STREQUAL "armeabi-v7a")
# set(ANDROID_EXTRA_LIBS
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
# endif()
#endif()
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Quick Widgets Concurrent REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Quick Widgets Concurrent REQUIRED)
set(PROJECT_SOURCES
src-cpp/main.cpp
src-cpp/constants.cpp
src-cpp/logs.cpp
resources.rcc
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(status-desktop
${PROJECT_SOURCES}
)
else()
if(ANDROID)
add_library(status-desktop SHARED
${PROJECT_SOURCES}
)
else()
add_executable(status-desktop
${PROJECT_SOURCES}
)
endif()
endif()
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/vendor/status-lib/vendor/status-go bin/status-go)
# The following are dependencies that do not include CMakeList.txt files
# Create a PR in those projects so we can include them just like it was
# done with status-go
include(cmake/QRCodeGen.cmake)
include(cmake/Keycard.cmake)
# Submodiles
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src-cpp/dotherside)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src-cpp/app)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src-cpp/app_service)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src-cpp/backend)
target_include_directories(status-desktop PUBLIC include)
target_compile_definitions(status-desktop
PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(status-desktop
PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::Widgets
DOtherSide
statusgo_shared # <- Link status-go with this target (remove `_shared` for static linking)
qrcodegen
keycard
app_service
app
)
add_custom_target(rcc ALL DEPENDS resources.rcc)
add_custom_command(
OUTPUT resources.rcc
COMMENT "Building resources.rcc"
COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_CURRENT_SOURCE_DIR}/resources.rcc
COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_CURRENT_SOURCE_DIR}/ui/resources.qrc
COMMAND go run ${CMAKE_CURRENT_SOURCE_DIR}/ui/generate-rcc.go -source=${CMAKE_CURRENT_SOURCE_DIR}/ui -output=${CMAKE_CURRENT_SOURCE_DIR}/ui/resources.qrc
COMMAND rcc -binary $<$<CONFIG:Debug>:--no-compress> ${CMAKE_CURRENT_SOURCE_DIR}/ui/resources.qrc ${CMAKE_CURRENT_SOURCE_DIR}/resources/resources.qrc -o ${CMAKE_CURRENT_SOURCE_DIR}/resources.rcc
VERBATIM
USES_TERMINAL
)
# run
add_custom_target(run
COMMENT "Execute desktop"
DEPENDS status-desktop
COMMAND ./status-desktop
VERBATIM
USES_TERMINAL
)
# run debug
add_custom_target(run-debug
COMMENT "Execute desktop with debugger on port 1234"
DEPENDS status-desktop
COMMAND ./status-desktop -qmljsdebugger=port:1234
VERBATIM
USES_TERMINAL
)

View File

@ -6,3 +6,15 @@ Desktop client for the [Status Network](https://statusnetwork.com/) built with [
Dev Docs: [https://hackmd.io/@status-desktop/B1naRjxh_/https%3A%2F%2Fhackmd.io%2F%40status-desktop%2FB1eOaf-nd](https://hackmd.io/@status-desktop/B1naRjxh_/https%3A%2F%2Fhackmd.io%2F%40status-desktop%2FB1eOaf-nd)
# CPP app
```
cd build
cmake ..
make
./test-qtapp
```

20
cmake/Keycard.cmake Normal file
View File

@ -0,0 +1,20 @@
# Keycard
# TODO: create a PR in that project to build it like we do with status-go ^
set(KEYCARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/vendor/status-lib/vendor/nim-keycard-go)
set(KEYCARD_LIB_DIR ${KEYCARD_ROOT}/go/keycard/build/libkeycard)
ExternalProject_Add(libkeycard
PREFIX ${KEYCARD_ROOT}
SOURCE_DIR ${KEYCARD_ROOT}
UPDATE_COMMAND ""
PATCH_COMMAND ""
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
BUILD_IN_SOURCE 1
BUILD_COMMAND make build-keycard-go V=1
BUILD_BYPRODUCTS ${KEYCARD_LIB_DIR}/libkeycard${CMAKE_SHARED_LIBRARY_SUFFIX}
)
ExternalProject_Get_Property(libkeycard SOURCE_DIR)
add_library(keycard SHARED IMPORTED)
set_property(TARGET keycard PROPERTY IMPORTED_LOCATION ${KEYCARD_LIB_DIR}/libkeycard${CMAKE_SHARED_LIBRARY_SUFFIX})
add_dependencies(keycard libkeycard)
include_directories(${KEYCARD_LIB_DIR})

20
cmake/QRCodeGen.cmake Normal file
View File

@ -0,0 +1,20 @@
# QR-Code-generator
# TODO: create a PR in that project to build it like we do with status-go ^
set(QRCODE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/vendor/QR-Code-generator)
set(QRCODE_LIB_DIR ${QRCODE_ROOT}/cpp)
ExternalProject_Add(libqrcodegen
PREFIX ${QRCODE_ROOT}
SOURCE_DIR ${QRCODE_ROOT}
UPDATE_COMMAND ""
PATCH_COMMAND ""
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
BUILD_IN_SOURCE 1
BUILD_COMMAND make -C cpp libqrcodegen.a V=1
BUILD_BYPRODUCTS ${QRCODE_LIB_DIR}/libqrcodegen.a
)
ExternalProject_Get_Property(libqrcodegen SOURCE_DIR)
add_library(qrcodegen STATIC IMPORTED)
set_property(TARGET qrcodegen PROPERTY IMPORTED_LOCATION ${QRCODE_LIB_DIR}/libqrcodegen.a)
add_dependencies(qrcodegen libqrcodegen)
include_directories(${QRCODE_LIB_DIR})

View File

@ -1,53 +0,0 @@
# https://github.com/github/gitignore/blob/master/CMake.gitignore
# ==============================================================================
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
# https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore
# ==============================================================================
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

View File

@ -1,63 +0,0 @@
cmake_minimum_required(VERSION 3.14)
project(test-qtapp LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(ExternalProject)
# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check https://doc.qt.io/qt/deployment-android.html for more information.
# They need to be set before the find_package(...) calls below.
#if(ANDROID)
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
# if (ANDROID_ABI STREQUAL "armeabi-v7a")
# set(ANDROID_EXTRA_LIBS
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
# endif()
#endif()
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Quick REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Quick REQUIRED)
set(PROJECT_SOURCES
main.cpp
qml.qrc
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(test-qtapp
${PROJECT_SOURCES}
)
else()
if(ANDROID)
add_library(test-qtapp SHARED
${PROJECT_SOURCES}
)
else()
add_executable(test-qtapp
${PROJECT_SOURCES}
)
endif()
endif()
# status-go has a CMakeLists.txt file we can use
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../vendor/status-lib/vendor/status-go bin/status-go)
target_compile_definitions(test-qtapp
PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(test-qtapp
PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Quick
statusgo_shared) # <- Link status-go with this target (remove `_shared` for static linking)

View File

@ -1,11 +0,0 @@
# Example QT app
```
cd build
cmake ..
make
./test-qtapp
```
This example app just shows how to plug-in status-go in a qt-app.
Also check [status-cpp](https://github.com/richard-ramos/status-cpp)

View File

@ -1,48 +0,0 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QJsonDocument>
#include <QJsonObject>
#include <iostream>
extern "C"
{
#include "libstatus.h"
}
int main(int argc, char *argv[])
{
QString hello = "Hello World!";
const char* result = HashMessage(hello.toUtf8().data()); // <- HashMessage comes from libstatus.h
const auto response = QJsonDocument::fromJson(QString(result).toUtf8()).object(); // <- For now, status-go always returns json
if (!response["error"].isUndefined() || !response["error"].toString().isEmpty()){ // <- Always do error handling
std::cout << "Could not obtain hash: " << response["result"].toString().toStdString() << std::endl << std::flush;
} else {
std::cout << "Hash of HelloWorld: " << response["result"].toString().toStdString() << std::endl << std::flush;
}
// For handling signals, see:
// https://github.com/richard-ramos/status-cpp/blob/master/src/core/status.cpp#L36
// https://github.com/richard-ramos/status-cpp/blob/master/src/core/status.cpp#L124-L127
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(
&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl)
{
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
engine.load(url);
return app.exec();
}

View File

@ -1,9 +0,0 @@
import QtQuick 2.14
import QtQuick.Window 2.14
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
}

View File

@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>

View File

@ -0,0 +1,76 @@
[
{
"id": "testnet_rpc",
"etherscan-link": "https://ropsten.etherscan.io/address/",
"name": "Ropsten with upstream RPC",
"config": {
"NetworkId": 3,
"DataDir": "/ethereum/testnet_rpc",
"UpstreamConfig": {
"Enabled": true,
"URL": "https://ropsten.infura.io/v3/%INFURA_KEY%"
}
}
},
{
"id": "rinkeby_rpc",
"etherscan-link": "https://rinkeby.etherscan.io/address/",
"name": "Rinkeby with upstream RPC",
"config": {
"NetworkId": 4,
"DataDir": "/ethereum/rinkeby_rpc",
"UpstreamConfig": {
"Enabled": true,
"URL": "https://rinkeby.infura.io/v3/%INFURA_KEY%"
}
}
},
{
"id": "goerli_rpc",
"etherscan-link": "https://goerli.etherscan.io/address/",
"name": "Goerli with upstream RPC",
"config": {
"NetworkId": 5,
"DataDir": "/ethereum/goerli_rpc",
"UpstreamConfig": {
"Enabled": true,
"URL": "https://goerli.blockscout.com/"
}
}
},
{
"id": "mainnet_rpc",
"etherscan-link": "https://etherscan.io/address/",
"name": "Mainnet with upstream RPC",
"config": {
"NetworkId": 1,
"DataDir": "/ethereum/mainnet_rpc",
"UpstreamConfig": {
"Enabled": true,
"URL": "https://mainnet.infura.io/v3/%INFURA_KEY%"
}
}
},
{
"id": "xdai_rpc",
"name": "xDai Chain",
"config": {
"NetworkId": 100,
"DataDir": "/ethereum/xdai_rpc",
"UpstreamConfig": {
"Enabled": true,
"URL": "https://dai.poa.network"
}
}
},
{
"id": "poa_rpc",
"name": "POA Network",
"config": {
"NetworkId": 99,
"DataDir": "/ethereum/poa_rpc",
"UpstreamConfig": { "Enabled": true, "URL": "https://core.poa.network" }
}
}
]

143
resources/fleets.json Normal file
View File

@ -0,0 +1,143 @@
{
"fleets": {
"eth.prod": {
"boot": {
"boot-01.ac-cn-hongkong-c.eth.prod": "enode://6e6554fb3034b211398fcd0f0082cbb6bd13619e1a7e76ba66e1809aaa0c5f1ac53c9ae79cf2fd4a7bacb10d12010899b370c75fed19b991d9c0cdd02891abad@47.75.99.169:443",
"boot-01.do-ams3.eth.prod": "enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@178.128.138.128:443",
"boot-01.gc-us-central1-a.eth.prod": "enode://32ff6d88760b0947a3dee54ceff4d8d7f0b4c023c6dad34568615fcae89e26cc2753f28f12485a4116c977be937a72665116596265aa0736b53d46b27446296a@34.70.75.208:443",
"boot-02.ac-cn-hongkong-c.eth.prod": "enode://23d0740b11919358625d79d4cac7d50a34d79e9c69e16831c5c70573757a1f5d7d884510bc595d7ee4da3c1508adf87bbc9e9260d804ef03f8c1e37f2fb2fc69@47.52.106.107:443",
"boot-02.do-ams3.eth.prod": "enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@178.128.140.188:443",
"boot-02.gc-us-central1-a.eth.prod": "enode://5405c509df683c962e7c9470b251bb679dd6978f82d5b469f1f6c64d11d50fbd5dd9f7801c6ad51f3b20a5f6c7ffe248cc9ab223f8bcbaeaf14bb1c0ef295fd0@35.223.215.156:443"
},
"mail": {
"mail-01.ac-cn-hongkong-c.eth.prod": "enode://606ae04a71e5db868a722c77a21c8244ae38f1bd6e81687cc6cfe88a3063fa1c245692232f64f45bd5408fed5133eab8ed78049332b04f9c110eac7f71c1b429@47.75.247.214:443",
"mail-01.do-ams3.eth.prod": "enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@178.128.142.54:443",
"mail-01.gc-us-central1-a.eth.prod": "enode://ee2b53b0ace9692167a410514bca3024695dbf0e1a68e1dff9716da620efb195f04a4b9e873fb9b74ac84de801106c465b8e2b6c4f0d93b8749d1578bfcaf03e@104.197.238.144:443",
"mail-02.ac-cn-hongkong-c.eth.prod": "enode://2c8de3cbb27a3d30cbb5b3e003bc722b126f5aef82e2052aaef032ca94e0c7ad219e533ba88c70585ebd802de206693255335b100307645ab5170e88620d2a81@47.244.221.14:443",
"mail-02.do-ams3.eth.prod": "enode://7aa648d6e855950b2e3d3bf220c496e0cae4adfddef3e1e6062e6b177aec93bc6cdcf1282cb40d1656932ebfdd565729da440368d7c4da7dbd4d004b1ac02bf8@178.128.142.26:443",
"mail-02.gc-us-central1-a.eth.prod": "enode://30211cbd81c25f07b03a0196d56e6ce4604bb13db773ff1c0ea2253547fafd6c06eae6ad3533e2ba39d59564cfbdbb5e2ce7c137a5ebb85e99dcfc7a75f99f55@23.236.58.92:443",
"mail-03.ac-cn-hongkong-c.eth.prod": "enode://e85f1d4209f2f99da801af18db8716e584a28ad0bdc47fbdcd8f26af74dbd97fc279144680553ec7cd9092afe683ddea1e0f9fc571ebcb4b1d857c03a088853d@47.244.129.82:443",
"mail-03.do-ams3.eth.prod": "enode://8a64b3c349a2e0ef4a32ea49609ed6eb3364be1110253c20adc17a3cebbc39a219e5d3e13b151c0eee5d8e0f9a8ba2cd026014e67b41a4ab7d1d5dd67ca27427@178.128.142.94:443",
"mail-03.gc-us-central1-a.eth.prod": "enode://44160e22e8b42bd32a06c1532165fa9e096eebedd7fa6d6e5f8bbef0440bc4a4591fe3651be68193a7ec029021cdb496cfe1d7f9f1dc69eb99226e6f39a7a5d4@35.225.221.245:443"
},
"rendezvous": {
"boot-01.ac-cn-hongkong-c.eth.prod": "/ip4/47.75.99.169/tcp/30703/ethv4/16Uiu2HAmV8Hq9e3zm9TMVP4zrVHo3BjqW5D6bDVV6VQntQd687e4",
"boot-01.do-ams3.eth.prod": "/ip4/178.128.138.128/tcp/30703/ethv4/16Uiu2HAmRHPzF3rQg55PgYPcQkyvPVH9n2hWsYPhUJBZ6kVjJgdV",
"boot-01.gc-us-central1-a.eth.prod": "/ip4/34.70.75.208/tcp/30703/ethv4/16Uiu2HAm6ZsERLx2BwVD2UM9SVPnnMU6NBycG8XPtu8qKys5awsU",
"boot-02.ac-cn-hongkong-c.eth.prod": "/ip4/47.52.106.107/tcp/30703/ethv4/16Uiu2HAmEHiptiDDd9gqNY8oQqo8hHUWMHJzfwt5aLRdD6W2zcXR",
"boot-02.do-ams3.eth.prod": "/ip4/178.128.140.188/tcp/30703/ethv4/16Uiu2HAmLqTXuY4Sb6G28HNooaFUXUKzpzKXCcgyJxgaEE2i5vnf",
"boot-02.gc-us-central1-a.eth.prod": "/ip4/35.223.215.156/tcp/30703/ethv4/16Uiu2HAmQEUFE2YaJohavWtHxPTEFv3sEGJtDqvtGEv78DFoEWQF"
},
"whisper": {
"node-01.ac-cn-hongkong-c.eth.prod": "enode://b957e51f41e4abab8382e1ea7229e88c6e18f34672694c6eae389eac22dab8655622bbd4a08192c321416b9becffaab11c8e2b7a5d0813b922aa128b82990dab@47.75.222.178:443",
"node-01.do-ams3.eth.prod": "enode://66ba15600cda86009689354c3a77bdf1a97f4f4fb3ab50ffe34dbc904fac561040496828397be18d9744c75881ffc6ac53729ddbd2cdbdadc5f45c400e2622f7@178.128.141.87:443",
"node-01.gc-us-central1-a.eth.prod": "enode://182ed5d658d1a1a4382c9e9f7c9e5d8d9fec9db4c71ae346b9e23e1a589116aeffb3342299bdd00e0ab98dbf804f7b2d8ae564ed18da9f45650b444aed79d509@34.68.132.118:443",
"node-02.ac-cn-hongkong-c.eth.prod": "enode://8bebe73ddf7cf09e77602c7d04c93a73f455b51f24ae0d572917a4792f1dec0bb4c562759b8830cc3615a658d38c1a4a38597a1d7ae3ba35111479fc42d65dec@47.75.85.212:443",
"node-02.do-ams3.eth.prod": "enode://4ea35352702027984a13274f241a56a47854a7fd4b3ba674a596cff917d3c825506431cf149f9f2312a293bb7c2b1cca55db742027090916d01529fe0729643b@134.209.136.79:443",
"node-02.gc-us-central1-a.eth.prod": "enode://fbeddac99d396b91d59f2c63a3cb5fc7e0f8a9f7ce6fe5f2eed5e787a0154161b7173a6a73124a4275ef338b8966dc70a611e9ae2192f0f2340395661fad81c0@34.67.230.193:443",
"node-03.ac-cn-hongkong-c.eth.prod": "enode://ac3948b2c0786ada7d17b80cf869cf59b1909ea3accd45944aae35bf864cc069126da8b82dfef4ddf23f1d6d6b44b1565c4cf81c8b98022253c6aea1a89d3ce2@47.75.88.12:443",
"node-03.do-ams3.eth.prod": "enode://ce559a37a9c344d7109bd4907802dd690008381d51f658c43056ec36ac043338bd92f1ac6043e645b64953b06f27202d679756a9c7cf62fdefa01b2e6ac5098e@134.209.136.123:443",
"node-03.gc-us-central1-a.eth.prod": "enode://c07aa0deea3b7056c5d45a85bca42f0d8d3b1404eeb9577610f386e0a4744a0e7b2845ae328efc4aa4b28075af838b59b5b3985bffddeec0090b3b7669abc1f3@35.226.92.155:443",
"node-04.ac-cn-hongkong-c.eth.prod": "enode://385579fc5b14e04d5b04af7eee835d426d3d40ccf11f99dbd95340405f37cf3bbbf830b3eb8f70924be0c2909790120682c9c3e791646e2d5413e7801545d353@47.244.221.249:443",
"node-04.do-ams3.eth.prod": "enode://4e0a8db9b73403c9339a2077e911851750fc955db1fc1e09f81a4a56725946884dd5e4d11258eac961f9078a393c45bcab78dd0e3bc74e37ce773b3471d2e29c@134.209.136.101:443",
"node-04.gc-us-central1-a.eth.prod": "enode://0624b4a90063923c5cc27d12624b6a49a86dfb3623fcb106801217fdbab95f7617b83fa2468b9ae3de593ff6c1cf556ccf9bc705bfae9cb4625999765127b423@35.222.158.246:443",
"node-05.ac-cn-hongkong-c.eth.prod": "enode://b77bffc29e2592f30180311dd81204ab845e5f78953b5ba0587c6631be9c0862963dea5eb64c90617cf0efd75308e22a42e30bc4eb3cd1bbddbd1da38ff6483e@47.75.10.177:443",
"node-05.do-ams3.eth.prod": "enode://a8bddfa24e1e92a82609b390766faa56cf7a5eef85b22a2b51e79b333c8aaeec84f7b4267e432edd1cf45b63a3ad0fc7d6c3a16f046aa6bc07ebe50e80b63b8c@178.128.141.249:443",
"node-05.gc-us-central1-a.eth.prod": "enode://a5fe9c82ad1ffb16ae60cb5d4ffe746b9de4c5fbf20911992b7dd651b1c08ba17dd2c0b27ee6b03162c52d92f219961cc3eb14286aca8a90b75cf425826c3bd8@104.154.230.58:443",
"node-06.ac-cn-hongkong-c.eth.prod": "enode://cf5f7a7e64e3b306d1bc16073fba45be3344cb6695b0b616ccc2da66ea35b9f35b3b231c6cf335fdfaba523519659a440752fc2e061d1e5bc4ef33864aac2f19@47.75.221.196:443",
"node-06.do-ams3.eth.prod": "enode://887cbd92d95afc2c5f1e227356314a53d3d18855880ac0509e0c0870362aee03939d4074e6ad31365915af41d34320b5094bfcc12a67c381788cd7298d06c875@178.128.141.0:443",
"node-06.gc-us-central1-a.eth.prod": "enode://282e009967f9f132a5c2dd366a76319f0d22d60d0c51f7e99795a1e40f213c2705a2c10e4cc6f3890319f59da1a535b8835ed9b9c4b57c3aad342bf312fd7379@35.223.240.17:443",
"node-07.ac-cn-hongkong-c.eth.prod": "enode://13d63a1f85ccdcbd2fb6861b9bd9d03f94bdba973608951f7c36e5df5114c91de2b8194d71288f24bfd17908c48468e89dd8f0fb8ccc2b2dedae84acdf65f62a@47.244.210.80:443",
"node-07.do-ams3.eth.prod": "enode://2b01955d7e11e29dce07343b456e4e96c081760022d1652b1c4b641eaf320e3747871870fa682e9e9cfb85b819ce94ed2fee1ac458904d54fd0b97d33ba2c4a4@134.209.136.112:443",
"node-07.gc-us-central1-a.eth.prod": "enode://b706a60572634760f18a27dd407b2b3582f7e065110dae10e3998498f1ae3f29ba04db198460d83ed6d2bfb254bb06b29aab3c91415d75d3b869cd0037f3853c@35.239.5.162:443",
"node-08.ac-cn-hongkong-c.eth.prod": "enode://32915c8841faaef21a6b75ab6ed7c2b6f0790eb177ad0f4ea6d731bacc19b938624d220d937ebd95e0f6596b7232bbb672905ee12601747a12ee71a15bfdf31c@47.75.59.11:443",
"node-08.do-ams3.eth.prod": "enode://0d9d65fcd5592df33ed4507ce862b9c748b6dbd1ea3a1deb94e3750052760b4850aa527265bbaf357021d64d5cc53c02b410458e732fafc5b53f257944247760@178.128.141.42:443",
"node-08.gc-us-central1-a.eth.prod": "enode://e87f1d8093d304c3a9d6f1165b85d6b374f1c0cc907d39c0879eb67f0a39d779be7a85cbd52920b6f53a94da43099c58837034afa6a7be4b099bfcd79ad13999@35.238.106.101:443"
}
},
"eth.staging": {
"boot": {
"boot-01.ac-cn-hongkong-c.eth.staging": "enode://630b0342ca4e9552f50714b6c8e28d6955bc0fd14e7950f93bc3b2b8cc8c1f3b6d103df66f51a13d773b5db0f130661fb5c7b8fa21c48890c64c79b41a56a490@47.91.229.44:443",
"boot-01.do-ams3.eth.staging": "enode://f79fb3919f72ca560ad0434dcc387abfe41e0666201ebdada8ede0462454a13deb05cda15f287d2c4bd85da81f0eb25d0a486bbbc8df427b971ac51533bd00fe@174.138.107.239:443",
"boot-01.gc-us-central1-a.eth.staging": "enode://10a78c17929a7019ef4aa2249d7302f76ae8a06f40b2dc88b7b31ebff4a623fbb44b4a627acba296c1ced3775d91fbe18463c15097a6a36fdb2c804ff3fc5b35@35.238.97.234:443"
},
"mail": {
"mail-01.ac-cn-hongkong-c.eth.staging": "enode://b74859176c9751d314aeeffc26ec9f866a412752e7ddec91b19018a18e7cca8d637cfe2cedcb972f8eb64d816fbd5b4e89c7e8c7fd7df8a1329fa43db80b0bfe@47.52.90.156:443",
"mail-01.do-ams3.eth.staging": "enode://69f72baa7f1722d111a8c9c68c39a31430e9d567695f6108f31ccb6cd8f0adff4991e7fdca8fa770e75bc8a511a87d24690cbc80e008175f40c157d6f6788d48@206.189.240.16:443",
"mail-01.gc-us-central1-a.eth.staging": "enode://e4fc10c1f65c8aed83ac26bc1bfb21a45cc1a8550a58077c8d2de2a0e0cd18e40fd40f7e6f7d02dc6cd06982b014ce88d6e468725ffe2c138e958788d0002a7f@35.239.193.41:443"
},
"rendezvous": {
"boot-01.ac-cn-hongkong-c.eth.staging": "/ip4/47.91.229.44/tcp/30703/ethv4/16Uiu2HAmRnt2Eyoknh3auxh4fJwkRgqkH1gqrWGes8Pk1k3MV4xu",
"boot-01.do-ams3.eth.staging": "/ip4/174.138.107.239/tcp/30703/ethv4/16Uiu2HAm8UZXUHEPZrpJbcQ3yVFH6UtKrwsG6jH4ai72PsbLfVFb",
"boot-01.gc-us-central1-a.eth.staging": "/ip4/35.238.97.234/tcp/30703/ethv4/16Uiu2HAm6G9sDMkrB4Xa5EH3Zx2dysCxFgBTSRzghic3Z9tRFRNE"
},
"whisper": {
"node-01.ac-cn-hongkong-c.eth.staging": "enode://088cf5a93c576fae52f6f075178467b8ff98bacf72f59e88efb16dfba5b30f80a4db78f8e3cb3d87f2f6521746ef4a8768465ef2896c6af24fd77a425e95b6dd@47.52.226.137:443",
"node-01.do-ams3.eth.staging": "enode://914c0b30f27bab30c1dfd31dad7652a46fda9370542aee1b062498b1345ee0913614b8b9e3e84622e84a7203c5858ae1d9819f63aece13ee668e4f6668063989@167.99.19.148:443",
"node-01.gc-us-central1-a.eth.staging": "enode://d3878441652f010326889f28360e69f2d09d06540f934fada0e17b374ce5319de64279aba3c44a5bf807d9967c6d705b3b4c6b03fa70763240e2ee6af01a539e@35.192.0.86:443"
}
},
"eth.test": {
"boot": {
"boot-01.ac-cn-hongkong-c.eth.test": "enode://daae2e72820e86e942fa2a8aa7d6e9954d4043a753483d8bd338e16be82cf962392d5c0e1ae57c3d793c3d3dddd8fd58339262e4234dc966f953cd73b535f5fa@47.52.188.149:443",
"boot-01.do-ams3.eth.test": "enode://9e0988575eb7717c25dea72fd11c7b37767dc09c1a7686f7c2ec577d308d24b377ceb675de4317474a1a870e47882732967f4fa785b02ba95d669b31d464dec0@206.189.243.164:443",
"boot-01.gc-us-central1-a.eth.test": "enode://c1e5018887c863d64e431b69bf617561087825430e4401733f5ba77c70db14236df381fefb0ebe1ac42294b9e261bbe233dbdb83e32c586c66ae26c8de70cb4c@35.188.168.137:443"
},
"mail": {
"mail-01.ac-cn-hongkong-c.eth.test": "enode://619dbb5dda12e85bf0eb5db40fb3de625609043242737c0e975f7dfd659d85dc6d9a84f9461a728c5ab68c072fed38ca6a53917ca24b8e93cc27bdef3a1e79ac@47.52.188.196:443",
"mail-01.do-ams3.eth.test": "enode://e4865fe6c2a9c1a563a6447990d8e9ce672644ae3e08277ce38ec1f1b690eef6320c07a5d60c3b629f5d4494f93d6b86a745a0bf64ab295bbf6579017adc6ed8@206.189.243.161:443",
"mail-01.gc-us-central1-a.eth.test": "enode://707e57453acd3e488c44b9d0e17975371e2f8fb67525eae5baca9b9c8e06c86cde7c794a6c2e36203bf9f56cae8b0e50f3b33c4c2b694a7baeea1754464ce4e3@35.192.229.172:443"
},
"rendezvous": {
"boot-01.ac-cn-hongkong-c.eth.test": "/ip4/47.52.188.149/tcp/30703/ethv4/16Uiu2HAm9Vatqr4GfVCqnyeaPtCF3q8fz8kDDUgqXVfFG7ZfSA7w",
"boot-01.do-ams3.eth.test": "/ip4/206.189.243.164/tcp/30703/ethv4/16Uiu2HAmBCh5bgYr6V3fDuLqUzvtSAsFTQJCQ3TVHT8ta8bTu2Jm",
"boot-01.gc-us-central1-a.eth.test": "/ip4/35.188.168.137/tcp/30703/ethv4/16Uiu2HAm3MUqtGjmetyZ9L4SN2R8oHDWvACUcec25LjtDD5euiRH"
},
"whisper": {
"node-01.ac-cn-hongkong-c.eth.test": "enode://ad38f94030a846cc7005b7a1f3b6b01bf4ef59d34e8d3d6f4d12df23d14ba8656702a435d34cf4df3b412c0c1923df5adcce8461321a0d8ffb9435b26e572c2a@47.52.255.194:443",
"node-01.do-ams3.eth.test": "enode://1d193635e015918fb85bbaf774863d12f65d70c6977506187ef04420d74ec06c9e8f0dcb57ea042f85df87433dab17a1260ed8dde1bdf9d6d5d2de4b7bf8e993@206.189.243.163:443",
"node-01.gc-us-central1-a.eth.test": "enode://f593a27731bc0f8eb088e2d39222c2d59dfb9bf0b3950d7a828d51e8ab9e08fffbd9916a82fd993c1a080c57c2bd70ed6c36f489a969de697aff93088dbee1a9@35.194.31.108:443"
}
},
"go-waku.test": {
"libp2p": {
"node-01.ac-cn-hongkong-c.go-waku.test": "/ip4/8.218.2.110/tcp/30303/p2p/16Uiu2HAmBDbMWFiG9ki8sDw6fYtraSxo4oHU9HbuN43S2HVyq1FD",
"node-01.do-ams3.go-waku.test": "/ip4/134.209.134.63/tcp/30303/p2p/16Uiu2HAm9vnvCQgCDrynDK1h7GJoEZVGvnuzq84RyDQ3DEdXmcX7",
"node-01.gc-us-central1-a.go-waku.test": "/ip4/35.223.183.91/tcp/30303/p2p/16Uiu2HAmPz63Xc6AuVkDeujz7YeZta18rcdau3Y1BzaxKAfDrBqz"
},
"websocket": {
"node-01.ac-cn-hongkong-c.go-waku.test": "/dns4/node-01.ac-cn-hongkong-c.go-waku.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmBDbMWFiG9ki8sDw6fYtraSxo4oHU9HbuN43S2HVyq1FD",
"node-01.do-ams3.go-waku.test": "/dns4/node-01.do-ams3.go-waku.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAm9vnvCQgCDrynDK1h7GJoEZVGvnuzq84RyDQ3DEdXmcX7",
"node-01.gc-us-central1-a.go-waku.test": "/dns4/node-01.gc-us-central1-a.go-waku.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPz63Xc6AuVkDeujz7YeZta18rcdau3Y1BzaxKAfDrBqz"
}
},
"wakuv2.prod": {
"waku": {
"node-01.ac-cn-hongkong-c.wakuv2.prod": "/ip4/8.210.222.231/tcp/30303/p2p/16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr24iDQpSN5Qa992BCjjwgrD",
"node-01.do-ams3.wakuv2.prod": "/ip4/188.166.135.145/tcp/30303/p2p/16Uiu2HAmL5okWopX7NqZWBUKVqW8iUxCEmd5GMHLVPwCgzYzQv3e",
"node-01.gc-us-central1-a.wakuv2.prod": "/ip4/34.121.100.108/tcp/30303/p2p/16Uiu2HAmVkKntsECaYfefR1V2yCR79CegLATuTPE6B9TxgxBiiiA"
},
"waku-websocket": {
"node-01.ac-cn-hongkong-c.wakuv2.prod": "/dns4/node-01.ac-cn-hongkong-c.wakuv2.prod.statusim.net/tcp/443/wss/p2p/16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr24iDQpSN5Qa992BCjjwgrD",
"node-01.do-ams3.wakuv2.prod": "/dns4/node-01.do-ams3.wakuv2.prod.statusim.net/tcp/443/wss/p2p/16Uiu2HAmL5okWopX7NqZWBUKVqW8iUxCEmd5GMHLVPwCgzYzQv3e",
"node-01.gc-us-central1-a.wakuv2.prod": "/dns4/node-01.gc-us-central1-a.wakuv2.prod.statusim.net/tcp/443/wss/p2p/16Uiu2HAmVkKntsECaYfefR1V2yCR79CegLATuTPE6B9TxgxBiiiA"
}
},
"wakuv2.test": {
"waku": {
"node-01.ac-cn-hongkong-c.wakuv2.test": "/ip4/47.242.210.73/tcp/30303/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm",
"node-01.do-ams3.wakuv2.test": "/ip4/134.209.139.210/tcp/30303/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ",
"node-01.gc-us-central1-a.wakuv2.test": "/ip4/104.154.239.128/tcp/30303/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS"
},
"waku-websocket": {
"node-01.ac-cn-hongkong-c.wakuv2.test": "/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkvWiyFsgRhuJEb9JfjYxEkoHLgnUQmr1N5mKWnYjxYRVm",
"node-01.do-ams3.wakuv2.test": "/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ",
"node-01.gc-us-central1-a.wakuv2.test": "/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS"
}
}
},
"meta": {
"hostname": "node-01.do-ams3.sites.misc",
"timestamp": "2021-10-19T00:00:15.465044"
}
}

1
resources/infura_key Normal file
View File

@ -0,0 +1 @@
220a1abb4b6943a093c35d0ce4fb0732

View File

@ -0,0 +1,65 @@
{
"BrowsersConfig": {
"Enabled": true
},
"ClusterConfig": {
"Enabled": true
},
"DataDir": "./ethereum/mainnet",
"EnableNTPSync": true,
"KeyStoreDir": "./keystore",
"LogEnabled": true,
"LogFile": "./geth.log",
"LogLevel": "INFO",
"MailserversConfig": {
"Enabled": true
},
"Name": "StatusDesktop",
"NetworkId": 1,
"NoDiscovery": false,
"PermissionsConfig": {
"Enabled": true
},
"Rendezvous": true,
"RegisterTopics": ["whispermail"],
"RequireTopics": {
"whisper": {
"Max": 2,
"Min": 2
}
},
"ShhextConfig": {
"BackupDisabledDataDir": "./",
"DataSyncEnabled": true,
"InstallationID": "%INSTALLATIONID%",
"MailServerConfirmations": true,
"MaxMessageDeliveryAttempts": 6,
"PFSEnabled": true,
"VerifyENSContractAddress": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
"VerifyENSURL": "https://mainnet.infura.io/v3/%INFURA_KEY%",
"VerifyTransactionChainID": 1,
"VerifyTransactionURL": "https://mainnet.infura.io/v3/%INFURA_KEY%"
},
"StatusAccountsConfig": {
"Enabled": true
},
"UpstreamConfig": {
"Enabled": true,
"URL": "https://mainnet.infura.io/v3/%INFURA_KEY%"
},
"WakuConfig": {
"BloomFilterMode": null,
"Enabled": true,
"LightClient": true,
"MinimumPoW": 0.001
},
"WakuV2Config": {
"Enabled": false,
"Host": "0.0.0.0",
"Port": 0,
"LightClient": false
},
"WalletConfig": {
"Enabled": true
}
}

View File

@ -0,0 +1,36 @@
add_library(app
include/signals.h
boot/app_controller.cpp
core/signals/signals.cpp
global/singleton.cpp
modules/startup/controller.cpp
modules/startup/module.cpp
modules/startup/view.cpp
modules/startup/onboarding/controller.cpp
modules/startup/onboarding/item.cpp
modules/startup/onboarding/model.cpp
modules/startup/onboarding/module.cpp
modules/startup/onboarding/view.cpp
modules/startup/login/controller.cpp
modules/startup/login/item.cpp
modules/startup/login/model.cpp
modules/startup/login/module.cpp
modules/startup/login/view.cpp
modules/startup/login/selected_account.cpp
)
target_include_directories(app
PUBLIC
./include/
./modules/startup
)
target_link_libraries(app
PRIVATE
app_service
statusgo_shared
Qt${QT_VERSION_MAJOR}::Concurrent
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Quick
)

View File

@ -0,0 +1,237 @@
#include "app_controller.h"
#include "accounts/service.h"
#include "app_service.h"
#include "modules/startup/module.h"
#include <QDebug>
AppController::AppController()
{
// result.statusFoundation = statusFoundation
// # Global
// result.localAppSettingsVariant = newQVariant(singletonInstance.localAppSettings)
// result.localAccountSettingsVariant = newQVariant(singletonInstance.localAccountSettings)
// result.localAccountSensitiveSettingsVariant = newQVariant(singletonInstance.localAccountSensitiveSettings)
// result.userProfileVariant = newQVariant(singletonInstance.userProfile)
// result.globalUtilsVariant = newQVariant(singletonInstance.utils)
// # Services
// result.settingsService = settings_service.newService()
// result.nodeConfigurationService = node_configuration_service.newService(statusFoundation.fleetConfiguration,
// result.settingsService)
// result.osNotificationService = os_notification_service.newService(statusFoundation.status.events)
// result.keychainService = keychain_service.newService(statusFoundation.status.events)
// result.ethService = eth_service.newService()
m_accountsService = new Accounts::Service();
// result.networkService = network_service.newService()
// result.contactsService = contacts_service.newService(statusFoundation.status.events, statusFoundation.threadpool)
// result.chatService = chat_service.newService(statusFoundation.status.events, result.contactsService)
// result.communityService = community_service.newService(statusFoundation.status.events)
// result.messageService = message_service.newService(statusFoundation.status.events, statusFoundation.threadpool)
// result.activityCenterService = activity_center_service.newService(statusFoundation.status.events,
// statusFoundation.threadpool, result.chatService)
// result.tokenService = token_service.newService(statusFoundation.status.events, statusFoundation.threadpool,
// result.settingsService)
// result.collectibleService = collectible_service.newService(result.settingsService)
// result.walletAccountService = wallet_account_service.newService(statusFoundation.status.events, result.settingsService,
// result.accountsService, result.tokenService)
// result.transactionService = transaction_service.newService(statusFoundation.status.events, statusFoundation.threadpool,
// result.walletAccountService)
// result.bookmarkService = bookmark_service.newService()
// result.profileService = profile_service.newService()
// result.stickersService = stickers_service.newService(
// statusFoundation.status.events,
// statusFoundation.threadpool,
// result.ethService,
// result.settingsService,
// result.walletAccountService,
// result.transactionService,
// result.networkService,
// result.chatService
// )
// result.aboutService = about_service.newService(statusFoundation.status.events, statusFoundation.threadpool,
// result.settingsService)
// result.dappPermissionsService = dapp_permissions_service.newService()
// result.languageService = language_service.newService()
// # result.mnemonicService = mnemonic_service.newService()
// result.privacyService = privacy_service.newService(statusFoundation.status.events, result.settingsService,
// result.accountsService)
// result.providerService = provider_service.newService(result.dappPermissionsService, result.settingsService)
// result.savedAddressService = saved_address_service.newService(statusFoundation.status.events)
// result.devicesService = devices_service.newService(statusFoundation.status.events, result.settingsService)
// result.mailserversService = mailservers_service.newService(statusFoundation.status.events, statusFoundation.marathon,
// result.settingsService, result.nodeConfigurationService, statusFoundation.fleetConfiguration)
// # Modules
m_startupModule = new Modules::Startup::Module(this, /*keychainService,*/ m_accountsService);
// result.mainModule = main_module.newModule[AppController](
// result,
// statusFoundation.status.events,
// result.keychainService,
// result.accountsService,
// result.chatService,
// result.communityService,
// result.messageService,
// result.tokenService,
// result.transactionService,
// result.collectibleService,
// result.walletAccountService,
// result.bookmarkService,
// result.profileService,
// result.settingsService,
// result.contactsService,
// result.aboutService,
// result.dappPermissionsService,
// result.languageService,
// # result.mnemonicService,
// result.privacyService,
// result.providerService,
// result.stickersService,
// result.activityCenterService,
// result.savedAddressService,
// result.nodeConfigurationService,
// result.devicesService,
// result.mailserversService
// )
// # Do connections
connect();
}
AppController::~AppController()
{
delete m_startupModule;
delete m_accountsService;
}
void AppController::connect()
{
// self.statusFoundation.status.events.once("nodeStopped") do(a: Args):
// TODO: remove this once accounts are not tracked in the AccountsModel
// self.statusFoundation.status.reset()
}
void AppController::startupDidLoad()
{
// singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant)
// singletonInstance.engine.setRootContextProperty("localAccountSettings", self.localAccountSettingsVariant)
// singletonInstance.engine.load(newQUrl("qrc:///main.qml"))
// We need to init a language service once qml is loaded
// self.languageService.init()
}
void AppController::mainDidLoad()
{
//self.statusFoundation.onLoggedIn()
m_startupModule->moveToAppState();
//self.mainModule.checkForStoringPassword()
}
void AppController::start()
{
// self.ethService.init()
m_accountsService->init();
m_startupModule->load();
}
void AppController::load()
{
qWarning() << "TODO: init services and load main module";
// self.settingsService.init()
// self.nodeConfigurationService.init()
// self.contactsService.init()
// self.chatService.init()
// self.messageService.init()
// self.communityService.init()
// self.bookmarkService.init()
// self.tokenService.init()
// self.dappPermissionsService.init()
// self.providerService.init()
// self.walletAccountService.init()
// self.transactionService.init()
// self.stickersService.init()
// self.networkService.init()
// self.activityCenterService.init()
// self.savedAddressService.init()
// self.aboutService.init()
// self.devicesService.init()
// self.mailserversService.init()
// let pubKey = self.settingsService.getPublicKey()
// singletonInstance.localAccountSensitiveSettings.setFileName(pubKey)
// singletonInstance.engine.setRootContextProperty("localAccountSensitiveSettings", self.localAccountSensitiveSettingsVariant)
// singletonInstance.engine.setRootContextProperty("globalUtils", self.globalUtilsVariant)
// # other global instances
// self.buildAndRegisterLocalAccountSensitiveSettings()
// self.buildAndRegisterUserProfile()
// # load main module
// self.mainModule.load(
// self.statusFoundation.status.events,
// self.settingsService,
// self.contactsService,
// self.chatService,
// self.communityService,
// self.messageService
// )
}
void AppController::userLoggedIn()
{
//self.statusFoundation.status.startMessenger()
AppController::load();
// Once user is logged in and main module is loaded we need to check if it gets here importing mnemonic or not
// and delete mnemonic in the first case.
auto importedAccount = m_accountsService->getImportedAccount();
if(importedAccount.isValid())
{
// self.privacyService.removeMnemonic();
}
}
void AppController::buildAndRegisterLocalAccountSensitiveSettings()
{
// var pubKey = self.settingsService.getPublicKey()
// singletonInstance.localAccountSensitiveSettings.setFileName(pubKey)
// singletonInstance.engine.setRootContextProperty("localAccountSensitiveSettings", self.localAccountSensitiveSettingsVariant)
}
void AppController::buildAndRegisterUserProfile()
{
// let pubKey = self.settingsService.getPublicKey()
// let preferredName = self.settingsService.getPreferredName()
// let ensUsernames = self.settingsService.getEnsUsernames()
// let firstEnsName = if (ensUsernames.len > 0): ensUsernames[0] else: ""
// let sendUserStatus = self.settingsService.getSendStatusUpdates()
// // This is still not in use. Read a comment in UserProfile.
// // let currentUserStatus = self.settingsService.getCurrentUserStatus()
// let loggedInAccount = self.accountsService.getLoggedInAccount()
// var thumbnail, large: string
// for img in loggedInAccount.images:
// if(img.imgType == "large"):
// large = img.uri
// elif(img.imgType == "thumbnail"):
// thumbnail = img.uri
// let meAsContact = self.contactsService.getContactById(pubKey)
// singletonInstance.userProfile.setFixedData(loggedInAccount.name, loggedInAccount.keyUid, loggedInAccount.identicon,
// pubKey)
// singletonInstance.userProfile.setPreferredName(preferredName)
// singletonInstance.userProfile.setEnsName(meAsContact.name)
// singletonInstance.userProfile.setFirstEnsName(firstEnsName)
// singletonInstance.userProfile.setThumbnailImage(thumbnail)
// singletonInstance.userProfile.setLargeImage(large)
// singletonInstance.userProfile.setUserStatus(sendUserStatus)
// singletonInstance.engine.setRootContextProperty("userProfile", self.userProfileVariant)
}

View File

@ -0,0 +1,91 @@
#include "signals.h"
#include "libstatus.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QObject>
#include <QtConcurrent>
namespace Signals
{
Manager* Manager::theInstance;
Manager* Manager::instance()
{
if(theInstance == 0) theInstance = new Manager();
return theInstance;
}
std::map<QString, SignalType> Manager::signalMap;
Manager::Manager(QObject* parent)
: QObject(parent)
{
SetSignalEventCallback((void*)&Manager::signalCallback);
signalMap = {{"node.ready", SignalType::NodeReady},
{"node.started", SignalType::NodeStarted},
{"node.stopped", SignalType::NodeStopped},
{"node.login", SignalType::NodeLogin},
{"node.crashed", SignalType::NodeCrashed}};
}
void Manager::processSignal(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 Manager::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()];
switch(signalType)
{
// TODO: create extractor functions like in nim
case NodeLogin: emit instance()->nodeLogin(NodeSignal{signalType, signalEvent["event"]["error"].toString()}); break;
case NodeReady: emit instance()->nodeReady(NodeSignal{signalType, signalEvent["event"]["error"].toString()}); break;
case NodeStarted:
emit instance()->nodeStarted(NodeSignal{signalType, signalEvent["event"]["error"].toString()});
break;
case NodeStopped:
emit instance()->nodeStopped(NodeSignal{signalType, signalEvent["event"]["error"].toString()});
break;
case NodeCrashed: {
auto signal = NodeSignal{signalType, signalEvent["event"]["error"].toString()};
qWarning() << "node.crashed, error: " << signal.error;
emit instance()->nodeCrashed(signal);
break;
}
default: qWarning() << "Signal decoding not implemented: " << signalEvent; break;
}
}
void Manager::signalCallback(const char* data)
{
QtConcurrent::run(instance(), &Manager::processSignal, QString(data));
}
} // namespace Signals

View File

@ -0,0 +1,23 @@
#include "singleton.h"
#include <QQmlApplicationEngine>
namespace Global
{
Singleton* Singleton::theInstance;
Singleton* Singleton::instance()
{
if(theInstance == 0) theInstance = new Singleton();
return theInstance;
}
Singleton::Singleton()
{
m_engine = new QQmlApplicationEngine();
}
QQmlApplicationEngine* Singleton::engine()
{
return m_engine;
}
} // namespace Global

View File

@ -0,0 +1,39 @@
#pragma once
#include "accounts/service.h"
#include "module_access_interface.h"
#include "app_controller_delegate.h"
#include "app_service.h"
class AppController : public AppControllerDelegate
{
//statusFoundation: StatusFoundation
// Global
//localAppSettingsVariant: QVariant
//localAccountSettingsVariant: QVariant
//localAccountSensitiveSettingsVariant: QVariant
//userProfileVariant: QVariant
//globalUtilsVariant: QVariant
// Services
Accounts::Service* m_accountsService;
// Modules
Modules::Startup::ModuleAccessInterface* m_startupModule;
//mainModule: main_module.AccessInterface
public:
AppController();
~AppController();
void start();
private:
void connect();
void startupDidLoad() override;
void mainDidLoad();
void load();
void userLoggedIn() override;
void buildAndRegisterLocalAccountSensitiveSettings();
void buildAndRegisterUserProfile();
};

View File

@ -0,0 +1,17 @@
#pragma once
#include <stdexcept>
class AppControllerDelegate
{
public:
virtual void startupDidLoad()
{
throw std::domain_error("Not implemented");
}
virtual void userLoggedIn()
{
throw std::domain_error("Not implemented");
}
};

View File

@ -0,0 +1,62 @@
#pragma once
#include <QObject>
#include <QString>
#include <QVariant>
#include <QVariantList>
namespace Signals
{
Q_NAMESPACE
enum SignalType
{
Unknown,
NodeLogin,
NodeReady,
NodeStarted,
NodeStopped,
NodeCrashed
};
Q_ENUM_NS(SignalType)
struct Signal
{
SignalType signalType;
};
struct NodeSignal : Signal
{
QString error;
};
class Manager : public QObject
{
Q_OBJECT
public:
static Manager* instance();
signals:
void signal(SignalType signal);
void nodeReady(NodeSignal signal);
void nodeStarted(NodeSignal signal);
void nodeStopped(NodeSignal signal);
void nodeLogin(NodeSignal signal);
void nodeCrashed(NodeSignal signal);
private:
static Manager* theInstance;
explicit Manager(QObject* parent = nullptr);
static std::map<QString, SignalType> signalMap;
static void signalCallback(const char* data);
void processSignal(QString ev);
void decode(const QJsonObject& signalEvent);
};
} // namespace Signals
Q_DECLARE_METATYPE(Signals::Signal)
Q_DECLARE_METATYPE(Signals::NodeSignal)

View File

@ -0,0 +1,20 @@
#pragma once
#include <QQmlApplicationEngine>
namespace Global
{
class Singleton
{
public:
QQmlApplicationEngine* engine();
static Singleton* instance();
private:
static Singleton* theInstance;
explicit Singleton();
QQmlApplicationEngine* m_engine;
};
} // namespace Global

View File

@ -0,0 +1,55 @@
#include "controller.h"
#include "accounts/service_interface.h"
#include "interfaces/module_controller_delegate_interface.h"
#include "signals.h"
#include <QDebug>
namespace Modules
{
namespace Startup
{
Controller::Controller(ModuleControllerDelegateInterface* d,
Accounts::ServiceInterface* accountsService,
QObject* parent)
: QObject(parent)
, m_accountsService(accountsService)
, m_delegate(d)
{ }
void Controller::init()
{
QObject::connect(Signals::Manager::instance(), &Signals::Manager::nodeLogin, this, &Controller::onLogin);
QObject::connect(Signals::Manager::instance(), &Signals::Manager::nodeStopped, this, &Controller::onNodeStopped);
QObject::connect(Signals::Manager::instance(), &Signals::Manager::nodeReady, this, &Controller::onNodeReady);
}
void Controller::onLogin(Signals::NodeSignal signal)
{
if(signal.error.isEmpty())
{
m_delegate->userLoggedIn();
}
else
{
qWarning() << "error: methodName=init, errDescription=login error " << signal.error;
}
}
void Controller::onNodeStopped(Signals::NodeSignal signal)
{
// self.events.emit("nodeStopped", Args())
m_accountsService->clear();
m_delegate->emitLogOut();
}
void Controller::onNodeReady(Signals::NodeSignal signal)
{
// self.events.emit("nodeReady", Args())
}
bool Controller::shouldStartWithOnboardingScreen()
{
return m_accountsService->openedAccounts().size() == 0;
}
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,32 @@
#pragma once
#include "accounts/service_interface.h"
#include "controller_interface.h"
#include "interfaces/module_controller_delegate_interface.h"
#include "signals.h"
#include <QObject>
namespace Modules
{
namespace Startup
{
class Controller : public QObject, ControllerInterface
{
public:
Controller(ModuleControllerDelegateInterface* d,
Accounts::ServiceInterface* accountsService,
QObject* parent = nullptr);
void init() override;
bool shouldStartWithOnboardingScreen() override;
void onLogin(Signals::NodeSignal signal);
void onNodeStopped(Signals::NodeSignal signal);
void onNodeReady(Signals::NodeSignal signal);
private:
Accounts::ServiceInterface* m_accountsService;
ModuleControllerDelegateInterface* m_delegate;
};
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,23 @@
#pragma once
namespace Modules
{
namespace Startup
{
// Abstract class for any input/interaction with this module.
class ControllerInterface
{
public:
virtual void init()
{
throw std::domain_error("Not implemented");
}
virtual bool shouldStartWithOnboardingScreen()
{
throw std::domain_error("Not implemented");
}
};
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,26 @@
#pragma once
#include "accounts/service_interface.h"
#include "app_controller_delegate.h"
#include "controller.h"
#include <stdexcept>
namespace Modules
{
namespace Startup
{
class ModuleControllerDelegateInterface
{
public:
virtual void userLoggedIn()
{
throw std::domain_error("Not implemented");
}
virtual void emitLogOut()
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,18 @@
#pragma once
#include <stdexcept>
namespace Modules
{
namespace Startup
{
class ModuleLoginDelegateInterface
{
public:
virtual void loginDidLoad()
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,18 @@
#pragma once
#include <stdexcept>
namespace Modules
{
namespace Startup
{
class ModuleOnboardingDelegateInterface
{
public:
virtual void onboardingDidLoad()
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,18 @@
#pragma once
#include <stdexcept>
namespace Modules
{
namespace Startup
{
class ModuleViewDelegateInterface
{
public:
virtual void viewDidLoad()
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,87 @@
#include "controller.h"
#include "accounts/account.h"
#include "accounts/service_interface.h"
#include "interfaces/module_controller_delegate_interface.h"
#include "signals.h"
#include <QDebug>
#include <QString>
#include <QVector>
namespace Modules
{
namespace Startup
{
namespace Login
{
Controller::Controller(ModuleControllerDelegateInterface* d,
// keychainService
Accounts::ServiceInterface* accountsService,
QObject* parent)
: QObject(parent)
, m_accountsService(accountsService)
, m_delegate(d)
{ }
void Controller::init()
{
QObject::connect(Signals::Manager::instance(), &Signals::Manager::nodeLogin, this, &Controller::onLogin);
// keychainServiceSuccess see src-cpp/app/modules/startup/login/controller.nim line 43
// keychainServiceError see src-cpp/app/modules/startup/login/controller.nim line 47
}
void Controller::onLogin(Signals::NodeSignal signal)
{
if(!signal.error.isEmpty())
{
m_delegate->emitAccountLoginError(signal.error);
}
}
QVector<Accounts::AccountDto> Controller::getOpenedAccounts()
{
return m_accountsService->openedAccounts();
}
Accounts::AccountDto Controller::getSelectedAccount()
{
auto openedAccounts = Controller::getOpenedAccounts();
foreach(const Accounts::AccountDto& acc, openedAccounts)
{
if(acc.keyUid == m_selectedAccountKeyUid)
{
return acc;
}
}
}
void Controller::setSelectedAccountKeyUid(QString keyUid)
{
m_selectedAccountKeyUid = keyUid;
// Dealing with Keychain is the MacOS only feature
// if(not defined(macosx)):
// return
// let selectedAccount = self.getSelectedAccount()
// singletonInstance.localAccountSettings.setFileName(selectedAccount.name)
// let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
// if (value != LS_VALUE_STORE):
// return
// self.keychainService.tryToObtainPassword(selectedAccount.name)
}
void Controller::login(QString password)
{
auto selectedAccount = Controller::getSelectedAccount();
auto error = m_accountsService->login(selectedAccount, password);
if(!error.isEmpty())
{
m_delegate->emitAccountLoginError(error);
}
}
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,41 @@
#pragma once
#include "accounts/account.h"
#include "accounts/service_interface.h"
#include "controller_interface.h"
#include "interfaces/module_controller_delegate_interface.h"
#include "signals.h"
#include <QObject>
#include <QString>
namespace Modules
{
namespace Startup
{
namespace Login
{
class Controller : public QObject, ControllerInterface
{
Q_OBJECT
public:
Controller(ModuleControllerDelegateInterface* d,
// keychainService,
Accounts::ServiceInterface* accountsService,
QObject* parent = nullptr);
void init() override;
QVector<Accounts::AccountDto> getOpenedAccounts() override;
Accounts::AccountDto getSelectedAccount();
void setSelectedAccountKeyUid(QString keyUid) override;
void login(QString password) override;
void onLogin(Signals::NodeSignal signal);
private:
// Keychain::m_keychainService
Accounts::ServiceInterface* m_accountsService;
ModuleControllerDelegateInterface* m_delegate;
QString m_selectedAccountKeyUid;
};
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,36 @@
#pragma once
#include "accounts/account.h"
namespace Modules
{
namespace Startup
{
namespace Login
{
// Abstract class for any input/interaction with this module.
class ControllerInterface
{
public:
virtual void init()
{
throw std::domain_error("Not implemented");
}
virtual QVector<Accounts::AccountDto> getOpenedAccounts()
{
throw std::domain_error("Not implemented");
}
virtual void setSelectedAccountKeyUid(QString keyUid)
{
throw std::domain_error("Not implemented");
}
virtual void login(QString password)
{
throw std::domain_error("Not implemented");
}
};
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,34 @@
#pragma once
#include "accounts/service_interface.h"
#include "app_controller_delegate.h"
#include "controller.h"
#include <stdexcept>
namespace Modules
{
namespace Startup
{
namespace Login
{
class ModuleControllerDelegateInterface
{
public:
virtual void emitAccountLoginError(QString error)
{
throw std::domain_error("Not implemented");
}
virtual void emitObtainingPasswordError(QString errorDescription)
{
throw std::domain_error("Not implemented");
}
virtual void emitObtainingPasswordSuccess(QString password)
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Login
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,32 @@
#pragma once
#include "../item.h"
#include <QString>
#include <stdexcept>
namespace Modules
{
namespace Startup
{
namespace Login
{
class ModuleViewDelegateInterface
{
public:
virtual void viewDidLoad()
{
throw std::domain_error("Not implemented");
}
virtual void setSelectedAccount(Item item)
{
throw std::domain_error("Not implemented");
}
virtual void login(QString password)
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Login
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,41 @@
#include "item.h"
#include <QString>
namespace Modules
{
namespace Startup
{
namespace Login
{
Item::Item() { }
Item::Item(QString name, QString identicon, QString thumbnailImage, QString largeImage, QString keyUid)
: m_name(name)
, m_identicon(identicon)
, m_thumbnailImage(thumbnailImage)
, m_largeImage(largeImage)
, m_keyUid(keyUid)
{ }
QString Item::getName()
{
return m_name;
}
QString Item::getIdenticon()
{
return m_identicon;
}
QString Item::getThumbnailImage()
{
return m_thumbnailImage;
}
QString Item::getLargeImage()
{
return m_largeImage;
}
QString Item::getKeyUid()
{
return m_keyUid;
}
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,31 @@
#pragma once
#include <QString>
namespace Modules
{
namespace Startup
{
namespace Login
{
class Item
{
private:
QString m_name;
QString m_identicon;
QString m_thumbnailImage;
QString m_largeImage;
QString m_keyUid;
public:
Item();
Item(QString name, QString identicon, QString thumbnailImage, QString largeImage, QString keyUid);
QString getName();
QString getIdenticon();
QString getThumbnailImage();
QString getLargeImage();
QString getKeyUid();
};
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,75 @@
#include "model.h"
#include <QAbstractListModel>
#include <QDebug>
namespace Modules
{
namespace Startup
{
namespace Login
{
Model::Model(QObject* parent)
: QAbstractListModel(parent)
{ }
QHash<int, QByteArray> Model::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Name] = "username";
roles[Identicon] = "identicon";
roles[ThumbnailImage] = "thumbnailImage";
roles[LargeImage] = "largeImage";
roles[KeyUid] = "keyUid";
return roles;
}
int Model::rowCount(const QModelIndex& parent = QModelIndex()) const
{
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 QVariant(item.getName());
case Identicon: return QVariant(item.getIdenticon());
case ThumbnailImage: return QVariant(item.getThumbnailImage());
case LargeImage: return QVariant(item.getLargeImage());
case KeyUid: return QVariant(item.getKeyUid());
}
return QVariant();
}
void Model::setItems(QVector<Item> items)
{
beginResetModel();
m_items = items;
endResetModel();
}
Item Model::getItemAtIndex(int index)
{
if(index < 0 || index >= m_items.size())
{
return Item();
}
return m_items[index];
}
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,41 @@
#pragma once
#include "item.h"
#include <QAbstractListModel>
#include <QHash>
#include <QVector>
namespace Modules
{
namespace Startup
{
namespace Login
{
class Model : public QAbstractListModel
{
Q_OBJECT
public:
enum ModelRole
{
Name = Qt::UserRole + 1,
Identicon = Qt::UserRole + 2,
ThumbnailImage = Qt::UserRole + 3,
LargeImage = Qt::UserRole + 4,
KeyUid = Qt::UserRole + 5
};
explicit Model(QObject* parent = nullptr);
QHash<int, QByteArray> roleNames() const;
virtual int rowCount(const QModelIndex&) const;
virtual QVariant data(const QModelIndex& index, int role) const;
void setItems(QVector<Item> items);
Item getItemAtIndex(int index);
private:
QVector<Item> m_items;
};
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,115 @@
#include "module.h"
#include "../interfaces/module_login_delegate_interface.h"
#include "accounts/account.h"
#include "accounts/service_interface.h"
#include "controller.h"
#include "singleton.h"
#include "view.h"
#include <QDebug>
#include <QObject>
#include <QQmlContext>
#include <QVariant>
#include <iostream>
namespace Modules
{
namespace Startup
{
namespace Login
{
Module::Module(Modules::Startup::ModuleLoginDelegateInterface* d,
// keychainService
Accounts::ServiceInterface* accountsService)
{
m_delegate = d;
m_controller = new Controller(this, accountsService);
m_view = new View(this);
m_moduleLoaded = false;
}
Module::~Module()
{
delete m_controller;
delete m_view;
}
void Module::extractImages(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::load()
{
Global::Singleton::instance()->engine()->rootContext()->setContextProperty("loginModule", m_view);
m_controller->init();
m_view->load();
QVector<Accounts::AccountDto> openedAccounts = m_controller->getOpenedAccounts();
if(openedAccounts.size() > 0)
{
QVector<Item> items;
foreach(const Accounts::AccountDto& acc, openedAccounts)
{
QString thumbnailImage;
QString largeImage;
Module::extractImages(acc, thumbnailImage, largeImage);
items << Item(acc.name, acc.identicon, thumbnailImage, largeImage, acc.keyUid);
}
m_view->setModelItems(items);
// set the first account as slected one
m_controller->setSelectedAccountKeyUid(items[0].getKeyUid());
Module::setSelectedAccount(items[0]);
}
}
bool Module::isLoaded()
{
return m_moduleLoaded;
}
void Module::viewDidLoad()
{
m_moduleLoaded = true;
m_delegate->loginDidLoad();
}
void Module::setSelectedAccount(Item item)
{
m_controller->setSelectedAccountKeyUid(item.getKeyUid());
m_view->setSelectedAccount(item);
}
void Module::login(QString password)
{
m_controller->login(password);
}
void Module::emitAccountLoginError(QString error)
{
m_view->emitAccountLoginError(error);
}
void Module::emitObtainingPasswordError(QString errorDescription)
{
m_view->emitObtainingPasswordError(errorDescription);
}
void Module::emitObtainingPasswordSuccess(QString password)
{
m_view->emitObtainingPasswordSuccess(password);
}
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,46 @@
#pragma once
#include "../interfaces/module_login_delegate_interface.h"
#include "accounts/generated_account.h"
#include "accounts/service_interface.h"
#include "controller.h"
#include "interfaces/module_controller_delegate_interface.h"
#include "interfaces/module_view_delegate_interface.h"
#include "item.h"
#include "module_access_interface.h"
#include "view.h"
#include <QVariant>
namespace Modules
{
namespace Startup
{
namespace Login
{
class Module : public ModuleAccessInterface, ModuleControllerDelegateInterface, ModuleViewDelegateInterface
{
private:
Modules::Startup::ModuleLoginDelegateInterface* m_delegate;
View* m_view;
Controller* m_controller;
bool m_moduleLoaded;
public:
Module(Modules::Startup::ModuleLoginDelegateInterface* d,
// keychainService
Accounts::ServiceInterface* accountsService);
~Module();
void extractImages(Accounts::AccountDto account, QString &thumbnailImage, QString &largeImage);
void load() override;
bool isLoaded() override;
void viewDidLoad() override;
void setSelectedAccount(Item item) override;
void login(QString password) override;
void setupAccountError();
void emitAccountLoginError(QString error) override;
void emitObtainingPasswordError(QString errorDescription) override;
void emitObtainingPasswordSuccess(QString password) override;
};
}; // namespace Login
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,26 @@
#pragma once
#include <stdexcept>
namespace Modules
{
namespace Startup
{
namespace Login
{
class ModuleAccessInterface
{
public:
virtual void load()
{
throw std::domain_error("Not implemented");
}
virtual bool isLoaded()
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Login
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,44 @@
#include "selected_account.h"
#include <QDebug>
#include <QObject>
namespace Modules
{
namespace Startup
{
namespace Login
{
SelectedAccount::SelectedAccount(QObject* parent)
: QObject(parent)
{ }
void SelectedAccount::setSelectedAccountData(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();
}
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,39 @@
#pragma once
#include <QObject>
#include <QString>
#include "item.h"
namespace Modules
{
namespace Startup
{
namespace 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);
private:
Item m_item;
public slots:
void setSelectedAccountData(Item item);
QString getName();
QString getIdenticon();
QString getKeyUid();
QString getThumbnailImage();
QString getLargeImage();
};
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,83 @@
#include "view.h"
#include "interfaces/module_view_delegate_interface.h"
#include "model.h"
#include "selected_account.h"
#include <QDebug>
#include <QObject>
namespace Modules
{
namespace Startup
{
namespace Login
{
View::View(ModuleViewDelegateInterface* d, QObject* parent)
: QObject(parent)
{
m_delegate = d;
m_model = new Model();
m_selectedAccount = new SelectedAccount();
}
View::~View()
{
delete m_model;
delete m_selectedAccount;
}
void View::load()
{
m_delegate->viewDidLoad();
}
Model* View::getModel()
{
return m_model;
}
SelectedAccount* View::getSelectedAccount()
{
return m_selectedAccount;
}
void View::setSelectedAccount(Item item)
{
m_selectedAccount->setSelectedAccountData(item);
View::selectedAccountChanged();
}
void View::setSelectedAccountByIndex(int index)
{
Item item = m_model->getItemAtIndex(index);
m_delegate->setSelectedAccount(item);
}
void View::setModelItems(QVector<Item> accounts)
{
m_model->setItems(accounts);
View::modelChanged();
}
void View::login(QString password)
{
m_delegate->login(password);
}
void View::emitAccountLoginError(QString error)
{
emit View::accountLoginError(error);
}
void View::emitObtainingPasswordError(QString errorDescription)
{
emit View::obtainingPasswordError(errorDescription);
}
void View::emitObtainingPasswordSuccess(QString password)
{
emit View::obtainingPasswordSuccess(password);
}
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,53 @@
#pragma once
#include "interfaces/module_view_delegate_interface.h"
#include "model.h"
#include "selected_account.h"
#include <QObject>
#include <QString>
#include <memory>
namespace Modules
{
namespace Startup
{
namespace Login
{
class View : public QObject
{
Q_OBJECT
Q_PROPERTY(SelectedAccount* selectedAccount READ getSelectedAccount NOTIFY selectedAccountChanged)
Q_PROPERTY(Model* accountsModel READ getModel NOTIFY modelChanged)
public:
explicit View(ModuleViewDelegateInterface* d, QObject* parent = nullptr);
~View();
void load();
signals:
void selectedAccountChanged();
void modelChanged();
void accountLoginError(QString error);
void obtainingPasswordError(QString errorDescription);
void obtainingPasswordSuccess(QString password);
private:
ModuleViewDelegateInterface* m_delegate;
Model* m_model;
SelectedAccount* m_selectedAccount;
public slots:
Model* getModel();
SelectedAccount* getSelectedAccount();
void setSelectedAccount(Item item);
void setSelectedAccountByIndex(int index);
void setModelItems(QVector<Item> accounts);
void login(QString password);
void emitAccountLoginError(QString error);
void emitObtainingPasswordError(QString errorDescription);
void emitObtainingPasswordSuccess(QString password);
};
} // namespace Login
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,102 @@
#include "module.h"
#include "accounts/service_interface.h"
#include "controller.h"
#include "modules/startup/login/module.h"
#include "modules/startup/onboarding/module.h"
#include "singleton.h"
#include "view.h"
#include <QDebug>
#include <QObject>
#include <QQmlContext>
#include <QVariant>
namespace Modules
{
namespace Startup
{
Module::Module(AppControllerDelegate* d,
/*keychainService,*/
Accounts::ServiceInterface* accountsService)
{
m_delegate = d;
m_controller = new Controller(this, accountsService);
m_view = new View(this);
// Submodules
m_onboardingModule = new Modules::Startup::Onboarding::Module(this, accountsService);
m_loginModule = new Modules::Startup::Login::Module(this, /*keychainService, */ accountsService);
}
Module::~Module()
{
delete m_controller;
delete m_view;
delete m_onboardingModule;
delete m_loginModule;
}
void Module::load()
{
Global::Singleton::instance()->engine()->rootContext()->setContextProperty("startupModule", m_view);
m_controller->init();
m_view->load();
AppState initialAppState(AppState::OnboardingState);
if(!m_controller->shouldStartWithOnboardingScreen())
{
initialAppState = AppState::LoginState;
}
m_view->setAppState(initialAppState);
m_onboardingModule->load();
m_loginModule->load();
}
void Module::checkIfModuleDidLoad()
{
if(!m_onboardingModule->isLoaded())
{
return;
}
if(!m_loginModule->isLoaded())
{
return;
}
m_delegate->startupDidLoad();
}
void Module::viewDidLoad()
{
Module::checkIfModuleDidLoad();
}
void Module::onboardingDidLoad()
{
Module::checkIfModuleDidLoad();
}
void Module::loginDidLoad()
{
Module::checkIfModuleDidLoad();
}
void Module::userLoggedIn()
{
m_delegate->userLoggedIn();
}
void Module::moveToAppState()
{
m_view->setAppState(AppState::MainAppState);
}
void Module::emitLogOut()
{
m_view->emitLogOut();
}
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,48 @@
#pragma once
#include "accounts/service_interface.h"
#include "app_controller_delegate.h"
#include "controller.h"
#include "interfaces/module_controller_delegate_interface.h"
#include "interfaces/module_login_delegate_interface.h"
#include "interfaces/module_onboarding_delegate_interface.h"
#include "interfaces/module_view_delegate_interface.h"
#include "login/module_access_interface.h"
#include "module_access_interface.h"
#include "onboarding/module_access_interface.h"
#include "view.h"
#include <QVariant>
namespace Modules
{
namespace Startup
{
class Module : public ModuleAccessInterface,
ModuleOnboardingDelegateInterface,
ModuleLoginDelegateInterface,
ModuleControllerDelegateInterface,
ModuleViewDelegateInterface
{
private:
AppControllerDelegate* m_delegate;
View* m_view;
Controller* m_controller;
Modules::Startup::Onboarding::ModuleAccessInterface* m_onboardingModule;
Modules::Startup::Login::ModuleAccessInterface* m_loginModule;
public:
Module(AppControllerDelegate* d,
/*keychainService,*/ Accounts::ServiceInterface* accountsService);
~Module();
void load() override;
void checkIfModuleDidLoad();
void viewDidLoad() override;
void onboardingDidLoad();
void loginDidLoad();
void userLoggedIn() override;
void moveToAppState() override;
void emitLogOut() override;
};
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,23 @@
#pragma once
#include <stdexcept>
namespace Modules
{
namespace Startup
{
class ModuleAccessInterface
{
public:
virtual void load()
{
throw std::domain_error("Not implemented");
}
virtual void moveToAppState()
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,80 @@
#include "controller.h"
#include "accounts/generated_account.h"
#include "accounts/service_interface.h"
#include "interfaces/module_controller_delegate_interface.h"
#include "signals.h"
#include <QDebug>
#include <QString>
#include <QVector>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
Controller::Controller(ModuleControllerDelegateInterface* d,
Accounts::ServiceInterface* accountsService,
QObject* parent)
: QObject(parent)
, m_accountsService(accountsService)
, m_delegate(d)
{ }
void Controller::init()
{
QObject::connect(Signals::Manager::instance(), &Signals::Manager::nodeLogin, this, &Controller::onLogin);
}
void Controller::onLogin(Signals::NodeSignal signal)
{
if(!signal.error.isEmpty())
{
m_delegate->setupAccountError();
}
}
QVector<Accounts::GeneratedAccountDto> Controller::getGeneratedAccounts()
{
return m_accountsService->generatedAccounts();
}
Accounts::GeneratedAccountDto Controller::getImportedAccount()
{
return m_accountsService->getImportedAccount();
}
void Controller::setSelectedAccountByIndex(int index)
{
auto accounts = Controller::getGeneratedAccounts();
m_selectedAccountId = accounts[index].id;
}
void Controller::storeSelectedAccountAndLogin(QString password)
{
if(!m_accountsService->setupAccount(m_selectedAccountId, password))
{
m_delegate->setupAccountError();
}
}
QString Controller::validateMnemonic(QString mnemonic)
{
return m_accountsService->validateMnemonic(mnemonic);
}
void Controller::importMnemonic(QString mnemonic)
{
if(m_accountsService->importMnemonic(mnemonic))
{
m_selectedAccountId = Controller::getImportedAccount().id;
m_delegate->importAccountSuccess();
}
else
{
m_delegate->importAccountError();
}
}
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,41 @@
#pragma once
#include "accounts/generated_account.h"
#include "accounts/service_interface.h"
#include "controller_interface.h"
#include "interfaces/module_controller_delegate_interface.h"
#include "signals.h"
#include <QObject>
#include <QString>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
class Controller : public QObject, ControllerInterface
{
Q_OBJECT
public:
Controller(ModuleControllerDelegateInterface* d,
Accounts::ServiceInterface* accountsService,
QObject* parent = nullptr);
void init() override;
QVector<Accounts::GeneratedAccountDto> getGeneratedAccounts() override;
Accounts::GeneratedAccountDto getImportedAccount() override;
void setSelectedAccountByIndex(int index) override;
void storeSelectedAccountAndLogin(QString password) override;
QString validateMnemonic(QString mnemonic) override;
void importMnemonic(QString mnemonic) override;
void onLogin(Signals::NodeSignal signal);
private:
Accounts::ServiceInterface* m_accountsService;
ModuleControllerDelegateInterface* m_delegate;
QString m_selectedAccountId;
};
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,48 @@
#pragma once
#include "accounts/generated_account.h"
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
// Abstract class for any input/interaction with this module.
class ControllerInterface
{
public:
virtual void init()
{
throw std::domain_error("Not implemented");
}
virtual QVector<Accounts::GeneratedAccountDto> getGeneratedAccounts()
{
throw std::domain_error("Not implemented");
}
virtual void setSelectedAccountByIndex(int index)
{
throw std::domain_error("Not implemented");
}
virtual void storeSelectedAccountAndLogin(QString password)
{
throw std::domain_error("Not implemented");
}
virtual Accounts::GeneratedAccountDto getImportedAccount()
{
throw std::domain_error("Not implemented");
}
virtual QString validateMnemonic(QString mnemonic)
{
throw std::domain_error("Not implemented");
}
virtual void importMnemonic(QString mnemonic)
{
throw std::domain_error("Not implemented");
}
};
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,34 @@
#pragma once
#include "accounts/service_interface.h"
#include "app_controller_delegate.h"
#include "controller.h"
#include <stdexcept>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
class ModuleControllerDelegateInterface
{
public:
virtual void setupAccountError()
{
throw std::domain_error("Not implemented");
}
virtual void importAccountError()
{
throw std::domain_error("Not implemented");
}
virtual void importAccountSuccess()
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Onboarding
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,47 @@
#pragma once
#include "accounts/generated_account.h"
#include <QString>
#include <stdexcept>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
class ModuleViewDelegateInterface
{
public:
virtual void viewDidLoad()
{
throw std::domain_error("Not implemented");
}
virtual void setSelectedAccountByIndex(int index)
{
throw std::domain_error("Not implemented");
}
virtual void storeSelectedAccountAndLogin(QString password)
{
throw std::domain_error("Not implemented");
}
virtual Accounts::GeneratedAccountDto getImportedAccount()
{
throw std::domain_error("Not implemented");
}
virtual QString validateMnemonic(QString mnemonic)
{
throw std::domain_error("Not implemented");
}
virtual void importMnemonic(QString mnemonic)
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Onboarding
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,39 @@
#include "item.h"
#include <QString>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
Item::Item(QString id, QString alias, QString identicon, QString address, QString keyUid)
: m_id(id)
, m_alias(alias)
, m_identicon(identicon)
, m_address(address)
, m_keyUid(keyUid)
{ }
QString Item::getId()
{
return m_id;
}
QString Item::getAlias()
{
return m_alias;
}
QString Item::getIdenticon()
{
return m_identicon;
}
QString Item::getAddress()
{
return m_address;
}
QString Item::getKeyUid()
{
return m_keyUid;
}
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,30 @@
#pragma once
#include <QString>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
class Item
{
private:
QString m_id;
QString m_alias;
QString m_identicon;
QString m_address;
QString m_keyUid;
public:
Item(QString id, QString alias, QString identicon, QString address, QString keyUid);
QString getId();
QString getAlias();
QString getIdenticon();
QString getAddress();
QString getKeyUid();
};
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,66 @@
#include "model.h"
#include <QAbstractListModel>
#include <QDebug>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
Model::Model(QObject* parent)
: QAbstractListModel(parent)
{ }
QHash<int, QByteArray> Model::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Id] = "accountId";
roles[Alias] = "username";
roles[Identicon] = "identicon";
roles[Address] = "address";
roles[KeyUid] = "keyUid";
return roles;
}
int Model::rowCount(const QModelIndex& parent = QModelIndex()) const
{
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 QVariant(item.getId());
case Alias: return QVariant(item.getAlias());
case Identicon: return QVariant(item.getIdenticon());
case Address: return QVariant(item.getAddress());
case KeyUid: return QVariant(item.getKeyUid());
}
return QVariant();
}
void Model::setItems(QVector<Item> items)
{
beginResetModel();
m_items = items;
endResetModel();
}
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,40 @@
#pragma once
#include <QAbstractListModel>
#include <QHash>
#include <QVector>
#include "item.h"
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
class Model : public QAbstractListModel
{
Q_OBJECT
public:
enum ModelRole
{
Id = Qt::UserRole + 1,
Alias = Qt::UserRole + 2,
Identicon = Qt::UserRole + 3,
Address = Qt::UserRole + 4,
KeyUid = Qt::UserRole + 5
};
explicit Model(QObject* parent = nullptr);
QHash<int, QByteArray> roleNames() const;
virtual int rowCount(const QModelIndex&) const;
virtual QVariant data(const QModelIndex& index, int role) const;
void setItems(QVector<Item> items);
private:
QVector<Item> m_items;
};
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,101 @@
#include "module.h"
#include "accounts/generated_account.h"
#include "accounts/service_interface.h"
#include "../interfaces/module_onboarding_delegate_interface.h"
#include "controller.h"
#include "singleton.h"
#include "view.h"
#include <QObject>
#include <QQmlContext>
#include <QVariant>
#include <QDebug>
#include <iostream>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
Module::Module(Modules::Startup::ModuleOnboardingDelegateInterface* d, Accounts::ServiceInterface* accountsService)
{
m_delegate = d;
m_controller = new Controller(this, accountsService);
m_view = new View(this);
m_moduleLoaded = false;
}
Module::~Module()
{
delete m_controller;
delete m_view;
}
void Module::load()
{
Global::Singleton::instance()->engine()->rootContext()->setContextProperty("onboardingModule", m_view);
m_controller->init();
m_view->load();
QVector<Accounts::GeneratedAccountDto> gAcc = m_controller->getGeneratedAccounts();
QVector<Item> 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(int index)
{
m_controller->setSelectedAccountByIndex(index);
}
void Module::storeSelectedAccountAndLogin(QString password)
{
m_controller->storeSelectedAccountAndLogin(password);
}
void Module::setupAccountError()
{
m_view->setupAccountError();
}
Accounts::GeneratedAccountDto Module::getImportedAccount()
{
return m_controller->getImportedAccount();
}
QString Module::validateMnemonic(QString mnemonic)
{
return m_controller->validateMnemonic(mnemonic);
}
void Module::importMnemonic(QString mnemonic)
{
m_controller->importMnemonic(mnemonic);
}
void Module::importAccountError()
{
m_view->importAccountError();
}
void Module::importAccountSuccess()
{
m_view->importAccountSuccess();
}
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,44 @@
#pragma once
#include "../interfaces/module_onboarding_delegate_interface.h"
#include "accounts/generated_account.h"
#include "accounts/service_interface.h"
#include "controller.h"
#include "interfaces/module_controller_delegate_interface.h"
#include "interfaces/module_view_delegate_interface.h"
#include "module_access_interface.h"
#include "view.h"
#include <QVariant>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
class Module : public ModuleAccessInterface, ModuleControllerDelegateInterface, ModuleViewDelegateInterface
{
private:
Modules::Startup::ModuleOnboardingDelegateInterface* m_delegate;
View* m_view;
Controller* m_controller;
bool m_moduleLoaded;
public:
Module(Modules::Startup::ModuleOnboardingDelegateInterface* d, Accounts::ServiceInterface* accountsService);
~Module();
void load() override;
bool isLoaded() override;
void viewDidLoad() override;
void setSelectedAccountByIndex(int index) override;
void storeSelectedAccountAndLogin(QString password) override;
void setupAccountError() override;
Accounts::GeneratedAccountDto getImportedAccount() override;
QString validateMnemonic(QString mnemonic) override;
void importMnemonic(QString mnemonic) override;
void importAccountError() override;
void importAccountSuccess() override;
};
}; // namespace Onboarding
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,26 @@
#pragma once
#include <stdexcept>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
class ModuleAccessInterface
{
public:
virtual void load()
{
throw std::domain_error("Not implemented");
}
virtual bool isLoaded()
{
throw std::domain_error("Not implemented");
}
};
}; // namespace Onboarding
}; // namespace Startup
}; // namespace Modules

View File

@ -0,0 +1,93 @@
#include "view.h"
#include "interfaces/module_view_delegate_interface.h"
#include <QObject>
#include <QDebug>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
View::View(ModuleViewDelegateInterface* d, QObject* parent)
: QObject(parent)
{
m_delegate = d;
m_model = new Model();
}
View::~View()
{
delete m_model;
}
void View::load()
{
m_delegate->viewDidLoad();
}
Model* View::getModel()
{
return m_model;
}
void View::setAccountList(QVector<Item> accounts)
{
m_model->setItems(accounts);
View::modelChanged();
}
QString View::getImportedAccountIdenticon()
{
return m_delegate->getImportedAccount().identicon;
}
QString View::getImportedAccountAlias()
{
return m_delegate->getImportedAccount().alias;
}
QString View::getImportedAccountAddress()
{
return m_delegate->getImportedAccount().address;
}
void View::setSelectedAccountByIndex(int index)
{
m_delegate->setSelectedAccountByIndex(index);
}
void View::storeSelectedAccountAndLogin(QString password)
{
m_delegate->storeSelectedAccountAndLogin(password);
}
void View::setupAccountError()
{
View::accountSetupError();
}
QString View::validateMnemonic(QString mnemonic)
{
return m_delegate->validateMnemonic(mnemonic);
}
void View::importMnemonic(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
View::accountImportError();
}
void View::importAccountSuccess()
{
View::importedAccountChanged();
}
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,55 @@
#pragma once
#include "interfaces/module_view_delegate_interface.h"
#include "model.h"
#include <QObject>
#include <QString>
#include <memory>
namespace Modules
{
namespace Startup
{
namespace Onboarding
{
class View : public QObject
{
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(ModuleViewDelegateInterface* d, QObject* parent = nullptr);
~View();
void load();
signals:
void modelChanged();
void importedAccountChanged();
void accountSetupError();
void accountImportError();
private:
ModuleViewDelegateInterface* m_delegate;
Model* m_model;
public slots:
Model* getModel();
void setAccountList(QVector<Item> accounts);
QString getImportedAccountIdenticon();
QString getImportedAccountAlias();
QString getImportedAccountAddress();
void setSelectedAccountByIndex(int index);
void storeSelectedAccountAndLogin(QString password);
QString validateMnemonic(QString mnemonic);
void importMnemonic(QString mnemonic);
void importAccountError();
void setupAccountError();
void importAccountSuccess();
};
} // namespace Onboarding
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,45 @@
#include "view.h"
#include "interfaces/module_view_delegate_interface.h"
#include <QObject>
namespace Modules
{
namespace Startup
{
View::View(ModuleViewDelegateInterface* d, QObject* parent)
: QObject(parent)
, m_appState(AppState::OnboardingState)
{
m_delegate = d;
}
void View::load()
{
// In some point, here, we will setup some exposed main module related things.
m_delegate->viewDidLoad();
}
int View::getAppState()
{
return static_cast<int>(m_appState);
}
void View::setAppState(AppState state)
{
if(m_appState == state)
{
return;
}
m_appState = state;
appStateChanged(static_cast<int>(m_appState));
}
void View::emitLogOut()
{
logOut();
}
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,41 @@
#pragma once
#include "interfaces/module_view_delegate_interface.h"
#include <QObject>
namespace Modules
{
namespace Startup
{
enum AppState
{
OnboardingState = 0,
LoginState = 1,
MainAppState = 2
// TODO: is Pending
};
class View : public QObject
{
Q_OBJECT
Q_PROPERTY(int appState READ getAppState NOTIFY appStateChanged)
public:
explicit View(ModuleViewDelegateInterface* d, QObject* parent = nullptr);
void emitLogOut();
void setAppState(AppState state);
void load();
signals:
void appStateChanged(int state);
void logOut();
private:
ModuleViewDelegateInterface* m_delegate;
AppState m_appState;
public slots:
int getAppState();
};
} // namespace Startup
} // namespace Modules

View File

@ -0,0 +1,34 @@
add_library(app_service
constants.cpp
service/accounts/dto/account.cpp
service/accounts/dto/generated_account.cpp
service/accounts/service.cpp
)
target_include_directories(app_service
PUBLIC
include
)
# default token is a free-tier token with limited capabilities and usage
# limits; setup your own infura key with
# cmake -DINFURA_KEY=infura_key_goes_here ..
if( "${INFURA_KEY}" STREQUAL "")
message("-- Using default Infura key")
file (STRINGS "../../resources/infura_key" INFURA_KEY)
else()
message("-- Using custom Infura key")
endif()
# Build constants
target_compile_definitions(app_service
PRIVATE
INFURA_KEY="${INFURA_KEY}"
)
target_link_libraries(app_service
PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Quick
backend
)

View File

@ -0,0 +1,24 @@
#include "constants.h"
#include <QDir>
#include <QFileInfo>
#include <QStandardPaths>
#include <QString>
// TODO: merge with constants from backend/
QString Constants::applicationPath(QString path)
{
return QFileInfo(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + path).absoluteFilePath();
}
QString Constants::tmpPath(QString path)
{
return QFileInfo(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + path).absoluteFilePath();
}
QString Constants::cachePath(QString path)
{
return QFileInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + path).absoluteFilePath();
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <QJsonValue>
#include <QString>
#include <QVector>
namespace Accounts
{
class Image
{
public:
QString keyUid;
QString imgType;
QString uri;
int width;
int height;
int fileSize;
int resizeTarget;
};
class AccountDto
{
public:
QString name;
long timestamp;
QString identicon;
QString keycardPairing;
QString keyUid;
QVector<Image> images;
bool isValid();
};
Image toImage(const QJsonValue jsonObj);
AccountDto toAccountDto(const QJsonValue jsonObj);
} // namespace Accounts

View File

@ -0,0 +1,47 @@
#pragma once
#include <QString>
#include <QJsonDocument>
namespace Accounts
{
class DerivedAccountDetails
{
public:
QString publicKey;
QString address;
QString derivationPath;
};
class DerivedAccounts
{
public:
DerivedAccountDetails whisper;
DerivedAccountDetails walletRoot;
DerivedAccountDetails defaultWallet;
DerivedAccountDetails eip1581;
};
class GeneratedAccountDto
{
public:
QString id;
QString publicKey;
QString address;
QString keyUid;
QString mnemonic;
DerivedAccounts derivedAccounts;
// The following two are set additionally.
QString alias;
QString identicon;
bool isValid();
};
DerivedAccountDetails toDerivedAccountDetails(const QJsonValue jsonObj, QString derivationPath);
DerivedAccounts toDerivedAccounts(const QJsonObject jsonObj);
GeneratedAccountDto toGeneratedAccountDto(const QJsonValue jsonObj);
} // namespace Accounts

View File

@ -0,0 +1,72 @@
#pragma once
#include "account.h"
#include "generated_account.h"
#include "service_interface.h"
#include <QString>
#include <QVector>
namespace Accounts
{
class Service : public ServiceInterface
{
private:
QVector<GeneratedAccountDto> m_generatedAccounts;
bool m_isFirstTimeAccountLogin;
AccountDto m_loggedInAccount;
GeneratedAccountDto m_importedAccount;
public:
Service();
void init() override;
virtual QVector<AccountDto> openedAccounts() override;
QVector<GeneratedAccountDto> generatedAccounts() override;
bool setupAccount(QString accountId, QString password) override;
AccountDto getLoggedInAccount() override;
GeneratedAccountDto getImportedAccount() override;
bool isFirstTimeAccountLogin() override;
QString validateMnemonic(QString mnemonic) override;
bool importMnemonic(QString mnemonic) override;
QString login(AccountDto account, QString password) override;
void clear() override;
QString generateAlias(QString publicKey) override;
QString generateIdenticon(QString publicKey) override;
bool verifyAccountPassword(QString account, QString password) override;
DerivedAccounts storeDerivedAccounts(QString accountId, QString hashedPassword, QVector<QString> paths);
QJsonObject getAccountDataForAccountId(QString accountId);
QJsonArray getSubaccountDataForAccountId(QString accountId);
QJsonObject getAccountSettings(QString accountId, QString installationId);
QJsonObject getDefaultNodeConfig(QString installationId);
QJsonObject prepareAccountJsonObject(const GeneratedAccountDto account);
QJsonArray prepareSubaccountJsonObject(GeneratedAccountDto account);
QJsonObject prepareAccountSettingsJsonObject(const GeneratedAccountDto account, QString installationId);
AccountDto saveAccountAndLogin(
QString hashedPassword, QJsonObject account, QJsonArray subaccounts, QJsonObject settings, QJsonObject config);
};
} // namespace Accounts

View File

@ -0,0 +1,83 @@
#pragma once
#include "../app_service.h"
#include "account.h"
#include "generated_account.h"
#include <QJsonValue>
#include <QString>
#include <QVector>
#include <stdexcept>
namespace Accounts
{
class ServiceInterface : public AppService
{
public:
virtual QVector<AccountDto> openedAccounts()
{
throw std::domain_error("Not implemented");
}
virtual QVector<GeneratedAccountDto> generatedAccounts()
{
throw std::domain_error("Not implemented");
}
virtual bool setupAccount(QString accountId, QString password)
{
throw std::domain_error("Not implemented");
}
virtual AccountDto getLoggedInAccount()
{
throw std::domain_error("Not implemented");
}
virtual GeneratedAccountDto getImportedAccount()
{
throw std::domain_error("Not implemented");
}
virtual bool isFirstTimeAccountLogin()
{
throw std::domain_error("Not implemented");
}
virtual QString validateMnemonic(QString mnemonic)
{
throw std::domain_error("Not implemented");
}
virtual bool importMnemonic(QString mnemonic)
{
throw std::domain_error("Not implemented");
}
virtual QString login(AccountDto account, QString password)
{
throw std::domain_error("Not implemented");
}
virtual void clear()
{
throw std::domain_error("Not implemented");
}
virtual QString generateAlias(QString publicKey)
{
throw std::domain_error("Not implemented");
}
virtual QString generateIdenticon(QString publicKey)
{
throw std::domain_error("Not implemented");
}
virtual bool verifyAccountPassword(QString account, QString password)
{
throw std::domain_error("Not implemented");
}
};
} // namespace Accounts

View File

@ -0,0 +1,12 @@
#pragma once
#include <stdexcept>
class AppService
{
public:
virtual void init()
{
throw std::domain_error("Not implemented");
}
};

View File

@ -0,0 +1,40 @@
#pragma once
#include <QString>
namespace Constants
{
namespace Fleet
{
const QString Prod = "eth.prod";
const QString Staging = "eth.staging";
const QString Test = "eth.test";
const QString WakuV2Prod = "wakuv2.prod";
const QString WakuV2Test = "wakuv2.test";
const QString GoWakuTest = "go-waku.test";
}; // namespace Fleet
namespace FleetNodes
{
const QString Bootnodes = "boot";
const QString Mailservers = "mail";
const QString Rendezvous = "rendezvous";
const QString Whisper = "whisper";
const QString Waku = "waku";
const QString LibP2P = "libp2p";
const QString Websocket = "websocket";
} // namespace FleetNodes
const QString DefaultNetworkName = "mainnet_rpc";
//const DEFAULT_NETWORKS_IDS* = @["mainnet_rpc", "testnet_rpc", "rinkeby_rpc", "goerli_rpc", "xdai_rpc", "poa_rpc" ]
const QString DataDir = "/data";
const QString Keystore = "/data/keystore";
QString applicationPath(QString path = "");
QString tmpPath(QString path = "");
QString cachePath(QString path = "");
} // namespace Constants

View File

@ -0,0 +1,39 @@
#include <QString>
#include <array>
const std::array phrases{
"area", "army", "atom", "aunt", "babe", "baby", "back", "bail", "bait", "bake", "ball", "band", "bank", "barn", "base", "bass", "bath", "bead",
"beak", "beam", "bean", "bear", "beat", "beef", "beer", "beet", "bell", "belt", "bend", "bike", "bill", "bird", "bite", "blow", "blue", "boar",
"boat", "body", "bolt", "bomb", "bone", "book", "boot", "bore", "boss", "bowl", "brow", "bulb", "bull", "burn", "bush", "bust", "cafe", "cake",
"calf", "call", "calm", "camp", "cane", "cape", "card", "care", "carp", "cart", "case", "cash", "cast", "cave", "cell", "cent", "chap", "chef",
"chin", "chip", "chop", "chub", "chug", "city", "clam", "clef", "clip", "club", "clue", "coal", "coat", "code", "coil", "coin", "coke", "cold",
"colt", "comb", "cone", "cook", "cope", "copy", "cord", "cork", "corn", "cost", "crab", "craw", "crew", "crib", "crop", "crow", "curl", "cyst",
"dame", "dare", "dark", "dart", "dash", "data", "date", "dead", "deal", "dear", "debt", "deck", "deep", "deer", "desk", "dhow", "diet", "dill",
"dime", "dirt", "dish", "disk", "dock", "doll", "door", "dory", "drag", "draw", "drop", "drug", "drum", "duck", "dump", "dust", "duty", "ease",
"east", "eave", "eddy", "edge", "envy", "epee", "exam", "exit", "face", "fact", "fail", "fall", "fame", "fang", "farm", "fawn", "fear", "feed",
"feel", "feet", "file", "fill", "film", "find", "fine", "fire", "fish", "flag", "flat", "flax", "flow", "foam", "fold", "font", "food", "foot",
"fork", "form", "fort", "fowl", "frog", "fuel", "full", "gain", "gale", "galn", "game", "garb", "gate", "gear", "gene", "gift", "girl", "give",
"glad", "glen", "glue", "glut", "goal", "goat", "gold", "golf", "gong", "good", "gown", "grab", "gram", "gray", "grey", "grip", "grit", "gyro",
"hail", "hair", "half", "hall", "hand", "hang", "harm", "harp", "hate", "hawk", "head", "heat", "heel", "hell", "helo", "help", "hemp", "herb",
"hide", "high", "hill", "hire", "hive", "hold", "hole", "home", "hood", "hoof", "hook", "hope", "hops", "horn", "hose", "host", "hour", "hunt",
"hurt", "icon", "idea", "inch", "iris", "iron", "item", "jail", "jeep", "jeff", "joey", "join", "joke", "judo", "jump", "junk", "jury", "jute",
"kale", "keep", "kick", "kill", "kilt", "kind", "king", "kiss", "kite", "knee", "knot", "lace", "lack", "lady", "lake", "lamb", "lamp", "land",
"lark", "lava", "lawn", "lead", "leaf", "leek", "lier", "life", "lift", "lily", "limo", "line", "link", "lion", "lisa", "list", "load", "loaf",
"loan", "lock", "loft", "long", "look", "loss", "lout", "love", "luck", "lung", "lute", "lynx", "lyre", "maid", "mail", "main", "make", "male",
"mall", "manx", "many", "mare", "mark", "mask", "mass", "mate", "math", "meal", "meat", "meet", "menu", "mess", "mice", "midi", "mile", "milk",
"mime", "mind", "mine", "mini", "mint", "miss", "mist", "moat", "mode", "mole", "mood", "moon", "most", "moth", "move", "mule", "mutt", "nail",
"name", "neat", "neck", "need", "neon", "nest", "news", "node", "nose", "note", "oboe", "okra", "open", "oval", "oven", "oxen", "pace", "pack",
"page", "pail", "pain", "pair", "palm", "pard", "park", "part", "pass", "past", "path", "peak", "pear", "peen", "peer", "pelt", "perp", "pest",
"pick", "pier", "pike", "pile", "pimp", "pine", "ping", "pink", "pint", "pipe", "piss", "pith", "plan", "play", "plot", "plow", "poem", "poet",
"pole", "polo", "pond", "pony", "poof", "pool", "port", "post", "prow", "pull", "puma", "pump", "pupa", "push", "quit", "race", "rack", "raft",
"rage", "rail", "rain", "rake", "rank", "rate", "read", "rear", "reef", "rent", "rest", "rice", "rich", "ride", "ring", "rise", "risk", "road",
"robe", "rock", "role", "roll", "roof", "room", "root", "rope", "rose", "ruin", "rule", "rush", "ruth", "sack", "safe", "sage", "sail", "sale",
"salt", "sand", "sari", "sash", "save", "scow", "seal", "seat", "seed", "self", "sell", "shed", "shin", "ship", "shoe", "shop", "shot", "show",
"sick", "side", "sign", "silk", "sill", "silo", "sing", "sink", "site", "size", "skin", "sled", "slip", "smog", "snob", "snow", "soap", "sock",
"soda", "sofa", "soft", "soil", "song", "soot", "sort", "soup", "spot", "spur", "stag", "star", "stay", "stem", "step", "stew", "stop", "stud",
"suck", "suit", "swan", "swim", "tail", "tale", "talk", "tank", "tard", "task", "taxi", "team", "tear", "teen", "tell", "temp", "tent", "term",
"test", "text", "thaw", "tile", "till", "time", "tire", "toad", "toga", "togs", "tone", "tool", "toot", "tote", "tour", "town", "tram", "tray",
"tree", "trim", "trip", "tuba", "tube", "tuna", "tune", "turn", "tutu", "twig", "type", "unit", "user", "vane", "vase", "vast", "veal", "veil",
"vein", "vest", "vibe", "view", "vise", "wait", "wake", "walk", "wall", "wash", "wasp", "wave", "wear", "weed", "week", "well", "west", "whip",
"wife", "will", "wind", "wine", "wing", "wire", "wish", "wolf", "wood", "wool", "word", "work", "worm", "wrap", "wren", "yard", "yarn", "yawl",
"year", "yoga", "yoke", "yurt", "zinc", "zone"};

View File

@ -0,0 +1,44 @@
#include "accounts/account.h"
#include "backend/accounts.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QStringList>
bool Accounts::AccountDto::isValid()
{
return name.length() > 0 && keyUid.length() > 0;
}
Accounts::Image Accounts::toImage(const QJsonValue jsonObj)
{
auto result = Accounts::Image();
result.keyUid = jsonObj["keyUid"].toString();
result.imgType = jsonObj["type"].toString();
result.uri = jsonObj["uri"].toString();
result.width = jsonObj["width"].toInt();
result.height = jsonObj["height"].toInt();
result.fileSize = jsonObj["fileSize"].toInt();
result.resizeTarget = jsonObj["resizeTarget"].toInt();
return result;
}
Accounts::AccountDto Accounts::toAccountDto(const QJsonValue jsonObj)
{
auto result = Accounts::AccountDto();
result.name = jsonObj["name"].toString();
result.timestamp = jsonObj["timestamp"].toInt();
result.identicon = jsonObj["identicon"].toString();
result.keycardPairing = jsonObj["keycard-pairing"].toString();
result.keyUid = jsonObj["key-uid"].toString();
foreach(const QJsonValue& value, jsonObj["images"].toArray())
{
result.images << Accounts::toImage(value);
}
return result;
}

View File

@ -0,0 +1,69 @@
#include "accounts/generated_account.h"
#include "backend/accounts.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QStringList>
bool Accounts::GeneratedAccountDto::isValid()
{
return id.length() > 0 && publicKey.length() > 0 && address.length() > 0 && keyUid.length() > 0;
}
Accounts::DerivedAccountDetails Accounts::toDerivedAccountDetails(const QJsonValue jsonObj, QString derivationPath)
{
// Mapping this DTO is not strightforward since only keys are used for id. We
// handle it a bit different.
auto result = Accounts::DerivedAccountDetails();
result.derivationPath = derivationPath;
result.publicKey = jsonObj["publicKey"].toString();
result.address = jsonObj["address"].toString();
return result;
}
Accounts::DerivedAccounts Accounts::toDerivedAccounts(const QJsonObject jsonObj)
{
auto result = Accounts::DerivedAccounts();
foreach(const QString& derivationPath, jsonObj.keys())
{
QJsonValue derivedObj = jsonObj.value(derivationPath);
if(derivationPath == Backend::Accounts::PATH_WHISPER)
{
result.whisper = Accounts::toDerivedAccountDetails(derivedObj, derivationPath);
}
else if(derivationPath == Backend::Accounts::PATH_WALLET_ROOT)
{
result.walletRoot = Accounts::toDerivedAccountDetails(derivedObj, derivationPath);
}
else if(derivationPath == Backend::Accounts::PATH_DEFAULT_WALLET)
{
result.defaultWallet = Accounts::toDerivedAccountDetails(derivedObj, derivationPath);
}
else if(derivationPath == Backend::Accounts::PATH_EIP_1581)
{
result.eip1581 = Accounts::toDerivedAccountDetails(derivedObj, derivationPath);
}
}
return result;
}
Accounts::GeneratedAccountDto Accounts::toGeneratedAccountDto(const QJsonValue jsonObj)
{
auto result = GeneratedAccountDto();
result.id = jsonObj["id"].toString();
result.address = jsonObj["address"].toString();
result.keyUid = jsonObj["keyUid"].toString();
result.mnemonic = jsonObj["mnemonic"].toString();
result.publicKey = jsonObj["publicKey"].toString();
if(!jsonObj["derived"].isUndefined())
{
result.derivedAccounts = Accounts::toDerivedAccounts(jsonObj["derived"].toObject());
}
return result;
}

View File

@ -0,0 +1,374 @@
#include "accounts/service.h"
#include "accounts/account.h"
#include "accounts/generated_account.h"
#include "accounts/service_interface.h"
#include "app_service.h"
#include "backend/accounts.h"
#include "backend/utils.h"
#include "constants.h"
#include "signing-phrases.h"
#include <QDebug>
#include <QFile>
#include <QJsonArray>
#include <QJsonObject>
#include <QRandomGenerator>
#include <QUuid>
namespace Accounts
{
Service::Service()
: m_isFirstTimeAccountLogin(false)
{ }
const QVector<QString> PATHS{Backend::Accounts::PATH_WALLET_ROOT,
Backend::Accounts::PATH_EIP_1581,
Backend::Accounts::PATH_WHISPER,
Backend::Accounts::PATH_DEFAULT_WALLET};
void Service::init()
{
auto response = Backend::Accounts::generateAddresses(Accounts::PATHS);
foreach(QJsonValue generatedAddressJson, response.m_result)
{
auto gAcc = toGeneratedAccountDto(generatedAddressJson);
gAcc.alias = generateAlias(gAcc.derivedAccounts.whisper.publicKey);
gAcc.identicon = generateIdenticon(gAcc.derivedAccounts.whisper.publicKey);
m_generatedAccounts << gAcc;
}
}
QVector<AccountDto> Service::openedAccounts()
{
// try
auto response = Backend::Accounts::openAccounts(Constants::applicationPath(Constants::DataDir));
QJsonArray multiAccounts = response.m_result;
QVector<AccountDto> result;
foreach(const QJsonValue& value, multiAccounts)
{
result << toAccountDto(value);
}
return result;
//} catch(const std::exception& e){
// error "error: ", methodName="openedAccounts", errName = e.name, errDesription = e.msg
}
QVector<GeneratedAccountDto> Service::generatedAccounts()
{
if(m_generatedAccounts.length() == 0)
{
qWarning("There was some issue initiating account service");
return QVector<GeneratedAccountDto>();
}
return m_generatedAccounts;
}
bool Service::setupAccount(QString accountId, QString password)
{
//try:
QString installationId(QUuid::createUuid().toString(QUuid::WithoutBraces));
QJsonObject accountData(Service::getAccountDataForAccountId(accountId));
QJsonArray subAccountData(Service::getSubaccountDataForAccountId(accountId));
QJsonObject settings(Service::getAccountSettings(accountId, installationId));
QJsonObject nodeConfig(Service::getDefaultNodeConfig(installationId));
// if(accountDataJson.isNil or subaccountDataJson.isNil or settingsJson.isNil or
// nodeConfigJson.isNil):
//let description = "at least one json object is not prepared well"
//error "error: ", methodName="setupAccount", errDesription = description
//return false
QString hashedPassword(Backend::Utils::hashString(password));
Service::storeDerivedAccounts(accountId, hashedPassword, PATHS);
m_loggedInAccount = Service::saveAccountAndLogin(hashedPassword, accountData, subAccountData, settings, nodeConfig);
return Service::getLoggedInAccount().isValid();
//except Exception as e:
// error "error: ", methodName="setupAccount", errName = e.name, errDesription = e.msg
//return false*/
}
AccountDto Service::getLoggedInAccount()
{
return m_loggedInAccount;
}
GeneratedAccountDto Service::getImportedAccount()
{
return m_importedAccount;
}
bool Service::isFirstTimeAccountLogin()
{
return m_isFirstTimeAccountLogin;
}
QString Service::validateMnemonic(QString mnemonic)
{
// TODO:
return "";
}
bool Service::importMnemonic(QString mnemonic)
{
// TODO:
return false;
}
QString Service::login(AccountDto account, QString password)
{
//try:
QString hashedPassword(Backend::Utils::hashString(password));
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;
}
}
auto response = Backend::Accounts::login(
account.name, account.keyUid, hashedPassword, account.identicon, thumbnailImage, largeImage);
// TODO: check response for errors
qDebug() << "Account logged in";
m_loggedInAccount = account;
return "";
//except Exception as e:
//error "error: ", methodName="setupAccount", errName = e.name, errDesription = e.msg
//return e.msg
}
void Service::clear()
{
m_generatedAccounts.clear();
m_loggedInAccount = Accounts::AccountDto();
m_importedAccount = Accounts::GeneratedAccountDto();
m_isFirstTimeAccountLogin = false;
}
QString Service::generateAlias(QString publicKey)
{
return Backend::Accounts::generateAlias(publicKey).m_result;
}
QString Service::generateIdenticon(QString publicKey)
{
return Backend::Accounts::generateIdenticon(publicKey).m_result;
}
bool Service::verifyAccountPassword(QString account, QString password)
{
// TODO:
return false;
}
DerivedAccounts Service::storeDerivedAccounts(QString accountId, QString hashedPassword, QVector<QString> paths)
{
try
{
auto response = Backend::Accounts::storeDerivedAccounts(accountId, hashedPassword, paths);
return toDerivedAccounts(response.m_result);
}
catch(Backend::RpcException& e)
{
qWarning() << e.what();
return DerivedAccounts(); // TODO: should it return empty?
}
}
Accounts::AccountDto Service::saveAccountAndLogin(
QString hashedPassword, QJsonObject account, QJsonArray subaccounts, QJsonObject settings, QJsonObject config)
{
//try:
auto response = Backend::Accounts::saveAccountAndLogin(hashedPassword, account, subaccounts, settings, config);
m_isFirstTimeAccountLogin = true;
return toAccountDto(account);
// except Exception as e:
// error "error: ", methodName="saveAccountAndLogin", errName = e.name, errDesription = e.msg
}
QJsonObject Service::prepareAccountJsonObject(const GeneratedAccountDto account)
{
return QJsonObject{{"name", account.alias},
{"address", account.address},
{"photo-path", account.identicon},
{"identicon", account.identicon},
{"key-uid", account.keyUid},
{"keycard-pairing", QJsonValue()}};
}
QJsonObject Service::getAccountDataForAccountId(QString accountId)
{
foreach(const GeneratedAccountDto& acc, m_generatedAccounts)
{
if(acc.id == accountId)
{
return Service::prepareAccountJsonObject(acc);
}
}
if(m_importedAccount.isValid())
{
if(m_importedAccount.id == accountId)
{
return Service::prepareAccountJsonObject(m_importedAccount);
}
}
}
QJsonArray Service::prepareSubaccountJsonObject(GeneratedAccountDto account)
{
return QJsonArray{QJsonObject{{"public-key", account.derivedAccounts.defaultWallet.publicKey},
{"address", account.derivedAccounts.defaultWallet.address},
{"color", "#4360df"},
{"wallet", true},
{"path", Backend::Accounts::PATH_DEFAULT_WALLET},
{"name", "Status account"}},
QJsonObject{{"public-key", account.derivedAccounts.whisper.publicKey},
{"address", account.derivedAccounts.whisper.address},
{"path", Backend::Accounts::PATH_WHISPER},
{"name", account.alias},
{"identicon", account.identicon},
{"chat", true}}};
}
QJsonArray Service::getSubaccountDataForAccountId(QString accountId)
{
foreach(const GeneratedAccountDto& acc, m_generatedAccounts)
{
if(acc.id == accountId)
{
return prepareSubaccountJsonObject(acc);
}
}
if(m_importedAccount.isValid())
{
if(m_importedAccount.id == accountId)
{
return prepareSubaccountJsonObject(m_importedAccount);
}
}
}
QString generateSigningPhrase(int count)
{
QStringList words;
for(int i = 0; i < count; i++)
{
words.append(phrases[QRandomGenerator::global()->bounded(static_cast<int>(phrases.size()))]);
}
return words.join(" ");
}
QJsonObject Service::prepareAccountSettingsJsonObject(const GeneratedAccountDto account, QString installationId)
{
QFile defaultNetworks(":/resources/default-networks.json");
defaultNetworks.open(QIODevice::ReadOnly);
QString defaultNetworksContent = defaultNetworks.readAll().replace("%INFURA_KEY%", INFURA_KEY);
QJsonArray defaultNetworksJson = QJsonDocument::fromJson(defaultNetworksContent.toUtf8()).array();
return QJsonObject{{"key-uid", account.keyUid},
{"mnemonic", account.mnemonic},
{"public-key", account.derivedAccounts.whisper.publicKey},
{"name", account.alias},
{"address", account.address},
{"eip1581-address", account.derivedAccounts.eip1581.address},
{"dapps-address", account.derivedAccounts.defaultWallet.address},
{"wallet-root-address", account.derivedAccounts.walletRoot.address},
{"preview-privacy?", true},
{"signing-phrase", generateSigningPhrase(3)},
{"log-level", "INFO"},
{"latest-derived-path", 0},
{"networks/networks", defaultNetworksJson},
{"currency", "usd"},
{"identicon", account.identicon},
{"waku-enabled", true},
{"wallet/visible-tokens", {{Constants::DefaultNetworkName, QJsonArray{"SNT"}}}},
{"appearance", 0},
{"networks/current-network", Constants::DefaultNetworkName},
{"installation-id", installationId}};
}
QJsonObject Service::getAccountSettings(QString accountId, QString installationId)
{
foreach(const GeneratedAccountDto& acc, m_generatedAccounts)
if(acc.id == accountId)
{
return Service::prepareAccountSettingsJsonObject(acc, installationId);
}
if(m_importedAccount.isValid())
{
if(m_importedAccount.id == accountId)
{
return Service::prepareAccountSettingsJsonObject(m_importedAccount, installationId);
}
}
}
QJsonArray getNodes(const QJsonObject fleet, QString nodeType)
{
auto nodes = fleet[nodeType].toObject();
QJsonArray result;
for(auto it = nodes.begin(); it != nodes.end(); ++it)
result << *it;
return result;
}
QJsonObject Service::getDefaultNodeConfig(QString installationId)
{
QFile nodeConfig(":/resources/node-config.json");
nodeConfig.open(QIODevice::ReadOnly);
QString nodeConfigContent = nodeConfig.readAll();
nodeConfigContent = nodeConfigContent.replace("%INSTALLATIONID%", installationId);
nodeConfigContent = nodeConfigContent.replace("%INFURA_KEY%", INFURA_KEY);
QJsonObject nodeConfigJson = QJsonDocument::fromJson(nodeConfigContent.toUtf8()).object();
QFile fleets(":/resources/fleets.json");
fleets.open(QIODevice::ReadOnly);
QJsonObject fleetsJson = QJsonDocument::fromJson(fleets.readAll()).object()["fleets"].toObject();
auto fleet = fleetsJson[Constants::Fleet::Prod].toObject();
QJsonObject clusterConfig = nodeConfigJson["ClusterConfig"].toObject();
clusterConfig["Fleet"] = Constants::Fleet::Prod;
clusterConfig["BootNodes"] = getNodes(fleet, Constants::FleetNodes::Bootnodes);
clusterConfig["TrustedMailServers"] = getNodes(fleet, Constants::FleetNodes::Mailservers);
clusterConfig["StaticNodes"] = getNodes(fleet, Constants::FleetNodes::Whisper);
clusterConfig["RendezvousNodes"] = getNodes(fleet, Constants::FleetNodes::Rendezvous);
clusterConfig["RelayNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
clusterConfig["StoreNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
clusterConfig["FilterNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
clusterConfig["LightpushNodes"] = getNodes(fleet, Constants::FleetNodes::Waku);
nodeConfigJson["ClusterConfig"] = clusterConfig;
return nodeConfigJson;
}
} // namespace Accounts

View File

@ -0,0 +1,18 @@
add_library(backend
accounts.cpp
types.cpp
utils.cpp
)
target_include_directories(backend
PUBLIC
include/
)
target_link_libraries(backend
PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Quick
statusgo_shared
)

View File

@ -0,0 +1,97 @@
#include "backend/accounts.h"
#include "backend/types.h"
#include "backend/utils.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QString>
#include <QVector>
#include "libstatus.h"
const int NUMBER_OF_ADDRESSES_TO_GENERATE = 5;
const int MNEMONIC_PHRASE_LENGTH = 12;
Backend::RpcResponse<QJsonArray> Backend::Accounts::generateAddresses(QVector<QString> paths)
{
QJsonObject payload{{"n", NUMBER_OF_ADDRESSES_TO_GENERATE},
{"mnemonicPhraseLength", MNEMONIC_PHRASE_LENGTH},
{"bip32Passphrase", ""},
{"paths", Utils::toJsonArray(paths)}
};
const char* result = MultiAccountGenerateAndDeriveAddresses(Utils::jsonToStr(payload).toUtf8().data());
return Backend::RpcResponse<QJsonArray>(result, QJsonDocument::fromJson(result).array());
}
Backend::RpcResponse<QString> Backend::Accounts::generateIdenticon(QString publicKey)
{
if(!publicKey.isEmpty())
{
auto identicon = QString(Identicon(publicKey.toUtf8().data()));
return Backend::RpcResponse<QString>(identicon, identicon);
}
else
{
throw Backend::RpcException("publicKey can't be empty1");
}
}
Backend::RpcResponse<QString> Backend::Accounts::generateAlias(QString publicKey)
{
if(!publicKey.isEmpty())
{
auto alias = QString(GenerateAlias(publicKey.toUtf8().data()));
return Backend::RpcResponse<QString>(alias, alias);
}
else
{
throw Backend::RpcException("publicKey can't be empty2");
}
}
Backend::RpcResponse<QJsonObject>
Backend::Accounts::storeDerivedAccounts(QString id, QString hashedPassword, QVector<QString> paths)
{
QJsonObject payload{{"accountID", id}, {"paths", Utils::toJsonArray(paths)}, {"password", hashedPassword}};
auto result = MultiAccountStoreDerivedAccounts(Utils::jsonToStr(payload).toUtf8().data());
auto obj = QJsonDocument::fromJson(result).object();
Backend::Utils::throwOnError(obj);
return Backend::RpcResponse<QJsonObject>(result, obj);
}
Backend::RpcResponse<QJsonObject> Backend::Accounts::saveAccountAndLogin(
QString hashedPassword, QJsonObject account, QJsonArray subaccounts, QJsonObject settings, QJsonObject nodeConfig)
{
auto result = SaveAccountAndLogin(Utils::jsonToStr(account).toUtf8().data(),
hashedPassword.toUtf8().data(),
Utils::jsonToStr(settings).toUtf8().data(),
Utils::jsonToStr(nodeConfig).toUtf8().data(),
Utils::jsonToStr(subaccounts).toUtf8().data());
auto obj = QJsonDocument::fromJson(result).object();
Backend::Utils::throwOnError(obj);
return Backend::RpcResponse<QJsonObject>(result, obj);
}
Backend::RpcResponse<QJsonArray> Backend::Accounts::openAccounts(QString path)
{
const char* result = OpenAccounts(path.toUtf8().data());
auto resp = Backend::RpcResponse<QJsonArray>(result, QJsonDocument::fromJson(result).array());
return resp;
}
Backend::RpcResponse<QJsonObject> Backend::Accounts::login(
QString name, QString keyUid, QString hashedPassword, QString identicon, QString thumbnail, QString large)
{
QJsonObject payload{{"name", name}, {"key-uid", keyUid}, {"identityImage", QJsonValue()}, {"identicon", identicon}};
if(!thumbnail.isEmpty() && !large.isEmpty())
{
payload["identityImage"] = QJsonObject{{"thumbnail", thumbnail}, {"large", large}};
}
auto result = Login(Utils::jsonToStr(payload).toUtf8().data(), hashedPassword.toUtf8().data());
auto obj = QJsonDocument::fromJson(result).object();
Backend::Utils::throwOnError(obj);
return Backend::RpcResponse<QJsonObject>(result, obj);
}

View File

@ -0,0 +1,39 @@
#pragma once
#include "backend/types.h"
#include <QJsonArray>
#include <QString>
#include <QVector>
namespace Backend
{
namespace Accounts
{
const QString ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
const QString PATH_WALLET_ROOT = "m/44'/60'/0'/0";
// EIP1581 Root Key, the extended key from which any whisper key/encryption key can be derived
const QString PATH_EIP_1581 = "m/43'/60'/1581'";
// BIP44-0 Wallet key, the default wallet key
const QString PATH_DEFAULT_WALLET = PATH_WALLET_ROOT + "/0";
// EIP1581 Chat Key 0, the default whisper key
const QString PATH_WHISPER = PATH_EIP_1581 + "/0'/0";
RpcResponse<QJsonArray> generateAddresses(QVector<QString> paths);
RpcResponse<QString> generateIdenticon(QString publicKey);
RpcResponse<QString> generateAlias(QString publicKey);
RpcResponse<QJsonObject> storeDerivedAccounts(QString accountId, QString hashedPassword, QVector<QString> paths);
RpcResponse<QJsonObject> saveAccountAndLogin(
QString hashedPassword, QJsonObject account, QJsonArray subaccounts, QJsonObject settings, QJsonObject nodeConfig);
RpcResponse<QJsonArray> openAccounts(QString path);
RpcResponse<QJsonObject>
login(QString name, QString keyUid, QString hashedPassword, QString identicon, QString thumbnail, QString large);
} // namespace Accounts
} // namespace Backend

View File

@ -0,0 +1,50 @@
#pragma once
#include <QString>
#include <iostream>
using namespace std;
namespace Backend
{
const QString GENERATED = "generated";
const QString SEED = "seed";
const QString KEY = "key";
const QString WATCH = "watch";
struct RpcException : public std::exception
{
private:
std::string m_message;
public:
explicit RpcException(const std::string& message);
const char* what() const throw();
};
class RpcError
{
public:
int m_code;
QString m_message;
friend ostream& operator<<(ostream& os, Backend::RpcError& r);
};
template <typename T>
class RpcResponse
{
public:
QString m_jsonrpc;
T m_result;
int m_id;
RpcError m_error;
public:
RpcResponse(QString jsonrpc, T result)
: m_jsonrpc(jsonrpc)
, m_result(result)
{ }
};
} // namespace Backend

View File

@ -0,0 +1,20 @@
#pragma once
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QVector>
namespace Backend
{
class Utils
{
public:
static QString hashString(QString str);
static QString jsonToStr(QJsonObject obj);
static QString jsonToStr(QJsonArray arr);
static QJsonArray toJsonArray(const QVector<QString>& value);
static QVector<QString> toStringVector(const QJsonArray& arr);
static void throwOnError(QJsonObject response);
};
} // namespace Backend

19
src-cpp/backend/types.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "backend/types.h"
#include <QString>
using namespace std;
ostream& operator<<(ostream& os, const Backend::RpcError& r)
{
return (os << "RpcError(\n code: " << r.m_code << "\n message: " << r.m_message.toStdString() << "\n)"
<< std::endl);
}
Backend::RpcException::RpcException(const std::string& message)
: m_message(message)
{ }
const char* Backend::RpcException::what() const throw()
{
return m_message.c_str();
}

51
src-cpp/backend/utils.cpp Normal file
View File

@ -0,0 +1,51 @@
#include "backend/utils.h"
#include "backend/types.h"
#include <QCryptographicHash>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QString>
#include <QVector>
#include <QDebug>
QJsonArray Backend::Utils::toJsonArray(const QVector<QString>& value)
{
QJsonArray array;
for(auto& v : value)
array << v;
return array;
}
QString Backend::Utils::jsonToStr(QJsonObject obj)
{
QJsonDocument doc(obj);
return QString::fromUtf8(doc.toJson());
}
QString Backend::Utils::jsonToStr(QJsonArray arr)
{
QJsonDocument doc(arr);
return QString::fromUtf8(doc.toJson());
}
QVector<QString> Backend::Utils::toStringVector(const QJsonArray& arr)
{
QVector<QString> result;
foreach(const QJsonValue& value, arr)
{
result << value.toString();
}
return result;
}
QString Backend::Utils::hashString(QString str)
{
return "0x" + QString::fromUtf8(QCryptographicHash::hash(str.toUtf8(), QCryptographicHash::Keccak_256).toHex());
}
void Backend::Utils::throwOnError(QJsonObject response) {
if(!response["error"].isUndefined() && !response["error"].toString().isEmpty()){
qWarning() << "RpcException: " << response["error"].toString();
throw Backend::RpcException(response["error"].toString().toStdString());
}
}

41
src-cpp/constants.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "constants.h"
#include <QDir>
#include <QFileInfo>
#include <QMessageBox>
#include <QStandardPaths>
#include <QString>
// TODO: merge with constants from backend/
QString Constants::applicationPath(QString path)
{
return QFileInfo(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + path).absoluteFilePath();
}
QString Constants::tmpPath(QString path)
{
return QFileInfo(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + path).absoluteFilePath();
}
QString Constants::cachePath(QString path)
{
return QFileInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + path).absoluteFilePath();
}
bool Constants::ensureDirectories()
{
if(Constants::applicationPath().isEmpty())
{
QDir d{Constants::applicationPath()};
if(!d.mkpath(d.absolutePath()))
{
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Warning);
msgBox.setText("Cannot determine storage location");
msgBox.exec();
return false;
}
}
return true;
}

15
src-cpp/constants.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <QString>
namespace Constants
{
const QString DataDir = "/data";
const QString Keystore = "/data/keystore";
QString applicationPath(QString path = "");
QString tmpPath(QString path = "");
QString cachePath(QString path = "");
bool ensureDirectories();
} // namespace Constants

View File

@ -0,0 +1,30 @@
if(APPLE)
file(GLOB_RECURSE SOURCES *.cpp *.mm)
else()
file(GLOB_RECURSE SOURCES *.cpp)
endif()
add_library(DOtherSide
${SOURCES})
if(APPLE)
target_include_directories(DOtherSide
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../../bottles/hunspell/include)
else()
target_include_directories(DOtherSide
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR})
endif()
target_link_libraries(DOtherSide
PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::Gui
)
include_directories(
# Add here any vendor folder
#../../vendor/vendor_name_goes_here/
)

View File

@ -0,0 +1,12 @@
#include "DOtherSide.h"
#include "SpellChecker.h"
#include "StatusSyntaxHighlighter.h"
#include "StatusWindow.h"
void DOtherSide::registerMetaTypes()
{
qRegisterMetaType<QVector<int>>();
qmlRegisterType<StatusWindow>("DotherSide", 0, 1, "StatusWindow");
qmlRegisterType<StatusSyntaxHighlighterHelper>("DotherSide", 0, 1, "StatusSyntaxHighlighter");
qmlRegisterType<SpellChecker>("DotherSide", 0, 1, "SpellChecker");
}

View File

@ -0,0 +1,6 @@
#pragma once
class DOtherSide {
public:
static void registerMetaTypes();
};

View File

@ -0,0 +1,63 @@
#include "DOtherSide/DOtherSideSingleInstance.h"
#include <QLocalServer>
#include <QLocalSocket>
namespace {
const int ReadWriteTimeoutMs = 1000;
}
SingleInstance::SingleInstance(const QString &uniqueName, const QString &eventStr, QObject *parent)
: QObject(parent)
, m_localServer(new QLocalServer(this))
{
QString socketName = uniqueName;
#ifndef Q_OS_WIN
socketName = QString("/tmp/%1").arg(socketName);
#endif
QLocalSocket localSocket;
localSocket.connectToServer(socketName);
// the first instance start will be delayed by this timeout (ms) to ensure there are no other instances.
// note: this is an ad-hoc timeout value selected based on prior experience.
if (!localSocket.waitForConnected(100)) {
connect(m_localServer, &QLocalServer::newConnection, this, &SingleInstance::handleNewConnection);
// on *nix a crashed process will leave /tmp/xyz file preventing to start a new server.
// therefore, if we were unable to connect, then we assume the server died and we need to clean up.
// p.s. on Windows, this function does nothing.
QLocalServer::removeServer(socketName);
if (!m_localServer->listen(socketName)) {
qWarning() << "QLocalServer::listen(" << socketName << ") failed";
}
} else if (!eventStr.isEmpty()) {
localSocket.write(eventStr.toUtf8() + '\n');
localSocket.waitForBytesWritten(ReadWriteTimeoutMs);
}
}
SingleInstance::~SingleInstance()
{
if (m_localServer->isListening()) {
m_localServer->close();
}
}
bool SingleInstance::isFirstInstance() const
{
return m_localServer->isListening();
}
void SingleInstance::handleNewConnection()
{
emit secondInstanceDetected();
auto socket = m_localServer->nextPendingConnection();
if (socket->waitForReadyRead(ReadWriteTimeoutMs) && socket->canReadLine()) {
auto event = socket->readLine();
emit eventReceived(QString::fromUtf8(event));
}
socket->deleteLater();
}

View File

@ -0,0 +1,32 @@
#ifndef SINGLEINSTANCE_H
#define SINGLEINSTANCE_H
#include <QObject>
class QLocalServer;
class SingleInstance : public QObject
{
Q_OBJECT
public:
// uniqueName - the name of named pipe
// eventStr - optional event to send if another instance is detected
explicit SingleInstance(const QString &uniqueName, const QString &eventStr, QObject *parent = nullptr);
~SingleInstance() override;
bool isFirstInstance() const;
signals:
void secondInstanceDetected();
void eventReceived(const QString &eventStr);
private slots:
void handleNewConnection();
private:
QLocalServer *m_localServer;
};
#endif // SINGLEINSTANCE_H

View File

@ -0,0 +1,185 @@
#include "SpellChecker.h"
#ifdef Q_OS_MACOS
#include "hunspell/hunspell.hxx"
#endif
#include <QTextCodec>
#include <QFile>
#include <QDebug>
#include <QLocale>
#include <QRegularExpression>
#include <QGuiApplication>
#include <QDir>
#include <QInputMethod>
SpellChecker::SpellChecker(QObject *parent)
: QObject(parent)
#ifdef Q_OS_MACOS
, m_hunspell(nullptr)
#endif
, m_userDict("userDict_")
{
}
SpellChecker::~SpellChecker()
{
#ifdef Q_OS_MACOS
delete m_hunspell;
#endif
}
bool SpellChecker::spell(const QString &word)
{
#ifdef Q_OS_MACOS
return m_hunspell->spell(m_codec->fromUnicode(word).toStdString());
#else
return true;
#endif
}
bool SpellChecker::isInit() const
{
#ifdef Q_OS_MACOS
return !m_hunspell;
#else
return true;
#endif
}
void SpellChecker::initHunspell()
{
#ifdef Q_OS_MACOS
if (m_hunspell) {
delete m_hunspell;
}
QString dictFile = QGuiApplication::applicationDirPath() + "/dictionaries/" + m_lang + "/index.dic";
QString affixFile = QGuiApplication::applicationDirPath() + "/dictionaries/" + m_lang + "/index.aff";
QByteArray dictFilePathBA = dictFile.toLocal8Bit();
QByteArray affixFilePathBA = affixFile.toLocal8Bit();
m_hunspell = new Hunspell(affixFilePathBA.constData(),
dictFilePathBA.constData());
// detect encoding analyzing the SET option in the affix file
auto encoding = QStringLiteral("ISO8859-15");
QFile _affixFile(affixFile);
if (_affixFile.open(QIODevice::ReadOnly)) {
QTextStream stream(&_affixFile);
QRegularExpression enc_detector(
QStringLiteral("^\\s*SET\\s+([A-Z0-9\\-]+)\\s*"),
QRegularExpression::CaseInsensitiveOption);
QString sLine;
QRegularExpressionMatch match;
while (!stream.atEnd()) {
sLine = stream.readLine();
if (sLine.isEmpty()) { continue; }
match = enc_detector.match(sLine);
if (match.hasMatch()) {
encoding = match.captured(1);
qDebug() << "Encoding set to " + encoding;
break;
}
}
_affixFile.close();
}
m_codec = QTextCodec::codecForName(encoding.toLatin1().constData());
QString userDict = m_userDict + m_lang + ".txt";
if (!userDict.isEmpty()) {
QFile userDictonaryFile(userDict);
if (userDictonaryFile.open(QIODevice::ReadOnly)) {
QTextStream stream(&userDictonaryFile);
for (QString word = stream.readLine();
!word.isEmpty();
word = stream.readLine())
ignoreWord(word);
userDictonaryFile.close();
} else {
qWarning() << "User dictionary in " << userDict
<< "could not be opened";
}
} else {
qDebug() << "User dictionary not set.";
}
#endif
}
QVariantList SpellChecker::suggest(const QString &word)
{
int numSuggestions = 0;
QVariantList suggestions;
#ifdef Q_OS_MACOS
std::vector<std::string> wordlist;
wordlist = m_hunspell->suggest(m_codec->fromUnicode(word).toStdString());
numSuggestions = static_cast<int>(wordlist.size());
if (numSuggestions > 0) {
suggestions.reserve(numSuggestions);
for (int i = 0; i < numSuggestions; i++) {
suggestions << m_codec->toUnicode(
QByteArray::fromStdString(wordlist[i]));
}
}
#endif
return suggestions;
}
void SpellChecker::ignoreWord(const QString &word)
{
#ifdef Q_OS_MACOS
m_hunspell->add(m_codec->fromUnicode(word).constData());
#endif
}
void SpellChecker::addToUserWordlist(const QString &word)
{
#ifdef Q_OS_MACOS
QString userDict = m_userDict + m_lang + ".txt";
if (!userDict.isEmpty()) {
QFile userDictonaryFile(userDict);
if (userDictonaryFile.open(QIODevice::Append)) {
QTextStream stream(&userDictonaryFile);
stream << word << "\n";
userDictonaryFile.close();
} else {
qWarning() << "User dictionary in " << userDict
<< "could not be opened for appending a new word";
}
} else {
qDebug() << "User dictionary not set.";
}
#endif
}
const QString& SpellChecker::lang() const
{
return m_lang;
}
void SpellChecker::setLang(const QString& lang)
{
if (m_lang != lang) {
m_lang = lang;
initHunspell();
emit langChanged();
}
}
const QString& SpellChecker::userDict() const
{
return m_userDict;
}
void SpellChecker::setUserDict(const QString& userDict)
{
if (m_userDict != userDict) {
m_userDict = userDict;
emit userDictChanged();
}
}

View File

@ -0,0 +1,51 @@
#pragma once
#include <QObject>
#include <QVariant>
#include <QQuickTextDocument>
#include <QSyntaxHighlighter>
#ifdef Q_OS_MACOS
class Hunspell;
#endif
class QTextCodec;
class SpellChecker : public QObject
{
Q_OBJECT
Q_PROPERTY(QString lang READ lang WRITE setLang NOTIFY langChanged)
Q_PROPERTY(QString userDict READ userDict WRITE setUserDict NOTIFY userDictChanged)
public:
explicit SpellChecker(QObject *parent = nullptr);
~SpellChecker();
Q_INVOKABLE bool spell(const QString& word);
Q_INVOKABLE QVariantList suggest(const QString &word);
Q_INVOKABLE void ignoreWord(const QString &word);
Q_INVOKABLE void addToUserWordlist(const QString &word);
Q_INVOKABLE bool isInit() const;
const QString& lang() const;
void setLang(const QString& lang);
const QString& userDict() const;
void setUserDict(const QString& userDict);
signals:
void langChanged();
void userDictChanged();
private:
void initHunspell();
private:
QString m_lang;
QString m_userDict;
QQuickTextDocument *m_document;
#ifdef Q_OS_MACOS
Hunspell *m_hunspell;
#endif
QTextCodec *m_codec;
};

View File

@ -0,0 +1,67 @@
#include "StatusSyntaxHighlighter.h"
#include <QQuickTextDocument>
StatusSyntaxHighlighter::StatusSyntaxHighlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent)
{
HighlightingRule rule;
//BOLD
singlelineBoldFormat.setFontWeight(QFont::Bold);
rule.pattern = QRegularExpression(QStringLiteral("\\*\\*(.*?)\\*\\*"));
rule.format = singlelineBoldFormat;
highlightingRules.append(rule);
//BOLD
//ITALIC
singleLineItalicFormat.setFontItalic(true);
rule.pattern = QRegularExpression(QStringLiteral("\\*(.*?)\\*"));
rule.format = singleLineItalicFormat;
highlightingRules.append(rule);
//ITALIC
//CODE
singlelineCodeBlockFormat.setFontFamily("Roboto Mono");
rule.pattern = QRegularExpression(QStringLiteral("\\`(.*?)\\`"));
rule.format = singlelineCodeBlockFormat;
highlightingRules.append(rule);
//CODE
//STRIKETHROUGH
singleLineStrikeThroughFormat.setFontStrikeOut(true);
rule.pattern = QRegularExpression(QStringLiteral("\\~+(.*?)\\~+"));
rule.format = singleLineStrikeThroughFormat;
highlightingRules.append(rule);
//STRIKETHROUGH
//CODE BLOCK
multiLineCodeBlockFormat.setFontFamily("Roboto Mono");
rule.pattern = QRegularExpression(QStringLiteral("\\`\\`\\`(.*?)\\`\\`\\`"));
rule.format = multiLineCodeBlockFormat;
highlightingRules.append(rule);
//CODE BLOCK
}
void StatusSyntaxHighlighter::highlightBlock(const QString &text)
{
for (const HighlightingRule &rule : qAsConst(highlightingRules)) {
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
while (matchIterator.hasNext()) {
QRegularExpressionMatch match = matchIterator.next();
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
}
}
setCurrentBlockState(0);
}
QQuickTextDocument *StatusSyntaxHighlighterHelper::quickTextDocument() const {
return m_quicktextdocument;
}
void StatusSyntaxHighlighterHelper::setQuickTextDocument(
QQuickTextDocument *quickTextDocument) {
m_quicktextdocument = quickTextDocument;
if (m_quicktextdocument) {
new StatusSyntaxHighlighter(m_quicktextdocument->textDocument());
}
}

View File

@ -0,0 +1,48 @@
#pragma once
#include <QSyntaxHighlighter>
#include <QTextCharFormat>
#include <QRegularExpression>
class QQuickTextDocument;
class StatusSyntaxHighlighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
StatusSyntaxHighlighter(QTextDocument *parent = nullptr);
protected:
void highlightBlock(const QString &text) override;
private:
struct HighlightingRule
{
QRegularExpression pattern;
QTextCharFormat format;
};
QVector<HighlightingRule> highlightingRules;
QTextCharFormat singlelineBoldFormat;
QTextCharFormat singleLineItalicFormat;
QTextCharFormat singlelineCodeBlockFormat;
QTextCharFormat singleLineStrikeThroughFormat;
QTextCharFormat multiLineCodeBlockFormat;
};
class StatusSyntaxHighlighterHelper : public QObject {
Q_OBJECT
Q_PROPERTY(QQuickTextDocument *quickTextDocument READ quickTextDocument WRITE
setQuickTextDocument NOTIFY quickTextDocumentChanged)
public:
StatusSyntaxHighlighterHelper(QObject *parent = nullptr)
: QObject(parent), m_quicktextdocument(nullptr) {}
QQuickTextDocument *quickTextDocument() const;
void setQuickTextDocument(QQuickTextDocument *quickTextDocument);
signals:
void quickTextDocumentChanged();
private:
QQuickTextDocument *m_quicktextdocument;
};

View File

@ -0,0 +1,34 @@
#include "StatusWindow.h"
StatusWindow::StatusWindow(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 StatusWindow::toggleFullScreen()
{
if (m_isFullScreen) {
showNormal();
} else {
showFullScreen();
}
}
bool StatusWindow::isFullScreen() const
{
return m_isFullScreen;
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <QQuickWindow>
#include <QScreen>
class StatusWindow: public QQuickWindow
{
Q_OBJECT
Q_PROPERTY(bool isFullScreen READ isFullScreen NOTIFY isFullScreenChanged)
public:
explicit StatusWindow(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 removeTitleBar();
void showTitleBar();
void initCallbacks();
private:
bool m_isFullScreen;
};

View File

@ -0,0 +1,35 @@
#include "StatusWindow.h"
#include <Foundation/Foundation.h>
#include <AppKit/NSView.h>
#include <AppKit/NSWindow.h>
#include <AppKit/NSColor.h>
#include <AppKit/NSToolbar.h>
#include <AppKit/NSButton.h>
#include <AppKit/AppKit.h>
void StatusWindow::removeTitleBar()
{
NSView *nsView = reinterpret_cast<NSView*>(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 StatusWindow::showTitleBar()
{
NSView *nsView = reinterpret_cast<NSView*>(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];
}

Some files were not shown because too many files have changed in this diff Show More