chore(CPP): integrate tokens balance in UI POC
Integrate token count in UI Use delete later for QML exposed items to avoid errors Closes #6321
This commit is contained in:
parent
f3dcdf636a
commit
7cf0170a8a
|
@ -63,7 +63,7 @@ cmake -B build -S . -DCMAKE_PREFIX_PATH="$HOME/Qt/6.3.2/gcc_64" -DCMAKE_BUILD_TY
|
|||
|
||||
# Windows: cmake -B build -S . -DCMAKE_PREFIX_PATH="$HOME/Qt/6.3.2/mingw_64" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=build/conan/conan_toolchain.cmake
|
||||
|
||||
cmake --build build --config Release
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
### Run tests
|
||||
|
|
|
@ -58,7 +58,9 @@ public:
|
|||
};
|
||||
|
||||
void clear() {
|
||||
beginResetModel();
|
||||
m_objects.clear();
|
||||
endResetModel();
|
||||
};
|
||||
|
||||
void push_back(const std::shared_ptr<T> newValue) {
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
#include "Helpers/BuildConfiguration.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory.h>
|
||||
|
||||
namespace Status::Helpers {
|
||||
|
||||
|
@ -29,4 +33,24 @@ bool iequals(const T& a, const T& b, size_t len = -1)
|
|||
});
|
||||
}
|
||||
|
||||
template<typename KeyType, typename ValT>
|
||||
std::vector<KeyType> getKeys(const std::map<KeyType, ValT>& map)
|
||||
{
|
||||
std::vector<KeyType> keys;
|
||||
keys.reserve(map.size());
|
||||
for (const auto& [key, _] : map)
|
||||
keys.push_back(key);
|
||||
return keys;
|
||||
}
|
||||
|
||||
static void doDeleteLater(QObject *obj) {
|
||||
obj->deleteLater();
|
||||
}
|
||||
|
||||
// TODO: use https://en.cppreference.com/w/cpp/memory/shared_ptr/allocate_shared
|
||||
template<typename T, typename ...Args>
|
||||
std::shared_ptr<T> makeSharedQObject(Args&& ...args) {
|
||||
return std::shared_ptr<T>(new T(std::forward<Args>(args)...), doDeleteLater);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ import Status.Onboarding
|
|||
Item {
|
||||
id: root
|
||||
|
||||
// TODO: fix error "Unable to assign Status::Onboarding::NewAccountController to Status::Onboarding::NewAccountController" then enable typed properties
|
||||
required property var/*NewAccountController*/ newAccountController
|
||||
/// \c NewAccountController
|
||||
required property var newAccountController
|
||||
|
||||
signal userLoggedIn()
|
||||
signal abortAccountCreation()
|
||||
|
|
|
@ -12,6 +12,7 @@ TextInput {
|
|||
Rectangle {
|
||||
anchors.fill: parent
|
||||
border.width: 1
|
||||
border.color: "#55555555"
|
||||
z: parent.z - 1
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ import "base"
|
|||
OnboardingPageBase {
|
||||
id: root
|
||||
|
||||
required property var onboardingController // OnboardingController
|
||||
/// \c OnboardingController
|
||||
/// \todo investigate "Unable to assign Status::Onboarding::OnboardingController to Status::Onboarding::OnboardingController"
|
||||
required property var onboardingController
|
||||
|
||||
signal setupNewAccount()
|
||||
/// \param statusAccount \c UserAccount
|
||||
|
@ -51,15 +53,6 @@ OnboardingPageBase {
|
|||
id: passwordInput
|
||||
Layout.preferredWidth: 328
|
||||
Layout.preferredHeight: 44
|
||||
|
||||
// TODO: remove dev helper
|
||||
text: "1234567890"
|
||||
Timer {
|
||||
interval: 100
|
||||
running: loginButton.enabled && accountsComboBox.count
|
||||
onTriggered: loginButton.clicked()
|
||||
}
|
||||
// END dev
|
||||
}
|
||||
|
||||
Button {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import QtQuick
|
||||
|
||||
import Status.Onboarding
|
||||
|
||||
/*! Proposal on how to templetize the alignment requirement of some views
|
||||
*/
|
||||
OnboardingPageBase {
|
||||
// TODO: fix error "Unable to assign Status::Onboarding::NewAccountController to Status::Onboarding::NewAccountController" then enable typed properties
|
||||
required property var/*NewAccountController*/ newAccountController
|
||||
required property var newAccountController
|
||||
|
||||
/// Common reference item that doesn't change between common views/pages
|
||||
readonly property Item alignmentItem: alignmentBaselineItem
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <StatusGo/SignalsManager.h>
|
||||
|
||||
#include <Helpers/helpers.h>
|
||||
|
||||
namespace Status::Onboarding {
|
||||
|
||||
namespace StatusGo = Status::StatusGo;
|
||||
|
@ -17,9 +19,9 @@ OnboardingController::OnboardingController(AccountsServiceInterfacePtr accountsS
|
|||
{ // Init accounts
|
||||
std::vector<std::shared_ptr<UserAccount>> accounts;
|
||||
for(auto &account : getOpenedAccounts()) {
|
||||
accounts.push_back(std::make_shared<UserAccount>(std::make_unique<MultiAccount>(std::move(account))));
|
||||
accounts.push_back(Helpers::makeSharedQObject<UserAccount>(std::make_unique<MultiAccount>(std::move(account))));
|
||||
}
|
||||
m_accounts = std::make_shared<UserAccountsModel>(std::move(accounts));
|
||||
m_accounts = Helpers::makeSharedQObject<UserAccountsModel>(std::move(accounts));
|
||||
}
|
||||
|
||||
connect(StatusGo::SignalsManager::instance(), &StatusGo::SignalsManager::nodeLogin, this, &OnboardingController::onLogin);
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
|
||||
UserAccountsModel *accounts() const;
|
||||
|
||||
Q_INVOKABLE NewAccountController *initNewAccountController();
|
||||
Q_INVOKABLE Status::Onboarding::NewAccountController *initNewAccountController();
|
||||
Q_INVOKABLE void terminateNewAccountController();
|
||||
NewAccountController *newAccountController() const;
|
||||
std::shared_ptr<AccountsServiceInterface> accountsService() const;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <ApplicationCore/UserConfiguration.h>
|
||||
|
||||
#include <Helpers/helpers.h>
|
||||
|
||||
namespace AppCore = Status::ApplicationCore;
|
||||
|
||||
|
@ -43,7 +44,7 @@ void OnboardingModule::initWithUserDataPath(const fs::path &path)
|
|||
auto result = m_accountsService->init(path);
|
||||
if(!result)
|
||||
throw std::runtime_error(std::string("Failed to initialize OnboadingService") + path.string());
|
||||
m_controller = std::make_shared<OnboardingController>(
|
||||
m_controller = Helpers::makeSharedQObject<OnboardingController>(
|
||||
m_accountsService);
|
||||
emit controllerChanged();
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ project(OnboardingTestHelpers
|
|||
|
||||
set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS true)
|
||||
|
||||
find_package(GTest REQUIRED)
|
||||
|
||||
find_package(Qt6 ${STATUS_QT_VERSION} COMPONENTS Quick Qml REQUIRED)
|
||||
qt6_standard_project_setup()
|
||||
|
||||
|
@ -47,6 +45,4 @@ target_link_libraries(OnboardingTestHelpers
|
|||
Status::ApplicationCore
|
||||
Status::Onboarding
|
||||
Status::StatusGoQt
|
||||
|
||||
GTest::gtest
|
||||
)
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
#include <StatusGo/Accounts/Accounts.h>
|
||||
#include <StatusGo/Accounts/AccountsAPI.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <Helpers/helpers.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace Testing = Status::Testing;
|
||||
namespace Onboarding = Status::Onboarding;
|
||||
|
@ -49,7 +49,7 @@ ScopedTestAccount::ScopedTestAccount(const std::string &tempTestSubfolderName,
|
|||
//
|
||||
|
||||
// Beware, smartpointer is a requirement
|
||||
m_onboarding = std::make_shared<Onboarding::OnboardingController>(accountsService);
|
||||
m_onboarding = Helpers::makeSharedQObject<Onboarding::OnboardingController>(accountsService);
|
||||
if(m_onboarding->getOpenedAccounts().size() != 0)
|
||||
throw std::runtime_error("ScopedTestAccount - already have opened account");
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <Onboarding/Accounts/AccountsService.h>
|
||||
#include <Onboarding/OnboardingController.h>
|
||||
|
||||
#include <Helpers/helpers.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
|
@ -49,7 +51,7 @@ std::shared_ptr<AccountsServiceMock> LoginTest::m_accountsServiceMock;
|
|||
TEST_F(LoginTest, DISABLED_TestLoginController)
|
||||
{
|
||||
// Controller hides as a regular class but at runtime it must be a shared pointer; TODO: refactor
|
||||
auto controller = std::make_shared<Onboarding::OnboardingController>(m_accountsServiceMock);
|
||||
auto controller = Helpers::makeSharedQObject<Onboarding::OnboardingController>(m_accountsServiceMock);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -28,9 +28,10 @@ struct Token
|
|||
ChainID chainId;
|
||||
};
|
||||
|
||||
using TokenPtr = std::shared_ptr<Token>;
|
||||
using Tokens = std::vector<Token>;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Token, address, name,symbol,
|
||||
color, decimals, chainId);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,13 @@ NetworkConfigurations getEthereumChains(bool onlyEnabled);
|
|||
|
||||
/// \note status-go's GetEthereumChains@api.go which calls
|
||||
/// NetworkManager@client.go -> network.Manager.get()
|
||||
/// \throws \c CallPrivateRpcError
|
||||
/// \throws \c CallPrivateRpcError.errorResponse().error with \c "no tokens for this network" in case of missing tokens for the network
|
||||
/// \throws \c CallPrivateRpcError for general RPC call failure
|
||||
NetworkConfigurations getEthereumChains(bool onlyEnabled);
|
||||
|
||||
|
||||
/// \note status-go's GetTokens@api.go -> TokenManager.getTokens@token.go
|
||||
/// \throws \c CallPrivateRpcError
|
||||
/// \throws \c CallPrivateRpcError with
|
||||
Tokens getTokens(const ChainID &chainId);
|
||||
|
||||
using TokenBalances = std::map<Accounts::EOAddress, std::map<Accounts::EOAddress, BigInt>>;
|
||||
|
|
|
@ -66,13 +66,17 @@ install(
|
|||
|
||||
target_sources(Wallet
|
||||
PRIVATE
|
||||
include/Status/Wallet/AccountAssetsController.h
|
||||
src/AccountAssetsController.cpp
|
||||
include/Status/Wallet/DerivedWalletAddress.h
|
||||
src/DerivedWalletAddress.cpp
|
||||
include/Status/Wallet/NewWalletAccountController.h
|
||||
src/NewWalletAccountController.cpp
|
||||
include/Status/Wallet/WalletAccount.h
|
||||
# Move to Accounts module
|
||||
include/Status/Wallet/WalletAccount.h
|
||||
src/WalletAccount.cpp
|
||||
include/Status/Wallet/WalletAsset.h
|
||||
src/WalletAsset.cpp
|
||||
include/Status/Wallet/WalletController.h
|
||||
src/WalletController.cpp
|
||||
)
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "Status/Wallet/WalletAccount.h"
|
||||
#include "Status/Wallet/WalletAsset.h"
|
||||
|
||||
#include <Helpers/QObjectVectorModel.h>
|
||||
|
||||
#include <QtQmlIntegration>
|
||||
|
||||
namespace Status::Wallet {
|
||||
|
||||
/// Controlls asset for an account using hardcoded network and token lists
|
||||
///
|
||||
/// \todo add static configuration component to provide networks, tokens and currency list
|
||||
/// \todo impliement \c AccountsBalanceService to fetch and cache realtime balance (or better implement this in status-go)
|
||||
/// \todo implement native token fetching
|
||||
/// \todo double responsibility, split functionality in asset management and balance
|
||||
class AccountAssetsController: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("C++ only")
|
||||
|
||||
Q_PROPERTY(QAbstractItemModel* assetModel READ assetsModel CONSTANT)
|
||||
Q_PROPERTY(float totalValue READ totalValue NOTIFY totalValueChanged)
|
||||
Q_PROPERTY(bool assetsReady READ assetsReady NOTIFY assetsReadyChanged)
|
||||
|
||||
public:
|
||||
AccountAssetsController(WalletAccount* address, QObject* parent = nullptr);
|
||||
|
||||
QAbstractItemModel* assetsModel() const;
|
||||
|
||||
float totalValue() const;
|
||||
|
||||
bool assetsReady() const;
|
||||
|
||||
signals:
|
||||
void totalValueChanged();
|
||||
void assetsReadyChanged();
|
||||
|
||||
private:
|
||||
void updateBalances();
|
||||
bool isTokenEnabled(const StatusGo::Wallet::Token& token) const;
|
||||
|
||||
WalletAccount* m_address;
|
||||
const std::vector<QString> m_enabledTokens;
|
||||
|
||||
using AssetModel = Helpers::QObjectVectorModel<WalletAsset>;
|
||||
std::shared_ptr<AssetModel> m_assets;
|
||||
float m_totalValue{};
|
||||
bool m_assetsReady{};
|
||||
};
|
||||
|
||||
} // namespace Status::Wallet
|
|
@ -2,30 +2,32 @@
|
|||
|
||||
#include <StatusGo/Wallet/DerivedAddress.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QtQmlIntegration>
|
||||
|
||||
namespace GoWallet = Status::StatusGo::Wallet;
|
||||
namespace WalletGo = Status::StatusGo::Wallet;
|
||||
|
||||
namespace Status::Wallet {
|
||||
|
||||
class DerivedWalletAddress : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("C++ only")
|
||||
|
||||
Q_PROPERTY(QString address READ address CONSTANT)
|
||||
Q_PROPERTY(bool alreadyCreated READ alreadyCreated CONSTANT)
|
||||
|
||||
public:
|
||||
explicit DerivedWalletAddress(GoWallet::DerivedAddress address, QObject *parent = nullptr);
|
||||
explicit DerivedWalletAddress(WalletGo::DerivedAddress address, QObject *parent = nullptr);
|
||||
|
||||
QString address() const;
|
||||
|
||||
const GoWallet::DerivedAddress &data() const { return m_derivedAddress; };
|
||||
const WalletGo::DerivedAddress &data() const { return m_derivedAddress; };
|
||||
|
||||
bool alreadyCreated() const;
|
||||
|
||||
private:
|
||||
const GoWallet::DerivedAddress m_derivedAddress;
|
||||
const WalletGo::DerivedAddress m_derivedAddress;
|
||||
};
|
||||
|
||||
using DerivedWalletAddressPtr = std::shared_ptr<DerivedWalletAddress>;
|
||||
|
|
|
@ -5,19 +5,16 @@
|
|||
|
||||
#include <Helpers/QObjectVectorModel.h>
|
||||
|
||||
#include <QQmlListProperty>
|
||||
#include <QtQmlIntegration>
|
||||
|
||||
class QQmlEngine;
|
||||
class QJSEngine;
|
||||
|
||||
namespace Status::Wallet {
|
||||
|
||||
/// \note the folowing values are kept in sync \c selectedDerivedAddress, \c derivedAddressIndex and \c derivationPath
|
||||
/// \note the following values are kept in sync \c selectedDerivedAddress, \c derivedAddressIndex and \c derivationPath
|
||||
/// and \c customDerivationPath; \see connascence.io/value
|
||||
class NewWalletAccountController: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("C++ only")
|
||||
|
||||
Q_PROPERTY(QAbstractListModel* mainAccountsModel READ mainAccountsModel CONSTANT)
|
||||
|
@ -35,10 +32,6 @@ public:
|
|||
/// \note On account creation \c accounts are updated with the newly created wallet account
|
||||
NewWalletAccountController(std::shared_ptr<AccountsModel> accounts);
|
||||
|
||||
/// Called by QML engine to register the instance. QML takes ownership of the instance
|
||||
static NewWalletAccountController *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
|
||||
|
||||
|
||||
QAbstractListModel *mainAccountsModel();
|
||||
QAbstractItemModel *currentDerivedAddressModel();
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "Accounts/ChatOrWalletAccount.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QtQmlIntegration>
|
||||
|
||||
namespace GoAccounts = Status::StatusGo::Accounts;
|
||||
|
||||
|
@ -11,9 +11,11 @@ namespace Status::Wallet {
|
|||
class WalletAccount: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("C++ only, for now")
|
||||
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(QString address READ address CONSTANT)
|
||||
Q_PROPERTY(QString address READ strAddress CONSTANT)
|
||||
Q_PROPERTY(QColor color READ color CONSTANT)
|
||||
|
||||
public:
|
||||
|
@ -21,7 +23,7 @@ public:
|
|||
|
||||
const QString &name() const;
|
||||
|
||||
const QString &address() const;
|
||||
const QString &strAddress() const;
|
||||
|
||||
QColor color() const;
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <StatusGo/Wallet/Token.h>
|
||||
#include <StatusGo/Wallet/BigInt.h>
|
||||
|
||||
#include <QtQmlIntegration>
|
||||
|
||||
namespace WalletGo = Status::StatusGo::Wallet;
|
||||
|
||||
namespace Status::Wallet {
|
||||
|
||||
class WalletAsset : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("C++ only, for now")
|
||||
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(QString symbol READ symbol CONSTANT)
|
||||
Q_PROPERTY(QColor color READ color CONSTANT)
|
||||
Q_PROPERTY(quint64 count READ count CONSTANT)
|
||||
Q_PROPERTY(float value READ value CONSTANT)
|
||||
|
||||
public:
|
||||
explicit WalletAsset(const WalletGo::TokenPtr token, StatusGo::Wallet::BigInt balance, QObject *parent = nullptr);
|
||||
|
||||
const QString name() const;
|
||||
|
||||
const QString symbol() const;
|
||||
|
||||
const QColor color() const;
|
||||
|
||||
quint64 count() const;
|
||||
|
||||
float value() const;
|
||||
|
||||
private:
|
||||
const WalletGo::TokenPtr m_token;
|
||||
// const GoWallet::NativeToken m_nativeToken;
|
||||
|
||||
StatusGo::Wallet::BigInt m_balance;
|
||||
int m_count;
|
||||
};
|
||||
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include <Helpers/QObjectVectorModel.h>
|
||||
|
||||
#include <QQmlListProperty>
|
||||
#include <QtQmlIntegration>
|
||||
|
||||
#include <memory>
|
||||
|
@ -16,6 +15,7 @@ class QJSEngine;
|
|||
namespace Status::Wallet {
|
||||
|
||||
class NewWalletAccountController;
|
||||
class AccountAssetsController;
|
||||
|
||||
/// \todo move account creation to its own controller
|
||||
class WalletController: public QObject
|
||||
|
@ -44,6 +44,9 @@ public:
|
|||
WalletAccount *currentAccount() const;
|
||||
Q_INVOKABLE void setCurrentAccountIndex(int index);
|
||||
|
||||
/// \note caller (QML) takes ownership of the returned instance
|
||||
Q_INVOKABLE Status::Wallet::AccountAssetsController* createAccountAssetsController(Status::Wallet::WalletAccount* account);
|
||||
|
||||
signals:
|
||||
void currentAccountChanged();
|
||||
|
||||
|
|
|
@ -1,14 +1,51 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Status.Wallet
|
||||
import Status.Containers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
/// WalletAccount
|
||||
required property var asset
|
||||
required property AccountAssetsController assetController
|
||||
required property WalletAccount account
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
text: asset.name
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
model: root.assetController.assetModel
|
||||
|
||||
delegate: RowLayout {
|
||||
required property WalletAsset asset
|
||||
|
||||
Label {
|
||||
text: asset.name
|
||||
|
||||
Layout.preferredWidth: listView.width * 0.4
|
||||
}
|
||||
RowLayout {
|
||||
Layout.preferredWidth: listView.width * 0.4
|
||||
|
||||
Label {
|
||||
text: asset.count
|
||||
}
|
||||
Label {
|
||||
text: asset.symbol
|
||||
}
|
||||
LayoutSpacer{}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: asset.value
|
||||
}
|
||||
Label {
|
||||
//text: asset.currency
|
||||
}
|
||||
}
|
||||
LayoutSpacer{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQml
|
||||
|
||||
import Status.Wallet
|
||||
|
||||
|
@ -13,6 +14,7 @@ Item {
|
|||
|
||||
/// WalletController
|
||||
required property WalletController controller
|
||||
readonly property AccountAssetsController currentAssetController: listView.currentItem ? listView.currentItem.assetController : null
|
||||
|
||||
ColumnLayout {
|
||||
anchors.left: leftLine.right
|
||||
|
@ -25,7 +27,8 @@ Item {
|
|||
}
|
||||
Label {
|
||||
id: totalValueLabel
|
||||
text: "" // TODO: Aggregate or API!?
|
||||
// TODO: source it from the last total cached value of balance service,
|
||||
text: "-"
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Total value")
|
||||
|
@ -37,6 +40,8 @@ Item {
|
|||
}
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
|
@ -47,10 +52,17 @@ Item {
|
|||
clip: true
|
||||
|
||||
delegate: ItemDelegate {
|
||||
required property int index
|
||||
// Enabling type generates 'Writing to "account" broke the binding to the underlying model'
|
||||
required property var/*WalletAccount*/ account
|
||||
|
||||
readonly property AccountAssetsController assetController: account && WalletController.createAccountAssetsController(account)
|
||||
|
||||
highlighted: ListView.isCurrentItem
|
||||
|
||||
width: ListView.view.width
|
||||
|
||||
|
||||
onClicked: ListView.view.currentIndex = index
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
|
@ -79,17 +91,19 @@ Item {
|
|||
elide: Label.ElideRight
|
||||
}
|
||||
}
|
||||
Label {
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 10
|
||||
Layout.bottomMargin: 5
|
||||
|
||||
text: "$"
|
||||
color: "grey"
|
||||
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
|
||||
elide: Label.ElideRight
|
||||
Label {
|
||||
text: assetController.assetsReady ? assetController.totalValue : "-"
|
||||
}
|
||||
Label {
|
||||
text: "$"
|
||||
color: "grey"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,13 +151,13 @@ Item {
|
|||
}
|
||||
model: ObjectModel {
|
||||
NewAccountEntry {
|
||||
title: "New Account"
|
||||
title: qsTr("New Account")
|
||||
sourceComponent: NewWalletAccountView {
|
||||
controller: root.controller.createNewWalletAccountController()
|
||||
}
|
||||
}
|
||||
NewAccountEntry {
|
||||
title: "Watch Only Account"
|
||||
title: qsTr("Watch Only Account")
|
||||
sourceComponent: AddWatchOnlyAccountView {
|
||||
controller: root.controller.createNewWalletAccountController()
|
||||
}
|
||||
|
|
|
@ -11,8 +11,7 @@ import Status.Containers
|
|||
Item {
|
||||
id: root
|
||||
|
||||
/// NewWalletAccountController
|
||||
required property var controller
|
||||
required property NewWalletAccountController controller
|
||||
|
||||
signal accountCreated()
|
||||
signal cancel()
|
||||
|
|
|
@ -9,7 +9,7 @@ import Status.Containers
|
|||
Item {
|
||||
id: root
|
||||
|
||||
/// NewWalletAccountController
|
||||
// Using the type NewWalletAccountController generates "/bin/sh: line 1: 12832 Segmentation fault: 11 qmlcachegen --resource-path /Status/Wallet/qml/Status/Wallet/NewAccount/NewWalletAccountView.qml"
|
||||
required property var controller
|
||||
|
||||
signal accountCreated()
|
||||
|
@ -135,7 +135,7 @@ Item {
|
|||
}
|
||||
highlighted: derivedAddressCombo.highlightedIndex === index
|
||||
|
||||
required property var derivedAddress
|
||||
required property DerivedWalletAddress derivedAddress
|
||||
required property int index
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ Item {
|
|||
}
|
||||
highlighted: derivedFromCombo.highlightedIndex === index
|
||||
|
||||
required property var account
|
||||
required property WalletAccount account
|
||||
required property int index
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,17 +8,17 @@ import Status.Wallet
|
|||
Item {
|
||||
id: root
|
||||
|
||||
/// WalletAccount
|
||||
required property var asset
|
||||
required property WalletAccount account
|
||||
required property AccountAssetsController assetController
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
text: asset.name
|
||||
text: account.name
|
||||
}
|
||||
Label {
|
||||
text: asset.address
|
||||
text: account.address
|
||||
}
|
||||
TabBar {
|
||||
id: tabBar
|
||||
|
@ -37,6 +37,7 @@ Item {
|
|||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: 10
|
||||
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
|
@ -44,21 +45,24 @@ Item {
|
|||
clip: true
|
||||
|
||||
Loader {
|
||||
active: SwipeView.isCurrentItem
|
||||
active: SwipeView.isCurrentItem && root.assetController && root.account
|
||||
sourceComponent: AssetView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#66000066"
|
||||
border.width: 1
|
||||
}
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
|
||||
asset: root.asset
|
||||
assetController: root.assetController
|
||||
account: root.account
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: SwipeView.isCurrentItem
|
||||
sourceComponent: Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
text: "TODO"
|
||||
|
|
|
@ -36,7 +36,8 @@ PanelAndContentBase {
|
|||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
asset: WalletController.currentAccount
|
||||
account: WalletController.currentAccount
|
||||
assetController: panel.currentAssetController
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
#include "Status/Wallet/AccountAssetsController.h"
|
||||
|
||||
#include <StatusGo/Wallet/WalletApi.h>
|
||||
#include <StatusGo/Wallet/BigInt.h>
|
||||
#include <StatusGo/Metadata/api_response.h>
|
||||
|
||||
#include <Helpers/helpers.h>
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
namespace WalletGo = Status::StatusGo::Wallet;
|
||||
|
||||
namespace Status::Wallet {
|
||||
|
||||
AccountAssetsController::AccountAssetsController(WalletAccount* address, QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_address(address)
|
||||
, m_enabledTokens({u"SNT"_qs, u"ETH"_qs, u"STT"_qs, u"DAI"_qs})
|
||||
, m_assets(Helpers::makeSharedQObject<AssetModel>("asset"))
|
||||
{
|
||||
QtConcurrent::run([this, address]{ updateBalances(); })
|
||||
.then([this] {
|
||||
m_assetsReady = true;
|
||||
emit assetsReadyChanged();
|
||||
|
||||
m_totalValue = 0;
|
||||
for(const auto& asset : m_assets->objects())
|
||||
m_totalValue += asset->value();
|
||||
emit totalValueChanged();
|
||||
})
|
||||
.onFailed([this] {
|
||||
emit assetsReadyChanged();
|
||||
qWarning() << "Unexpected failure while executing updateBalances for account" << m_address->data().address.get();
|
||||
});
|
||||
}
|
||||
|
||||
void AccountAssetsController::updateBalances()
|
||||
{
|
||||
const StatusGo::Accounts::EOAddress& address = m_address->data().address;
|
||||
if(m_assets->size() > 0)
|
||||
m_assets->clear();
|
||||
// TODO: this should be moved to status-go and exposed as "get balances for account and tokens with currency"
|
||||
std::map<Accounts::EOAddress, StatusGo::Wallet::Token> tokens;
|
||||
std::vector<WalletGo::ChainID> chainIds;
|
||||
auto allNets = WalletGo::getEthereumChains(false);
|
||||
for(const auto &net : allNets) {
|
||||
if(net.enabled && !net.isTest) {
|
||||
try {
|
||||
const auto allTokens = WalletGo::getTokens(net.chainId);
|
||||
for(const auto& tokenToMove : allTokens) {
|
||||
if(isTokenEnabled(tokenToMove)) {
|
||||
auto address = tokenToMove.address;
|
||||
tokens.emplace(std::move(address), std::move(tokenToMove));
|
||||
}
|
||||
}
|
||||
chainIds.push_back(net.chainId);
|
||||
}
|
||||
catch (const StatusGo::CallPrivateRpcError& e) {
|
||||
// Most probably "no tokens for this network"
|
||||
if(e.errorResponse().error.message.compare("no tokens for this network") != 0)
|
||||
qWarning() << "Failed retrieving tokens for network" << net.chainId.get() << "; error" << e.errorResponse().error.message.c_str();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto accountBalances = WalletGo::getTokensBalancesForChainIDs(chainIds, { address }, std::move(Helpers::getKeys(tokens)));
|
||||
if(accountBalances.size() == 1) {
|
||||
for(const auto& accountAndBalance : accountBalances.begin()->second) {
|
||||
auto asset = Helpers::makeSharedQObject<WalletAsset>(std::make_shared<WalletGo::Token>(tokens.at(accountAndBalance.first)), accountAndBalance.second);
|
||||
m_assets->push_back(asset);
|
||||
}
|
||||
}
|
||||
else
|
||||
qWarning() << "Failed fetching balances for account" << address.get() << "; balances count" << accountBalances.size();
|
||||
}
|
||||
|
||||
bool AccountAssetsController::isTokenEnabled(const StatusGo::Wallet::Token& token) const
|
||||
{
|
||||
return find_if(m_enabledTokens.begin(), m_enabledTokens.end(), [&token](const auto& symbol) {
|
||||
return token.symbol == symbol;
|
||||
}) != m_enabledTokens.end();
|
||||
}
|
||||
|
||||
QAbstractItemModel* AccountAssetsController::assetsModel() const
|
||||
{
|
||||
return m_assets.get();
|
||||
}
|
||||
|
||||
float AccountAssetsController::totalValue() const
|
||||
{
|
||||
return m_totalValue;
|
||||
}
|
||||
|
||||
bool AccountAssetsController::assetsReady() const
|
||||
{
|
||||
return m_assetsReady;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Status::Wallet {
|
||||
|
||||
DerivedWalletAddress::DerivedWalletAddress(GoWallet::DerivedAddress address, QObject *parent)
|
||||
DerivedWalletAddress::DerivedWalletAddress(WalletGo::DerivedAddress address, QObject *parent)
|
||||
: QObject{parent}
|
||||
, m_derivedAddress{std::move(address)}
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <ranges>
|
||||
|
||||
namespace GoAccounts = Status::StatusGo::Accounts;
|
||||
namespace GoWallet = Status::StatusGo::Wallet;
|
||||
namespace WalletGo = Status::StatusGo::Wallet;
|
||||
namespace UtilsSG = Status::StatusGo::Utils;
|
||||
namespace StatusGo = Status::StatusGo;
|
||||
|
||||
|
@ -31,7 +31,6 @@ NewWalletAccountController::NewWalletAccountController(std::shared_ptr<Helpers::
|
|||
{
|
||||
}
|
||||
|
||||
|
||||
QAbstractListModel* NewWalletAccountController::mainAccountsModel()
|
||||
{
|
||||
return &m_mainAccounts;
|
||||
|
@ -107,7 +106,7 @@ bool NewWalletAccountController::retrieveAndUpdateDerivedAddresses(const QString
|
|||
auto maxPageCount = static_cast<int>(std::ceil(static_cast<double>(m_maxDerivedAddresses)/static_cast<double>(m_derivedAddressesPageSize)));
|
||||
std::shared_ptr<DerivedWalletAddress> foundEntry;
|
||||
while(currentPage <= maxPageCount && foundIndex < 0) {
|
||||
auto all = GoWallet::getDerivedAddressesForPath(StatusGo::HashedPassword(UtilsSG::hashPassword(password)),
|
||||
auto all = WalletGo::getDerivedAddressesForPath(StatusGo::HashedPassword(UtilsSG::hashPassword(password)),
|
||||
derivedFrom->data().derivedFrom.value(),
|
||||
Status::Constants::General::PathWalletRoot,
|
||||
m_derivedAddressesPageSize, currentPage);
|
||||
|
@ -115,7 +114,7 @@ bool NewWalletAccountController::retrieveAndUpdateDerivedAddresses(const QString
|
|||
m_derivedAddress.resize(currentIndex + all.size());
|
||||
|
||||
for(auto newDerived : all) {
|
||||
auto newEntry = std::make_shared<DerivedWalletAddress>(std::move(newDerived));
|
||||
auto newEntry = Helpers::makeSharedQObject<DerivedWalletAddress>(std::move(newDerived));
|
||||
m_derivedAddress.set(currentIndex, newEntry);
|
||||
if(foundIndex < 0 && !newEntry->data().alreadyCreated) {
|
||||
foundIndex = currentIndex;
|
||||
|
@ -147,7 +146,7 @@ WalletAccountPtr NewWalletAccountController::findMissingAccount()
|
|||
return std::none_of(m_accounts->objects().begin(), m_accounts->objects().end(),
|
||||
[&a](const auto &eA) { return a.address == eA->data().address; });
|
||||
});
|
||||
return it != accounts.end() ? std:: make_shared<WalletAccount>(*it) : nullptr;
|
||||
return it != accounts.end() ? Helpers::makeSharedQObject<WalletAccount>(*it) : nullptr;
|
||||
}
|
||||
|
||||
NewWalletAccountController::AccountsModel::ObjectContainer
|
||||
|
|
|
@ -13,7 +13,7 @@ const QString &WalletAccount::name() const
|
|||
return m_data.name;
|
||||
}
|
||||
|
||||
const QString &WalletAccount::address() const
|
||||
const QString &WalletAccount::strAddress() const
|
||||
{
|
||||
return m_data.address.get();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
#include "WalletAsset.h"
|
||||
|
||||
#include <boost/multiprecision/number.hpp>
|
||||
|
||||
namespace Status::Wallet {
|
||||
|
||||
WalletAsset::WalletAsset(const WalletGo::TokenPtr token, StatusGo::Wallet::BigInt balance, QObject *parent)
|
||||
: QObject{parent}
|
||||
, m_token(token)
|
||||
, m_balance(std::move(balance))
|
||||
{
|
||||
}
|
||||
|
||||
const QString WalletAsset::name() const
|
||||
{
|
||||
return m_token ? m_token->name : u""_qs;
|
||||
}
|
||||
|
||||
const QString WalletAsset::symbol() const
|
||||
{
|
||||
return m_token ? m_token->symbol : u""_qs;
|
||||
}
|
||||
|
||||
const QColor WalletAsset::color() const
|
||||
{
|
||||
return m_token ? m_token->color : QColor();
|
||||
}
|
||||
|
||||
quint64 WalletAsset::count() const
|
||||
{
|
||||
return (m_token)
|
||||
? static_cast<quint64>(m_balance/boost::multiprecision::pow(WalletGo::BigInt(10), m_token->decimals))
|
||||
: 0;
|
||||
}
|
||||
|
||||
float WalletAsset::value() const
|
||||
{
|
||||
if(m_token) {
|
||||
const int mantissaDigits = m_token->decimals > 3 ? 3 : 0;
|
||||
const auto scale = 10 * mantissaDigits;
|
||||
return static_cast<float>((m_balance*scale)/boost::multiprecision::pow(WalletGo::BigInt(10), m_token->decimals))/scale;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
#include "Status/Wallet/WalletController.h"
|
||||
|
||||
#include "NewWalletAccountController.h"
|
||||
#include "AccountAssetsController.h"
|
||||
|
||||
#include <StatusGo/Wallet/WalletApi.h>
|
||||
|
||||
|
@ -16,14 +18,14 @@
|
|||
#include <QJSEngine>
|
||||
|
||||
namespace GoAccounts = Status::StatusGo::Accounts;
|
||||
namespace GoWallet = Status::StatusGo::Wallet;
|
||||
namespace WalletGo = Status::StatusGo::Wallet;
|
||||
namespace UtilsSG = Status::StatusGo::Utils;
|
||||
namespace StatusGo = Status::StatusGo;
|
||||
|
||||
namespace Status::Wallet {
|
||||
|
||||
WalletController::WalletController()
|
||||
: m_accounts(std::make_shared<AccountsModel>(std::move(getWalletAccounts()), "account"))
|
||||
: m_accounts(Helpers::makeSharedQObject<AccountsModel>(std::move(getWalletAccounts()), "account"))
|
||||
, m_currentAccount(m_accounts->get(0))
|
||||
{
|
||||
}
|
||||
|
@ -60,13 +62,18 @@ void WalletController::setCurrentAccountIndex(int index)
|
|||
emit currentAccountChanged();
|
||||
}
|
||||
|
||||
AccountAssetsController *WalletController::createAccountAssetsController(WalletAccount *account)
|
||||
{
|
||||
return new AccountAssetsController(account);
|
||||
}
|
||||
|
||||
std::vector<WalletAccountPtr> WalletController::getWalletAccounts(bool rootWalletAccountsOnly) const
|
||||
{
|
||||
auto all = GoAccounts::getAccounts();
|
||||
std::vector<WalletAccountPtr> result;
|
||||
for(auto account : all) {
|
||||
if(!account.isChat && (!rootWalletAccountsOnly || account.isWallet))
|
||||
result.push_back(std::make_shared<WalletAccount>(std::move(account)));
|
||||
result.push_back(Helpers::makeSharedQObject<WalletAccount>(std::move(account)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include <StatusGo/Accounts/AccountsAPI.h>
|
||||
|
||||
#include <StatusGo/Wallet/WalletApi.h>
|
||||
|
||||
#include <StatusGo/Metadata/api_response.h>
|
||||
|
||||
#include <Onboarding/Accounts/AccountsServiceInterface.h>
|
||||
|
@ -223,14 +225,14 @@ TEST(WalletApi, TestGetTokensBalancesForChainIDs)
|
|||
ASSERT_GT(balances.size(), 0);
|
||||
|
||||
ASSERT_TRUE(balances.contains(testAddress));
|
||||
const auto &addressBallance = balances[testAddress];
|
||||
ASSERT_GT(addressBallance.size(), 0);
|
||||
const auto &addressBalance = balances[testAddress];
|
||||
ASSERT_GT(addressBalance.size(), 0);
|
||||
|
||||
ASSERT_TRUE(addressBallance.contains(sntMain.address));
|
||||
ASSERT_EQ(toQString(addressBallance.at(sntMain.address)), "0");
|
||||
ASSERT_TRUE(addressBalance.contains(sntMain.address));
|
||||
ASSERT_EQ(toQString(addressBalance.at(sntMain.address)), "0");
|
||||
|
||||
ASSERT_TRUE(addressBallance.contains(sntTest.address));
|
||||
ASSERT_EQ(toQString(addressBallance.at(sntTest.address)), "0");
|
||||
ASSERT_TRUE(addressBalance.contains(sntTest.address));
|
||||
ASSERT_EQ(toQString(addressBalance.at(sntTest.address)), "0");
|
||||
}
|
||||
|
||||
TEST(WalletApi, TestGetTokensBalancesForChainIDs_WatchOnlyAccount)
|
||||
|
@ -267,11 +269,14 @@ TEST(WalletApi, TestGetTokensBalancesForChainIDs_WatchOnlyAccount)
|
|||
ASSERT_GT(balances.size(), 0);
|
||||
|
||||
ASSERT_TRUE(balances.contains(newAccount.address));
|
||||
const auto &addressBallance = balances[newAccount.address];
|
||||
ASSERT_GT(addressBallance.size(), 0);
|
||||
const auto &addressBalance = balances[newAccount.address];
|
||||
ASSERT_GT(addressBalance.size(), 0);
|
||||
|
||||
ASSERT_TRUE(addressBallance.contains(sntMain.address));
|
||||
ASSERT_GT(addressBallance.at(sntMain.address), 0);
|
||||
ASSERT_TRUE(addressBalance.contains(sntMain.address));
|
||||
ASSERT_GT(addressBalance.at(sntMain.address), 0);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// "{"jsonrpc":"2.0","id":0,"error":{"code":-32000,"message":"no tokens for this network"}}"
|
||||
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in New Issue