diff --git a/CMakeLists.txt b/CMakeLists.txt index 43bd1da..cc4c303 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,9 +70,6 @@ endif() # Find Qt packages find_package(Qt6 REQUIRED COMPONENTS Core Widgets RemoteObjects Quick QuickWidgets) -# Get the real path to handle symlinks correctly -get_filename_component(REAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH) - # Try to find the component-interfaces package first find_package(component-interfaces QUIET) @@ -115,68 +112,20 @@ if(_cpp_sdk_is_source) ) endif() -# Run Logos C++ generator on metadata before compilation (only for source layout) -set(METADATA_JSON "${CMAKE_CURRENT_SOURCE_DIR}/metadata.json") - -# Only run generator for source layout - nix builds already have generated files -if(_cpp_sdk_is_source) - # Source layout: build and run the generator - # Generate into build directory - set(PLUGINS_OUTPUT_DIR "${CMAKE_BINARY_DIR}/generated_code") - set(CPP_GENERATOR_BUILD_DIR "${LOGOS_CPP_SDK_ROOT}/../build/cpp-generator") - set(CPP_GENERATOR "${CPP_GENERATOR_BUILD_DIR}/bin/logos-cpp-generator") - - if(NOT TARGET cpp_generator_build) - add_custom_target(cpp_generator_build - COMMAND bash "${LOGOS_CPP_SDK_ROOT}/cpp-generator/compile.sh" - WORKING_DIRECTORY "${LOGOS_CPP_SDK_ROOT}/.." - COMMENT "Building logos-cpp-generator via ${LOGOS_CPP_SDK_ROOT}/cpp-generator/compile.sh" - VERBATIM - ) - endif() - - add_custom_target(run_cpp_generator_blockchain_ui - COMMAND "${CPP_GENERATOR}" --metadata "${METADATA_JSON}" --general-only --output-dir "${PLUGINS_OUTPUT_DIR}" - WORKING_DIRECTORY "${LOGOS_CPP_SDK_ROOT}/.." - COMMENT "Running logos-cpp-generator on ${METADATA_JSON} with output-dir ${PLUGINS_OUTPUT_DIR}" - VERBATIM - ) - add_dependencies(run_cpp_generator_blockchain_ui cpp_generator_build) - - # Add generated logos_sdk.cpp - will be generated by run_cpp_generator_blockchain_ui - list(APPEND SOURCES ${PLUGINS_OUTPUT_DIR}/logos_sdk.cpp) - # Mark as generated so CMake knows to wait for it - set_source_files_properties( - ${PLUGINS_OUTPUT_DIR}/logos_sdk.cpp - PROPERTIES GENERATED TRUE - ) -else() - # Installed/nix layout: lib.nix preConfigure populates generated_code before CMake. - # May be overridden by -DPLUGINS_OUTPUT_DIR= when source tree is read-only (e.g. Nix sandbox). - set(PLUGINS_OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated_code" CACHE PATH "Generated code directory (nix layout)") - list(APPEND SOURCES ${PLUGINS_OUTPUT_DIR}/include/logos_sdk.cpp) -endif() - # Create the plugin library add_library(blockchain_ui SHARED ${SOURCES}) -# Set output name without lib prefix and with correct name for generator +# Set output name without lib prefix set_target_properties(blockchain_ui PROPERTIES PREFIX "" OUTPUT_NAME "blockchain_ui" ) -# Ensure generator runs before building the plugin (only for source layout) -if(_cpp_sdk_is_source) - add_dependencies(blockchain_ui run_cpp_generator_blockchain_ui) -endif() - # Include directories target_include_directories(blockchain_ui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR} - ${PLUGINS_OUTPUT_DIR} ) # Add include directories based on layout type @@ -189,16 +138,12 @@ endif() if(_cpp_sdk_is_source) target_include_directories(blockchain_ui PRIVATE ${LOGOS_CPP_SDK_ROOT}/cpp - ${LOGOS_CPP_SDK_ROOT}/cpp/generated ) else() - # For nix builds, also include the include subdirectory - # (lib.nix moves header files to ./generated_code/include/) target_include_directories(blockchain_ui PRIVATE ${LOGOS_CPP_SDK_ROOT}/include ${LOGOS_CPP_SDK_ROOT}/include/cpp ${LOGOS_CPP_SDK_ROOT}/include/core - ${PLUGINS_OUTPUT_DIR}/include ) endif() @@ -212,10 +157,14 @@ target_link_libraries(blockchain_ui PRIVATE component-interfaces ) -# Link SDK library if using installed layout +# When using installed SDK layout (e.g. Nix), link the pre-built SDK library if(NOT _cpp_sdk_is_source) - find_library(LOGOS_SDK_LIB logos_sdk PATHS ${LOGOS_CPP_SDK_ROOT}/lib NO_DEFAULT_PATH REQUIRED) - target_link_libraries(blockchain_ui PRIVATE ${LOGOS_SDK_LIB}) + find_library(LOGOS_SDK_LIB logos_sdk PATHS ${LOGOS_CPP_SDK_ROOT}/lib NO_DEFAULT_PATH) + if(LOGOS_SDK_LIB) + target_link_libraries(blockchain_ui PRIVATE ${LOGOS_SDK_LIB}) + else() + message(FATAL_ERROR "logos_sdk library not found in ${LOGOS_CPP_SDK_ROOT}/lib - required when using installed SDK layout") + endif() endif() # Link against Abseil libraries if found @@ -260,13 +209,8 @@ install(TARGETS blockchain_ui ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules ) -install(FILES ${METADATA_JSON} - DESTINATION ${CMAKE_INSTALL_DATADIR}/logos-blockchain-ui-new -) - -install(DIRECTORY "${PLUGINS_OUTPUT_DIR}/" - DESTINATION ${CMAKE_INSTALL_DATADIR}/logos-blockchain-ui-new/generated - OPTIONAL +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/metadata.json + DESTINATION ${CMAKE_INSTALL_DATADIR}/logos-blockchain-ui ) # Print status messages diff --git a/README.md b/README.md index f04238d..14cc923 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# logos-blockchain-ui-new +# logos-blockchain-ui A Qt UI plugin for the Logos Blockchain Module, providing a graphical interface to control and monitor the Logos blockchain node. @@ -62,7 +62,7 @@ After building the app with `nix build '.#app'`, you can run it: ./result/bin/logos-blockchain-ui-app ``` -The app will automatically load the required modules (capability_module, liblogos_blockchain_module) and the blockchain_ui Qt plugin. All dependencies are bundled in the Nix store layout. +The app will automatically load the required modules (capability_module, liblogos-blockchain-module) and the blockchain_ui Qt plugin. All dependencies are bundled in the Nix store layout. #### Nix Organization @@ -95,7 +95,7 @@ result/ │ └── Logos/DesignSystem/ # QML design system ├── modules/ │ ├── capability_module_plugin.dylib -│ ├── liblogos_blockchain_module.dylib +│ ├── liblogos-blockchain-module.dylib │ └── liblogos_blockchain.dylib └── blockchain_ui.dylib # Qt plugin (loaded by app) ``` diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index bdb008d..494d131 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -15,36 +15,15 @@ if(NOT DEFINED LOGOS_LIBLOGOS_ROOT) message(FATAL_ERROR "LOGOS_LIBLOGOS_ROOT must be defined") endif() -# Find logos-cpp-sdk -if(NOT DEFINED LOGOS_CPP_SDK_ROOT) - message(FATAL_ERROR "LOGOS_CPP_SDK_ROOT must be defined") -endif() - message(STATUS "Using logos-liblogos at: ${LOGOS_LIBLOGOS_ROOT}") -message(STATUS "Using logos-cpp-sdk at: ${LOGOS_CPP_SDK_ROOT}") -# Check if logos_sdk library exists -if(NOT EXISTS "${LOGOS_CPP_SDK_ROOT}/lib/liblogos_sdk.a" AND NOT EXISTS "${LOGOS_CPP_SDK_ROOT}/lib/liblogos_sdk.dylib" AND NOT EXISTS "${LOGOS_CPP_SDK_ROOT}/lib/liblogos_sdk.so") - message(WARNING "logos_sdk library not found in ${LOGOS_CPP_SDK_ROOT}/lib/") - message(STATUS "Available files in ${LOGOS_CPP_SDK_ROOT}/lib/:") - file(GLOB SDK_LIB_FILES "${LOGOS_CPP_SDK_ROOT}/lib/*") - foreach(file ${SDK_LIB_FILES}) - message(STATUS " ${file}") - endforeach() -endif() - -# Include directories - the new structure has headers in /include with subdirectories +# Include and link directories (app only uses logos_core from liblogos, not the SDK) include_directories( ${LOGOS_LIBLOGOS_ROOT}/include - ${LOGOS_CPP_SDK_ROOT}/include - ${LOGOS_CPP_SDK_ROOT}/include/cpp - ${LOGOS_CPP_SDK_ROOT}/include/core ) -# Link directories link_directories( ${LOGOS_LIBLOGOS_ROOT}/lib - ${LOGOS_CPP_SDK_ROOT}/lib ) # Set output directories @@ -62,7 +41,6 @@ target_link_libraries(logos-blockchain-ui-app PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets logos_core - logos_sdk ) # Set RPATH settings for the executable diff --git a/app/main.cpp b/app/main.cpp index 6c0ba25..62a6e72 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) qWarning() << "Failed to load capability_module plugin"; } - if (!logos_core_load_plugin("liblogos_blockchain_module")) { + if (!logos_core_load_plugin("liblogos-blockchain-module")) { qWarning() << "Failed to load blockchain module plugin"; } diff --git a/flake.lock b/flake.lock index 7690be4..c900bc7 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1769737823, - "narHash": "sha256-DrBaNpZ+sJ4stXm+0nBX7zqZT9t9P22zbk6m5YhQxS4=", + "lastModified": 1770419512, + "narHash": "sha256-o8Vcdz6B6bkiGUYkZqFwH3Pv1JwZyXht3dMtS7RchIo=", "owner": "ipetkov", "repo": "crane", - "rev": "b2f45c3830aa96b7456a4c4bc327d04d7a43e1ba", + "rev": "2510f2cbc3ccd237f700bb213756a8f35c32d8d7", "type": "github" }, "original": { @@ -23,11 +23,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1770888466, - "narHash": "sha256-7IJz+UIwa8QPg81cvqunpnpO87VUdWt0TcPz7HGBnYE=", + "lastModified": 1771418281, + "narHash": "sha256-9nATUijKs7OCEcepHKghuWdgHu+hq11JbJz2Lv4uyDU=", "owner": "logos-blockchain", "repo": "logos-blockchain", - "rev": "3ef7a137b91c4a5a7708405815354ff58e0e179c", + "rev": "b862e6f640a79097b8a42c072e1f78bc430fa222", "type": "github" }, "original": { @@ -41,11 +41,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1769780029, - "narHash": "sha256-B2CYcJWuJIXAJaWgBcY4k0FTxo62mI8Hd9RqBECfq4o=", + "lastModified": 1770979891, + "narHash": "sha256-cvkVnE7btuFLzv70ORAZve9K1Huiplq0iECgXSXb0ZY=", "owner": "logos-blockchain", "repo": "logos-blockchain-circuits", - "rev": "480b9bc4fddb4643f528d607f046f27610439a78", + "rev": "ec7d298e5a3a0507bb8570df86cdf78dc452d024", "type": "github" }, "original": { @@ -67,17 +67,17 @@ ] }, "locked": { - "lastModified": 1770934619, - "narHash": "sha256-DyksgOrea/gktElcOZmMDUcYW45JPpkDA5BnPkwVmmc=", + "lastModified": 1771423304, + "narHash": "sha256-ONurMDUbFhdNzdbQ5r1jPPzFm2bHJBRElwcRNPPFmpM=", "owner": "logos-blockchain", "repo": "logos-blockchain-module", - "rev": "578308270ecfe7463a94ac50cae0584451c135ef", + "rev": "214a87885115296bf4e56fd820f5fbfdf4ee4453", "type": "github" }, "original": { "owner": "logos-blockchain", "repo": "logos-blockchain-module", - "rev": "578308270ecfe7463a94ac50cae0584451c135ef", + "rev": "214a87885115296bf4e56fd820f5fbfdf4ee4453", "type": "github" } }, @@ -1164,11 +1164,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1769461804, - "narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=", + "lastModified": 1770841267, + "narHash": "sha256-9xejG0KoqsoKEGp2kVbXRlEYtFFcDTHjidiuX8hGO44=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d", + "rev": "ec7c70d12ce2fc37cb92aff673dcdca89d187bae", "type": "github" }, "original": { @@ -1392,11 +1392,11 @@ ] }, "locked": { - "lastModified": 1769742225, - "narHash": "sha256-roSD/OJ3x9nF+Dxr+/bLClX3U8FP9EkCQIFpzxKjSUM=", + "lastModified": 1770952264, + "narHash": "sha256-CjymNrJZWBtpavyuTkfPVPaZkwzIzGaf0E/3WgcwM14=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "bcdd8d37594f0e201639f55889c01c827baf5c75", + "rev": "ec6a3d5cdf14bb5a1dd03652bd3f6351004d2188", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 6c28395..64af90d 100644 --- a/flake.nix +++ b/flake.nix @@ -6,7 +6,7 @@ nixpkgs.follows = "logos-liblogos/nixpkgs"; logos-cpp-sdk.url = "github:logos-co/logos-cpp-sdk"; logos-liblogos.url = "github:logos-co/logos-liblogos"; - logos-blockchain-module.url = "github:logos-blockchain/logos-blockchain-module/578308270ecfe7463a94ac50cae0584451c135ef"; + logos-blockchain-module.url = "github:logos-blockchain/logos-blockchain-module"; logos-capability-module.url = "github:logos-co/logos-capability-module"; logos-design-system.url = "github:logos-co/logos-design-system"; logos-design-system.inputs.nixpkgs.follows = "nixpkgs"; @@ -35,12 +35,12 @@ # Library package (default blockchain-module has lib + include via symlinkJoin) lib = import ./nix/lib.nix { - inherit pkgs common src logosBlockchainModule logosSdk; + inherit pkgs common src logosBlockchainModule; }; # App package app = import ./nix/app.nix { - inherit pkgs common src logosLiblogos logosSdk logosBlockchainModule logosCapabilityModule logosDesignSystem; + inherit pkgs common src logosLiblogos logosBlockchainModule logosCapabilityModule logosDesignSystem; logosBlockchainUI = lib; }; in @@ -71,11 +71,9 @@ ]; shellHook = '' - export LOGOS_CPP_SDK_ROOT="${logosSdk}" export LOGOS_LIBLOGOS_ROOT="${logosLiblogos}" export LOGOS_DESIGN_SYSTEM_ROOT="${logosDesignSystem}" echo "Logos Blockchain UI development environment" - echo "LOGOS_CPP_SDK_ROOT: $LOGOS_CPP_SDK_ROOT" echo "LOGOS_LIBLOGOS_ROOT: $LOGOS_LIBLOGOS_ROOT" ''; }; diff --git a/metadata.json b/metadata.json index 60ecd3b..48d23f0 100644 --- a/metadata.json +++ b/metadata.json @@ -5,7 +5,8 @@ "author": "Logos Blockchain Team", "type": "ui", "main": "blockchain_ui", - "dependencies": ["liblogos_blockchain_module"], + "icon": ":/icons/blockchain.png", + "dependencies": ["liblogos-blockchain-module"], "category": "blockchain", "build": { "type": "cmake", diff --git a/nix/app.nix b/nix/app.nix index 0e0b587..a55d7e3 100644 --- a/nix/app.nix +++ b/nix/app.nix @@ -1,15 +1,14 @@ # Builds the logos-blockchain-ui-app standalone application -{ pkgs, common, src, logosLiblogos, logosSdk, logosBlockchainModule, logosCapabilityModule, logosBlockchainUI, logosDesignSystem }: +{ pkgs, common, src, logosLiblogos, logosBlockchainModule, logosCapabilityModule, logosBlockchainUI, logosDesignSystem }: pkgs.stdenv.mkDerivation rec { pname = "logos-blockchain-ui-app"; version = common.version; inherit src; - inherit (common) buildInputs cmakeFlags meta; + inherit (common) buildInputs meta; - # Add logosSdk to nativeBuildInputs for logos-cpp-generator - nativeBuildInputs = common.nativeBuildInputs ++ [ logosSdk pkgs.patchelf pkgs.removeReferencesTo ]; + nativeBuildInputs = common.nativeBuildInputs ++ [ pkgs.patchelf pkgs.removeReferencesTo ]; # Provide Qt/GL runtime paths so the wrapper can inject them qtLibPath = pkgs.lib.makeLibraryPath ( @@ -55,31 +54,7 @@ pkgs.stdenv.mkDerivation rec { preConfigure = '' runHook prePreConfigure - - # Set macOS deployment target to match Qt frameworks export MACOSX_DEPLOYMENT_TARGET=12.0 - - # Copy logos-cpp-sdk headers to expected location - echo "Copying logos-cpp-sdk headers for app..." - mkdir -p ./logos-cpp-sdk/include/cpp - cp -r ${logosSdk}/include/cpp/* ./logos-cpp-sdk/include/cpp/ - - # Also copy core headers - echo "Copying core headers..." - mkdir -p ./logos-cpp-sdk/include/core - cp -r ${logosSdk}/include/core/* ./logos-cpp-sdk/include/core/ - - # Copy SDK library files to lib directory - echo "Copying SDK library files..." - mkdir -p ./logos-cpp-sdk/lib - if [ -f "${logosSdk}/lib/liblogos_sdk.dylib" ]; then - cp "${logosSdk}/lib/liblogos_sdk.dylib" ./logos-cpp-sdk/lib/ - elif [ -f "${logosSdk}/lib/liblogos_sdk.so" ]; then - cp "${logosSdk}/lib/liblogos_sdk.so" ./logos-cpp-sdk/lib/ - elif [ -f "${logosSdk}/lib/liblogos_sdk.a" ]; then - cp "${logosSdk}/lib/liblogos_sdk.a" ./logos-cpp-sdk/lib/ - fi - runHook postPreConfigure ''; @@ -122,16 +97,8 @@ pkgs.stdenv.mkDerivation rec { runHook preConfigure echo "Configuring logos-blockchain-ui-app..." - echo "liblogos: ${logosLiblogos}" - echo "cpp-sdk: ${logosSdk}" - echo "blockchain-module: ${logosBlockchainModule}" - echo "capability-module: ${logosCapabilityModule}" - echo "blockchain-ui: ${logosBlockchainUI}" - echo "logos-design-system: ${logosDesignSystem}" - - # Verify that the built components exist + test -d "${logosLiblogos}" || (echo "liblogos not found" && exit 1) - test -d "${logosSdk}" || (echo "cpp-sdk not found" && exit 1) test -d "${logosBlockchainModule}" || (echo "blockchain-module not found" && exit 1) test -d "${logosCapabilityModule}" || (echo "capability-module not found" && exit 1) test -d "${logosBlockchainUI}" || (echo "blockchain-ui not found" && exit 1) @@ -144,8 +111,7 @@ pkgs.stdenv.mkDerivation rec { -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE \ -DCMAKE_INSTALL_RPATH="" \ -DCMAKE_SKIP_BUILD_RPATH=TRUE \ - -DLOGOS_LIBLOGOS_ROOT=${logosLiblogos} \ - -DLOGOS_CPP_SDK_ROOT=$(pwd)/logos-cpp-sdk + -DLOGOS_LIBLOGOS_ROOT=${logosLiblogos} runHook postConfigure ''; @@ -186,11 +152,6 @@ pkgs.stdenv.mkDerivation rec { cp -L "${logosLiblogos}/lib/"liblogos_core.* "$out/lib/" || true fi - # Copy SDK library if it exists - if ls "${logosSdk}/lib/"liblogos_sdk.* >/dev/null 2>&1; then - cp -L "${logosSdk}/lib/"liblogos_sdk.* "$out/lib/" || true - fi - # Determine platform-specific plugin extension OS_EXT="so" case "$(uname -s)" in @@ -203,8 +164,8 @@ pkgs.stdenv.mkDerivation rec { if [ -f "${logosCapabilityModule}/lib/capability_module_plugin.$OS_EXT" ]; then cp -L "${logosCapabilityModule}/lib/capability_module_plugin.$OS_EXT" "$out/modules/" fi - if [ -f "${logosBlockchainModule}/lib/liblogos_blockchain_module.$OS_EXT" ]; then - cp -L "${logosBlockchainModule}/lib/liblogos_blockchain_module.$OS_EXT" "$out/modules/" + if [ -f "${logosBlockchainModule}/lib/liblogos-blockchain-module.$OS_EXT" ]; then + cp -L "${logosBlockchainModule}/lib/liblogos-blockchain-module.$OS_EXT" "$out/modules/" fi # Copy liblogos_blockchain library to modules directory (needed by blockchain module) @@ -229,25 +190,20 @@ pkgs.stdenv.mkDerivation rec { echo "Copied Logos.Controls to lib/Logos/Controls/" fi - # Create a README for reference cat > $out/README.txt </dev/null || true exit 1 fi - - # Also install the generated include files - if [ -d "./generated_code/include" ]; then - mkdir -p $out/include - cp -r ./generated_code/include/* $out/include/ - echo "Installed generated include files:" - ls -la $out/include/ - fi - + runHook postInstall ''; } diff --git a/src/BlockchainBackend.cpp b/src/BlockchainBackend.cpp index 099d6bc..e6deffd 100644 --- a/src/BlockchainBackend.cpp +++ b/src/BlockchainBackend.cpp @@ -1,35 +1,55 @@ #include "BlockchainBackend.h" +#include #include #include +#include +#include #include +namespace { + const char SETTINGS_ORG[] = "Logos"; + const char SETTINGS_APP[] = "BlockchainUI"; + const char CONFIG_PATH_KEY[] = "configPath"; + const QString BLOCKCHAIN_MODULE_NAME = QStringLiteral("liblogos-blockchain-module"); +} + BlockchainBackend::BlockchainBackend(LogosAPI* logosAPI, QObject* parent) : QObject(parent), m_status(NotStarted), m_configPath(""), m_logModel(new LogModel(this)), - m_logos(nullptr), - m_blockchainModule(nullptr) + m_logosAPI(nullptr), + m_blockchainClient(nullptr) { + QSettings s(SETTINGS_ORG, SETTINGS_APP); + const QString envConfigPath = QString::fromUtf8(qgetenv("LB_CONFIG_PATH")); + const QString savedConfigPath = s.value(CONFIG_PATH_KEY).toString(); - m_configPath = QString::fromUtf8(qgetenv("LB_CONFIG_PATH")); - - if (!logosAPI) { - logosAPI = new LogosAPI("core", this); + if (!envConfigPath.isEmpty()) { + m_configPath = envConfigPath; + } else if (!savedConfigPath.isEmpty()) { + m_configPath = savedConfigPath; } - m_logos = new LogosModules(logosAPI); + if (!logosAPI) { + logosAPI = new LogosAPI("blockchain_ui", this); + } - if (!m_logos) { + m_logosAPI = logosAPI; + m_blockchainClient = m_logosAPI->getClient(BLOCKCHAIN_MODULE_NAME); + + if (!m_blockchainClient) { setStatus(ErrorNotInitialized); return; } - m_blockchainModule = &m_logos->liblogos_blockchain_module; - - if (m_blockchainModule && !m_blockchainModule->on("newBlock", [this](const QVariantList& data) { - onNewBlock(data); - })) { + QObject* replica = m_blockchainClient->requestObject(BLOCKCHAIN_MODULE_NAME); + if (replica) { + replica->setParent(this); + m_blockchainClient->onEvent(replica, this, "newBlock", [this](const QString&, const QVariantList& data) { + onNewBlock(data); + }); + } else { setStatus(ErrorSubscribeFailed); } } @@ -52,6 +72,8 @@ void BlockchainBackend::setConfigPath(const QString& path) const QString localPath = QUrl::fromUserInput(path).toLocalFile(); if (m_configPath != localPath) { m_configPath = localPath; + QSettings s(SETTINGS_ORG, SETTINGS_APP); + s.setValue(CONFIG_PATH_KEY, m_configPath); emit configPathChanged(); } } @@ -63,67 +85,83 @@ void BlockchainBackend::clearLogs() QString BlockchainBackend::getBalance(const QString& addressHex) { - if (!m_blockchainModule) { + if (!m_blockchainClient) { return QStringLiteral("Error: Module not initialized."); } - // The generated proxy converts C pointer params (uint8_t*, BalanceResult*) to QVariant, - // which cannot carry raw C pointers. The module needs to expose a QString-based - // wrapper (e.g. getWalletBalanceQ) for this to work through the proxy. - Q_UNUSED(addressHex) - return QStringLiteral("Not yet available: module needs Qt-friendly wallet API."); + QVariant result = m_blockchainClient->invokeRemoteMethod(BLOCKCHAIN_MODULE_NAME, "wallet_get_balance", addressHex); + return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed."); } QString BlockchainBackend::transferFunds(const QString& fromKeyHex, const QString& toKeyHex, const QString& amountStr) { - if (!m_blockchainModule) { + if (!m_blockchainClient) { return QStringLiteral("Error: Module not initialized."); } - // Same limitation: TransferFundsArguments and Hash are C types that cannot - // pass through the QVariant-based generated proxy. - Q_UNUSED(fromKeyHex) - Q_UNUSED(toKeyHex) - Q_UNUSED(amountStr) - return QStringLiteral("Not yet available: module needs Qt-friendly wallet API."); + QVariant result = m_blockchainClient->invokeRemoteMethod( + BLOCKCHAIN_MODULE_NAME, + "wallet_transfer_funds", + fromKeyHex, + fromKeyHex, + toKeyHex, + amountStr, + QString()); + return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed."); } void BlockchainBackend::startBlockchain() { - if (!m_blockchainModule) { + if (!m_blockchainClient) { setStatus(ErrorNotInitialized); return; } setStatus(Starting); - int result = m_blockchainModule->start(m_configPath, QString()); + QVariant result = m_blockchainClient->invokeRemoteMethod( + BLOCKCHAIN_MODULE_NAME, "start", m_configPath, QString()); + int resultCode = result.isValid() ? result.toInt() : -1; - if (result == 0 || result == 1) { + if (resultCode == 0 || resultCode == 1) { setStatus(Running); - } else if (result == 2) { + QTimer::singleShot(500, this, [this]() { refreshKnownAddresses(); }); + } else if (resultCode == 2) { setStatus(ErrorConfigMissing); - } else if (result == 3) { + } else if (resultCode == 3) { setStatus(ErrorStartFailed); } else { setStatus(ErrorStartFailed); } } +void BlockchainBackend::refreshKnownAddresses() +{ + if (!m_blockchainClient) return; + QVariant result = m_blockchainClient->invokeRemoteMethod(BLOCKCHAIN_MODULE_NAME, "wallet_get_known_addresses"); + QStringList list = result.isValid() && result.canConvert() ? result.toStringList() : QStringList(); + qDebug() << "BlockchainBackend: received from blockchain lib: type=QStringList, count=" << list.size(); + if (m_knownAddresses != list) { + m_knownAddresses = std::move(list); + emit knownAddressesChanged(); + } +} + void BlockchainBackend::stopBlockchain() { if (m_status != Running && m_status != Starting) { return; } - if (!m_blockchainModule) { + if (!m_blockchainClient) { setStatus(ErrorNotInitialized); return; } setStatus(Stopping); - int result = m_blockchainModule->stop(); + QVariant result = m_blockchainClient->invokeRemoteMethod(BLOCKCHAIN_MODULE_NAME, "stop"); + int resultCode = result.isValid() ? result.toInt() : -1; - if (result == 0 || result == 1) { + if (resultCode == 0 || resultCode == 1) { setStatus(Stopped); } else { setStatus(ErrorStopFailed); diff --git a/src/BlockchainBackend.h b/src/BlockchainBackend.h index c6b6dc9..071fec2 100644 --- a/src/BlockchainBackend.h +++ b/src/BlockchainBackend.h @@ -2,15 +2,11 @@ #include #include -#include +#include #include "logos_api.h" #include "logos_api_client.h" -#include "logos_sdk.h" #include "LogModel.h" -// Type of the blockchain module proxy (has start(), stop(), on() etc.) -using BlockchainModuleProxy = std::remove_reference_t().liblogos_blockchain_module)>; - class BlockchainBackend : public QObject { Q_OBJECT @@ -33,6 +29,7 @@ public: Q_PROPERTY(BlockchainStatus status READ status NOTIFY statusChanged) Q_PROPERTY(QString configPath READ configPath WRITE setConfigPath NOTIFY configPathChanged) Q_PROPERTY(LogModel* logModel READ logModel CONSTANT) + Q_PROPERTY(QStringList knownAddresses READ knownAddresses NOTIFY knownAddressesChanged) explicit BlockchainBackend(LogosAPI* logosAPI = nullptr, QObject* parent = nullptr); ~BlockchainBackend(); @@ -40,13 +37,18 @@ public: BlockchainStatus status() const { return m_status; } QString configPath() const { return m_configPath; } LogModel* logModel() const { return m_logModel; } + QStringList knownAddresses() const { return m_knownAddresses; } void setConfigPath(const QString& path); Q_INVOKABLE void clearLogs(); Q_INVOKABLE QString getBalance(const QString& addressHex); - Q_INVOKABLE QString transferFunds(const QString& fromKeyHex, const QString& toKeyHex, const QString& amountStr); + Q_INVOKABLE QString transferFunds( + const QString& fromKeyHex, + const QString& toKeyHex, + const QString& amountStr); Q_INVOKABLE void startBlockchain(); Q_INVOKABLE void stopBlockchain(); + Q_INVOKABLE void refreshKnownAddresses(); public slots: void onNewBlock(const QVariantList& data); @@ -54,6 +56,7 @@ public slots: signals: void statusChanged(); void configPathChanged(); + void knownAddressesChanged(); private: void setStatus(BlockchainStatus newStatus); @@ -61,7 +64,8 @@ private: BlockchainStatus m_status; QString m_configPath; LogModel* m_logModel; + QStringList m_knownAddresses; - LogosModules* m_logos; - BlockchainModuleProxy* m_blockchainModule; + LogosAPI* m_logosAPI; + LogosAPIClient* m_blockchainClient; }; diff --git a/src/blockchain_resources.qrc b/src/blockchain_resources.qrc index 0455c72..9286533 100644 --- a/src/blockchain_resources.qrc +++ b/src/blockchain_resources.qrc @@ -5,5 +5,6 @@ qml/views/StatusConfigView.qml qml/views/LogsView.qml qml/views/WalletView.qml + icons/blockchain.png diff --git a/src/icons/blockchain.png b/src/icons/blockchain.png new file mode 100644 index 0000000..40b720f Binary files /dev/null and b/src/icons/blockchain.png differ diff --git a/src/qml/BlockchainView.qml b/src/qml/BlockchainView.qml index 34caa37..7d43b2c 100644 --- a/src/qml/BlockchainView.qml +++ b/src/qml/BlockchainView.qml @@ -78,6 +78,7 @@ Rectangle { WalletView { id: walletView Layout.preferredWidth: parent.width / 2 + knownAddresses: backend.knownAddresses onGetBalanceRequested: function(addressHex) { walletView.setBalanceResult(backend.getBalance(addressHex)) diff --git a/src/qml/views/WalletView.qml b/src/qml/views/WalletView.qml index 8d1f410..f6063cd 100644 --- a/src/qml/views/WalletView.qml +++ b/src/qml/views/WalletView.qml @@ -8,6 +8,9 @@ import Logos.Controls ColumnLayout { id: root + // list of known wallet addresses for Get balance dropdown + property var knownAddresses: [] + // --- Public API --- signal getBalanceRequested(string addressHex) signal transferRequested(string fromKeyHex, string toKeyHex, string amount) @@ -25,7 +28,8 @@ ColumnLayout { // Get balance card Rectangle { Layout.fillWidth: true - Layout.preferredHeight: balanceCol.implicitHeight + implicitHeight: balanceCol.implicitHeight + 2 * Theme.spacing.large + Layout.preferredHeight: implicitHeight color: Theme.palette.backgroundTertiary radius: Theme.spacing.radiusLarge border.color: Theme.palette.border @@ -33,7 +37,9 @@ ColumnLayout { ColumnLayout { id: balanceCol - anchors.fill: parent + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top anchors.margins: Theme.spacing.large spacing: Theme.spacing.large @@ -43,23 +49,39 @@ ColumnLayout { font.bold: true } - CustomTextFeild { - id: balanceAddressField - placeholderText: qsTr("Wallet address (64 hex chars)") - } - - LogosButton { - text: qsTr("Get balance") - Layout.alignment: Qt.AlignRight - onClicked: root.getBalanceRequested(balanceAddressField.text) - } - - LogosText { - id: balanceResultText + // Dropdown of known addresses, or type a custom address + ComboBox { + id: balanceAddressCombo Layout.fillWidth: true + editable: true + model: knownAddresses font.pixelSize: Theme.typography.secondaryText - color: Theme.palette.textSecondary - wrapMode: Text.WordWrap + } + + RowLayout { + Layout.fillWidth: true + Layout.preferredHeight: balanceButton.implicitHeight + spacing: Theme.spacing.large + + LogosButton { + id: balanceButton + text: qsTr("Get balance") + onClicked: root.getBalanceRequested(balanceAddressCombo.currentText.trim()) + } + + LogosButton { + Layout.fillWidth: true + enabled: false + padding: Theme.spacing.medium + contentItem: Text { + id: balanceResultText + width: parent.width + color: Theme.palette.textSecondary + font.pixelSize: Theme.typography.secondaryText + font.weight: Theme.typography.weightMedium + wrapMode: Text.WordWrap + } + } } } } @@ -67,7 +89,7 @@ ColumnLayout { // Transfer funds card Rectangle { Layout.fillWidth: true - Layout.preferredHeight: transferCol.height + Layout.preferredHeight: transferCol.height + 2 * Theme.spacing.large color: Theme.palette.backgroundTertiary radius: Theme.spacing.radiusLarge border.color: Theme.palette.border @@ -88,6 +110,7 @@ ColumnLayout { } CustomTextFeild { + id: transferFromField placeholderText: qsTr("From key (64 hex chars)") } @@ -97,21 +120,35 @@ ColumnLayout { } CustomTextFeild { + id: transferAmountField placeholderText: qsTr("Amount") } - LogosButton { - text: qsTr("Transfer") - Layout.alignment: Qt.AlignRight - onClicked: root.transferRequested(transferFromField.text, transferToField.text, transferAmountField.text) - } - - LogosText { - id: transferResultText + RowLayout { Layout.fillWidth: true - font.pixelSize: Theme.typography.secondaryText - color: Theme.palette.textSecondary - wrapMode: Text.WordWrap + Layout.preferredHeight: transferButton.implicitHeight + spacing: Theme.spacing.large + + LogosButton { + id: transferButton + text: qsTr("Transfer") + Layout.alignment: Qt.AlignRight + onClicked: root.transferRequested(transferFromField.text, transferToField.text, transferAmountField.text) + } + + LogosButton { + Layout.fillWidth: true + enabled: false + padding: Theme.spacing.medium + contentItem: Text { + id: transferResultText + width: parent.width + color: Theme.palette.textSecondary + font.pixelSize: Theme.typography.secondaryText + font.weight: Theme.typography.weightMedium + wrapMode: Text.WordWrap + } + } } } }