mirror of
https://github.com/logos-blockchain/logos-execution-zone-wallet-ui.git
synced 2026-05-06 04:39:28 +00:00
Merge pull request #14 from logos-blockchain/feat/portToUsingModuleBuilder
feat: use new module builder and new qml + cpp backend
This commit is contained in:
commit
648bc85722
229
CMakeLists.txt
229
CMakeLists.txt
@ -1,212 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(ExecutionZoneWalletUIPlugin VERSION 1.0.0 LANGUAGES CXX)
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(LEZWalletPlugin LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
# Allow override from environment or command line
|
||||
if(NOT DEFINED LOGOS_LIBLOGOS_ROOT)
|
||||
set(_parent_liblogos "${CMAKE_SOURCE_DIR}/../logos-liblogos")
|
||||
set(_use_vendor ${LOGOS_EXECUTION_ZONE_WALLET_UI_USE_VENDOR})
|
||||
if(NOT _use_vendor)
|
||||
if(NOT EXISTS "${_parent_liblogos}/interface.h")
|
||||
set(_use_vendor ON)
|
||||
endif()
|
||||
endif()
|
||||
if(_use_vendor)
|
||||
set(LOGOS_LIBLOGOS_ROOT "${CMAKE_SOURCE_DIR}/vendor/logos-liblogos")
|
||||
else()
|
||||
set(LOGOS_LIBLOGOS_ROOT "${_parent_liblogos}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED LOGOS_CPP_SDK_ROOT)
|
||||
set(_parent_cpp_sdk "${CMAKE_SOURCE_DIR}/../logos-cpp-sdk")
|
||||
set(_use_vendor ${LOGOS_EXECUTION_ZONE_WALLET_UI_USE_VENDOR})
|
||||
if(NOT _use_vendor)
|
||||
if(NOT EXISTS "${_parent_cpp_sdk}/cpp/logos_api.h")
|
||||
set(_use_vendor ON)
|
||||
endif()
|
||||
endif()
|
||||
if(_use_vendor)
|
||||
set(LOGOS_CPP_SDK_ROOT "${CMAKE_SOURCE_DIR}/vendor/logos-cpp-sdk")
|
||||
else()
|
||||
set(LOGOS_CPP_SDK_ROOT "${_parent_cpp_sdk}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check if dependencies are available (support both source and installed layouts)
|
||||
set(_liblogos_found FALSE)
|
||||
if(EXISTS "${LOGOS_LIBLOGOS_ROOT}/interface.h")
|
||||
set(_liblogos_found TRUE)
|
||||
set(_liblogos_is_source TRUE)
|
||||
elseif(EXISTS "${LOGOS_LIBLOGOS_ROOT}/include/interface.h")
|
||||
set(_liblogos_found TRUE)
|
||||
set(_liblogos_is_source FALSE)
|
||||
endif()
|
||||
|
||||
set(_cpp_sdk_found FALSE)
|
||||
if(EXISTS "${LOGOS_CPP_SDK_ROOT}/cpp/logos_api.h")
|
||||
set(_cpp_sdk_found TRUE)
|
||||
set(_cpp_sdk_is_source TRUE)
|
||||
elseif(EXISTS "${LOGOS_CPP_SDK_ROOT}/include/cpp/logos_api.h")
|
||||
set(_cpp_sdk_found TRUE)
|
||||
set(_cpp_sdk_is_source FALSE)
|
||||
endif()
|
||||
|
||||
if(NOT _liblogos_found)
|
||||
message(FATAL_ERROR "logos-liblogos not found at ${LOGOS_LIBLOGOS_ROOT}. "
|
||||
"Set LOGOS_LIBLOGOS_ROOT or run git submodule update --init --recursive.")
|
||||
endif()
|
||||
|
||||
if(NOT _cpp_sdk_found)
|
||||
message(FATAL_ERROR "logos-cpp-sdk not found at ${LOGOS_CPP_SDK_ROOT}. "
|
||||
"Set LOGOS_CPP_SDK_ROOT or run git submodule update --init --recursive.")
|
||||
endif()
|
||||
|
||||
# Find Qt packages
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Widgets RemoteObjects Quick QuickWidgets)
|
||||
|
||||
# Try to find the component-interfaces package first
|
||||
find_package(component-interfaces QUIET)
|
||||
|
||||
# If not found, use the local interfaces folder
|
||||
if(NOT component-interfaces_FOUND)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces)
|
||||
add_library(component-interfaces INTERFACE)
|
||||
target_include_directories(component-interfaces INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/interfaces)
|
||||
endif()
|
||||
|
||||
# Source files
|
||||
set(SOURCES
|
||||
src/LEZAccountFilterModel.cpp
|
||||
src/LEZAccountFilterModel.h
|
||||
src/LEZWalletAccountModel.cpp
|
||||
src/LEZWalletAccountModel.h
|
||||
src/LEZWalletPlugin.cpp
|
||||
src/LEZWalletPlugin.h
|
||||
src/LEZWalletBackend.cpp
|
||||
src/LEZWalletBackend.h
|
||||
src/wallet_resources.qrc
|
||||
)
|
||||
|
||||
# Add SDK sources (only if source layout, installed layout uses the library)
|
||||
if(_cpp_sdk_is_source)
|
||||
list(APPEND SOURCES
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/logos_api.cpp
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/logos_api.h
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_client.cpp
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_client.h
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_consumer.cpp
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_consumer.h
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_provider.cpp
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_provider.h
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/token_manager.cpp
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/token_manager.h
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/module_proxy.cpp
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp/module_proxy.h
|
||||
)
|
||||
endif()
|
||||
|
||||
# Create the plugin library
|
||||
add_library(logos_execution_zone_wallet_ui SHARED ${SOURCES})
|
||||
|
||||
# Set output name without lib prefix
|
||||
set_target_properties(logos_execution_zone_wallet_ui PROPERTIES
|
||||
PREFIX ""
|
||||
OUTPUT_NAME "logos_execution_zone_wallet_ui"
|
||||
)
|
||||
|
||||
# Include directories
|
||||
target_include_directories(logos_execution_zone_wallet_ui PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Add include directories based on layout type
|
||||
if(_liblogos_is_source)
|
||||
target_include_directories(logos_execution_zone_wallet_ui PRIVATE ${LOGOS_LIBLOGOS_ROOT})
|
||||
if(DEFINED ENV{LOGOS_MODULE_BUILDER_ROOT})
|
||||
include($ENV{LOGOS_MODULE_BUILDER_ROOT}/cmake/LogosModule.cmake)
|
||||
else()
|
||||
target_include_directories(logos_execution_zone_wallet_ui PRIVATE ${LOGOS_LIBLOGOS_ROOT}/include)
|
||||
message(FATAL_ERROR "LogosModule.cmake not found. Set LOGOS_MODULE_BUILDER_ROOT.")
|
||||
endif()
|
||||
|
||||
if(_cpp_sdk_is_source)
|
||||
target_include_directories(logos_execution_zone_wallet_ui PRIVATE
|
||||
${LOGOS_CPP_SDK_ROOT}/cpp
|
||||
)
|
||||
else()
|
||||
target_include_directories(logos_execution_zone_wallet_ui PRIVATE
|
||||
${LOGOS_CPP_SDK_ROOT}/include
|
||||
${LOGOS_CPP_SDK_ROOT}/include/cpp
|
||||
${LOGOS_CPP_SDK_ROOT}/include/core
|
||||
)
|
||||
endif()
|
||||
|
||||
# Link against libraries
|
||||
target_link_libraries(logos_execution_zone_wallet_ui PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Widgets
|
||||
Qt6::RemoteObjects
|
||||
Qt6::Quick
|
||||
Qt6::QuickWidgets
|
||||
component-interfaces
|
||||
logos_module(
|
||||
NAME lez_wallet_ui
|
||||
REP_FILE src/LEZWalletBackend.rep
|
||||
SOURCES
|
||||
src/LEZWalletPluginInterface.h
|
||||
src/LEZWalletPlugin.h
|
||||
src/LEZWalletPlugin.cpp
|
||||
src/LEZWalletBackend.h
|
||||
src/LEZWalletBackend.cpp
|
||||
src/LEZWalletAccountModel.h
|
||||
src/LEZWalletAccountModel.cpp
|
||||
src/LEZAccountFilterModel.h
|
||||
src/LEZAccountFilterModel.cpp
|
||||
FIND_PACKAGES
|
||||
Qt6Gui
|
||||
LINK_LIBRARIES
|
||||
Qt6::Gui
|
||||
)
|
||||
|
||||
# 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)
|
||||
if(LOGOS_SDK_LIB)
|
||||
target_link_libraries(logos_execution_zone_wallet_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
|
||||
find_package(absl QUIET)
|
||||
if(absl_FOUND)
|
||||
target_link_libraries(logos_execution_zone_wallet_ui PRIVATE
|
||||
absl::base
|
||||
absl::strings
|
||||
absl::log
|
||||
absl::check
|
||||
)
|
||||
endif()
|
||||
|
||||
# Set common properties for both platforms
|
||||
set_target_properties(logos_execution_zone_wallet_ui PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules"
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
SKIP_BUILD_RPATH FALSE)
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(logos_execution_zone_wallet_ui PROPERTIES
|
||||
INSTALL_RPATH "@loader_path"
|
||||
INSTALL_NAME_DIR "@rpath"
|
||||
BUILD_WITH_INSTALL_NAME_DIR TRUE)
|
||||
add_custom_command(TARGET logos_execution_zone_wallet_ui POST_BUILD
|
||||
COMMAND install_name_tool -id "@rpath/logos_execution_zone_wallet_ui.dylib" $<TARGET_FILE:logos_execution_zone_wallet_ui>
|
||||
COMMENT "Updating library paths for macOS")
|
||||
elseif(UNIX)
|
||||
set_target_properties(logos_execution_zone_wallet_ui PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN"
|
||||
INSTALL_RPATH_USE_LINK_PATH FALSE)
|
||||
endif()
|
||||
# Windows: DLLs are found via PATH or same directory; no RPATH
|
||||
|
||||
install(TARGETS logos_execution_zone_wallet_ui
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules
|
||||
)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/metadata.json
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/logos-execution-zone-wallet-ui
|
||||
)
|
||||
|
||||
message(STATUS "Execution Zone Wallet UI Plugin configured successfully")
|
||||
|
||||
124
README.md
124
README.md
@ -1,65 +1,129 @@
|
||||
# logos-execution-zone-wallet-ui
|
||||
|
||||
A Qt UI plugin for the Logos Execution Zone Wallet Module, providing a graphical interface to manage execution zone wallet accounts and transfers.
|
||||
A QML + C++ backend UI module for the [Logos](https://logos.co) platform that provides a graphical interface to manage execution zone wallet accounts and transfers.
|
||||
|
||||
Built with [`logos-module-builder`](https://github.com/logos-co/logos-module-builder) using the `mkLogosQmlModule` pattern (QML frontend + C++ backend with Qt Remote Objects).
|
||||
|
||||
## Features
|
||||
|
||||
- Create and list public/private accounts
|
||||
- View account balances
|
||||
- Sync to block height
|
||||
- Public and private transfers
|
||||
- Public and private transfers (shielded, deshielded, private-owned)
|
||||
- First-time onboarding (config path, storage path, password)
|
||||
- Account key management
|
||||
|
||||
## Supported platforms
|
||||
## Supported Platforms
|
||||
|
||||
- **Linux**: x86_64, aarch64
|
||||
- **macOS**: aarch64 (Apple Silicon)
|
||||
- **Windows**: x86_64 (Nix build depends on logos-liblogos and other inputs providing Windows packages)
|
||||
|
||||
## How to Build
|
||||
## How to Run
|
||||
|
||||
### Using Nix (Recommended)
|
||||
|
||||
```bash
|
||||
# Build plugin (default)
|
||||
nix build
|
||||
|
||||
# Build standalone app
|
||||
nix build '.#app'
|
||||
|
||||
# Development shell
|
||||
nix develop
|
||||
```
|
||||
|
||||
### Running the Standalone App
|
||||
|
||||
Build and run in one step:
|
||||
### Standalone (recommended for development)
|
||||
|
||||
```bash
|
||||
# Run directly
|
||||
nix run
|
||||
|
||||
# With local workspace overrides
|
||||
nix run --override-input lez_wallet_module path:../logos-execution-zone-module \
|
||||
--override-input logos-module-builder path:../logos-module-builder
|
||||
```
|
||||
|
||||
Or build first, then run:
|
||||
The standalone app starts Logos Core, loads `capability_module` and the `lez_wallet_module` core plugin (flake input name must match `metadata.json` `dependencies`), then launches the QML UI via an isolated `ui-host` process.
|
||||
|
||||
### In Basecamp
|
||||
|
||||
```bash
|
||||
nix build '.#app'
|
||||
./result/bin/logos-execution-zone-wallet-ui-app
|
||||
# Build LGX
|
||||
nix build .#lgx
|
||||
|
||||
# Install into Basecamp's plugin directory
|
||||
lgpm --ui-plugins-dir ~/Library/Application\ Support/Logos/LogosBasecampDev/plugins \
|
||||
install --file result/*.lgx
|
||||
```
|
||||
|
||||
## Nix Organization
|
||||
Or from the workspace:
|
||||
|
||||
- `nix/default.nix` — Common configuration (dependencies, flags)
|
||||
- `nix/lib.nix` — UI plugin compilation
|
||||
- `nix/app.nix` — Standalone Qt application compilation
|
||||
```bash
|
||||
ws bundle logos-execution-zone-wallet-ui --auto-local
|
||||
```
|
||||
|
||||
### Build Targets
|
||||
|
||||
```bash
|
||||
nix build # default — combined plugin + QML output
|
||||
nix build .#lgx # .lgx package for distribution
|
||||
nix build .#install # lgpm-installed output (modules/ + plugins/)
|
||||
nix run # standalone app with wallet module
|
||||
nix develop # enter development shell
|
||||
```
|
||||
|
||||
## Module Structure
|
||||
|
||||
```
|
||||
logos-execution-zone-wallet-ui/
|
||||
├── flake.nix # mkLogosQmlModule
|
||||
├── metadata.json # Module config (ui_qml type)
|
||||
├── CMakeLists.txt # logos_module() macro
|
||||
└── src/
|
||||
├── LEZWalletBackend.rep # RemoteObject interface
|
||||
├── LEZWalletBackend.h/cpp # Business logic (extends LEZWalletBackendSimpleSource)
|
||||
├── LEZWalletPlugin.h/cpp # Thin plugin entry point
|
||||
├── LEZWalletPluginInterface.h # Plugin interface marker
|
||||
├── LEZWalletAccountModel.h/cpp # QAbstractListModel for accounts
|
||||
├── LEZAccountFilterModel.h/cpp # Proxy model for account filtering
|
||||
└── qml/
|
||||
└── ExecutionZoneWalletView.qml # QML frontend (+ sub-views)
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Config path and storage path are persisted via QSettings (`Logos`, `ExecutionZoneWalletUI`). On first run, if opening the wallet fails, the onboarding screen is shown to create a new wallet.
|
||||
|
||||
### QML hot reload
|
||||
### Resetting saved paths and onboarding
|
||||
|
||||
During development, set `DEV_QML_PATH` to your `src/qml` directory to load QML from disk and see changes without recompiling:
|
||||
Saved config path, storage path, and related UI state come from **QSettings** (organization `Logos`, application `ExecutionZoneWalletUI`), in addition to whatever files live on disk under your chosen storage path.
|
||||
|
||||
To fully reset onboarding and drop old paths:
|
||||
|
||||
1. **Quit** the app (and any `ui-host` process if you use standalone).
|
||||
2. **Remove wallet data** on disk if you no longer need it (your storage directory).
|
||||
3. **Clear persisted settings** for this app so QSettings does not immediately repopulate the old paths. Where that store lives is **platform-specific** (Qt native format per OS).
|
||||
|
||||
**macOS** — preferences are under the domain `com.logos.ExecutionZoneWalletUI`. After quitting the app:
|
||||
|
||||
```bash
|
||||
defaults delete com.logos.ExecutionZoneWalletUI 2>/dev/null
|
||||
rm -f ~/Library/Preferences/com.logos.ExecutionZoneWalletUI.plist
|
||||
killall cfprefsd
|
||||
```
|
||||
|
||||
Restarting `cfprefsd` (it comes back automatically) avoids stale in-memory preference cache.
|
||||
|
||||
On **Linux** and **Windows**, use your platform’s usual way to clear app settings (e.g. delete the Qt settings file under `~/.config` / registry / `%AppData%` for `Logos` / `ExecutionZoneWalletUI`, or an equivalent tool), following [QSettings](https://doc.qt.io/qt-6/qsettings.html#locations) locations for the native format on that OS.
|
||||
|
||||
### QML Hot Reload
|
||||
|
||||
During development, set `DEV_QML_PATH` to load QML from disk without recompiling:
|
||||
|
||||
```bash
|
||||
export DEV_QML_PATH=/path/to/logos-execution-zone-wallet-ui/src/qml
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Dependency | Purpose |
|
||||
|---|---|
|
||||
| Qt6 Core, RemoteObjects, Declarative | UI framework + IPC |
|
||||
| [`logos-module-builder`](https://github.com/logos-co/logos-module-builder) | Build system (mkLogosQmlModule) |
|
||||
| [`logos-execution-zone-module`](https://github.com/logos-blockchain/logos-execution-zone-module) | Wallet backend module |
|
||||
|
||||
## Related Repositories
|
||||
|
||||
| Repository | Role |
|
||||
|---|---|
|
||||
| [`logos-execution-zone-module`](https://github.com/logos-blockchain/logos-execution-zone-module) | Wallet backend — this UI's required dependency |
|
||||
| [`logos-module-builder`](https://github.com/logos-co/logos-module-builder) | Module build system |
|
||||
| [`logos-liblogos`](https://github.com/logos-co/logos-liblogos) | Logos Core platform |
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(LogosExecutionZoneWalletUIApp LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
# Find Qt packages
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Widgets)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets)
|
||||
|
||||
# Find logos-liblogos
|
||||
if(NOT DEFINED LOGOS_LIBLOGOS_ROOT)
|
||||
message(FATAL_ERROR "LOGOS_LIBLOGOS_ROOT must be defined")
|
||||
endif()
|
||||
|
||||
message(STATUS "Using logos-liblogos at: ${LOGOS_LIBLOGOS_ROOT}")
|
||||
|
||||
include_directories(
|
||||
${LOGOS_LIBLOGOS_ROOT}/include
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${LOGOS_LIBLOGOS_ROOT}/lib
|
||||
)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
add_executable(logos-execution-zone-wallet-ui-app
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.h
|
||||
)
|
||||
|
||||
target_link_libraries(logos-execution-zone-wallet-ui-app PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
logos_core
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(logos-execution-zone-wallet-ui-app PROPERTIES
|
||||
INSTALL_RPATH "@executable_path/../lib"
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
)
|
||||
elseif(UNIX)
|
||||
set_target_properties(logos-execution-zone-wallet-ui-app PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN/../lib"
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS logos-execution-zone-wallet-ui-app
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
53
app/main.cpp
53
app/main.cpp
@ -1,53 +0,0 @@
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
extern "C" {
|
||||
void logos_core_set_plugins_dir(const char* plugins_dir);
|
||||
void logos_core_start();
|
||||
void logos_core_cleanup();
|
||||
char** logos_core_get_loaded_plugins();
|
||||
int logos_core_load_plugin(const char* plugin_name);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QString pluginsDir = QDir::cleanPath(QCoreApplication::applicationDirPath() + "/../modules");
|
||||
logos_core_set_plugins_dir(pluginsDir.toUtf8().constData());
|
||||
|
||||
logos_core_start();
|
||||
|
||||
if (!logos_core_load_plugin("capability_module")) {
|
||||
qWarning() << "Failed to load capability_module plugin";
|
||||
}
|
||||
|
||||
if (!logos_core_load_plugin("liblogos_execution_zone_wallet_module")) {
|
||||
qWarning() << "Failed to load execution zone wallet module plugin";
|
||||
}
|
||||
|
||||
char** loadedPlugins = logos_core_get_loaded_plugins();
|
||||
int count = 0;
|
||||
if (loadedPlugins) {
|
||||
qInfo() << "Currently loaded plugins:";
|
||||
for (char** p = loadedPlugins; *p != nullptr; ++p) {
|
||||
qInfo() << " -" << *p;
|
||||
++count;
|
||||
}
|
||||
qInfo() << "Total plugins:" << count;
|
||||
} else {
|
||||
qInfo() << "No plugins loaded.";
|
||||
}
|
||||
|
||||
MainWindow window;
|
||||
window.show();
|
||||
|
||||
int result = app.exec();
|
||||
|
||||
logos_core_cleanup();
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
#include "mainwindow.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
{
|
||||
setupUi();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void MainWindow::setupUi()
|
||||
{
|
||||
QString pluginExtension;
|
||||
#if defined(Q_OS_WIN)
|
||||
pluginExtension = ".dll";
|
||||
#elif defined(Q_OS_MAC)
|
||||
pluginExtension = ".dylib";
|
||||
#else
|
||||
pluginExtension = ".so";
|
||||
#endif
|
||||
|
||||
QString pluginPath = QCoreApplication::applicationDirPath() + "/../logos_execution_zone_wallet_ui" + pluginExtension;
|
||||
QPluginLoader loader(pluginPath);
|
||||
|
||||
QWidget* walletWidget = nullptr;
|
||||
|
||||
if (loader.load()) {
|
||||
QObject* plugin = loader.instance();
|
||||
if (plugin) {
|
||||
QMetaObject::invokeMethod(plugin, "createWidget",
|
||||
Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QWidget*, walletWidget));
|
||||
}
|
||||
}
|
||||
|
||||
if (walletWidget) {
|
||||
setCentralWidget(walletWidget);
|
||||
} else {
|
||||
qWarning() << "================================================";
|
||||
qWarning() << "Failed to load execution zone wallet UI plugin from:" << pluginPath;
|
||||
qWarning() << "Error:" << loader.errorString();
|
||||
qWarning() << "================================================";
|
||||
|
||||
QWidget* fallbackWidget = new QWidget(this);
|
||||
QVBoxLayout* layout = new QVBoxLayout(fallbackWidget);
|
||||
|
||||
QLabel* messageLabel = new QLabel("Execution Zone Wallet UI module not loaded", fallbackWidget);
|
||||
QFont font = messageLabel->font();
|
||||
font.setPointSize(14);
|
||||
messageLabel->setFont(font);
|
||||
messageLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
layout->addWidget(messageLabel);
|
||||
setCentralWidget(fallbackWidget);
|
||||
}
|
||||
|
||||
setWindowTitle("Logos Execution Zone Wallet UI App");
|
||||
resize(800, 600);
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
5417
flake.lock
generated
5417
flake.lock
generated
File diff suppressed because it is too large
Load Diff
90
flake.nix
90
flake.nix
@ -1,90 +1,16 @@
|
||||
{
|
||||
description = "Logos Execution Zone Wallet UI - A Qt UI plugin for Logos Execution Zone Wallet Module";
|
||||
description = "Logos Execution Zone Wallet UI - QML view + C++ backend module";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.follows = "logos-liblogos/nixpkgs";
|
||||
logos-cpp-sdk.url = "github:logos-co/logos-cpp-sdk";
|
||||
logos-liblogos.url = "github:logos-co/logos-liblogos";
|
||||
logos-execution-zone-module.url = "github:logos-blockchain/logos-execution-zone-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";
|
||||
logos-module-builder.url = "github:logos-co/logos-module-builder";
|
||||
nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx";
|
||||
logos-package-manager.url = "github:logos-co/logos-package-manager-module";
|
||||
lez_wallet_module.url = "github:logos-blockchain/logos-execution-zone-module";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, logos-cpp-sdk, logos-liblogos, logos-execution-zone-module, logos-capability-module, logos-design-system, nix-bundle-lgx, logos-package-manager }:
|
||||
let
|
||||
systems = [ "aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux" "x86_64-windows" ];
|
||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f {
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
logosSdk = logos-cpp-sdk.packages.${system}.default;
|
||||
logosLiblogos = logos-liblogos.packages.${system}.default;
|
||||
logosExecutionZoneModule = logos-execution-zone-module.packages.${system}.default;
|
||||
logosCapabilityModule = logos-capability-module.packages.${system}.default;
|
||||
logosDesignSystem = logos-design-system.packages.${system}.default;
|
||||
lgxBundler = nix-bundle-lgx.bundlers.${system}.default;
|
||||
lgpm = logos-package-manager.packages.${system}.cli;
|
||||
});
|
||||
in
|
||||
{
|
||||
packages = forAllSystems ({ pkgs, logosSdk, logosLiblogos, logosExecutionZoneModule, logosCapabilityModule, logosDesignSystem, lgxBundler, lgpm }:
|
||||
let
|
||||
common = import ./nix/default.nix {
|
||||
inherit pkgs logosSdk logosLiblogos;
|
||||
};
|
||||
src = ./.;
|
||||
|
||||
lib = import ./nix/lib.nix {
|
||||
inherit pkgs common src logosExecutionZoneModule;
|
||||
};
|
||||
|
||||
logosCapabilityModuleLgx = lgxBundler logosCapabilityModule;
|
||||
logosExecutionZoneModuleLgx = lgxBundler logosExecutionZoneModule;
|
||||
|
||||
app = import ./nix/app.nix {
|
||||
inherit pkgs common src logosLiblogos logosExecutionZoneModule logosCapabilityModule logosDesignSystem lgpm logosCapabilityModuleLgx logosExecutionZoneModuleLgx;
|
||||
logosExecutionZoneWalletUI = lib;
|
||||
};
|
||||
in
|
||||
{
|
||||
logos-execution-zone-wallet-ui-lib = lib;
|
||||
app = app;
|
||||
lib = lib;
|
||||
|
||||
default = lib;
|
||||
}
|
||||
);
|
||||
|
||||
apps = nixpkgs.lib.genAttrs systems (system: {
|
||||
default = {
|
||||
type = "app";
|
||||
program = "${self.packages.${system}.app}/bin/logos-execution-zone-wallet-ui-app";
|
||||
};
|
||||
});
|
||||
|
||||
devShells = forAllSystems ({ pkgs, logosSdk, logosLiblogos, logosExecutionZoneModule, logosCapabilityModule, logosDesignSystem, lgpm, ... }: {
|
||||
default = pkgs.mkShell {
|
||||
nativeBuildInputs = [
|
||||
pkgs.cmake
|
||||
pkgs.ninja
|
||||
pkgs.pkg-config
|
||||
];
|
||||
buildInputs = [
|
||||
pkgs.qt6.qtbase
|
||||
pkgs.qt6.qtremoteobjects
|
||||
pkgs.zstd
|
||||
pkgs.krb5
|
||||
pkgs.abseil-cpp
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
export LOGOS_LIBLOGOS_ROOT="${logosLiblogos}"
|
||||
export LOGOS_DESIGN_SYSTEM_ROOT="${logosDesignSystem}"
|
||||
echo "Logos Execution Zone Wallet UI development environment"
|
||||
echo "LOGOS_LIBLOGOS_ROOT: $LOGOS_LIBLOGOS_ROOT"
|
||||
'';
|
||||
};
|
||||
});
|
||||
outputs = inputs@{ logos-module-builder, ... }:
|
||||
logos-module-builder.lib.mkLogosQmlModule {
|
||||
src = ./.;
|
||||
configFile = ./metadata.json;
|
||||
flakeInputs = inputs;
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QtPlugin>
|
||||
|
||||
class LogosAPI;
|
||||
|
||||
class IComponent {
|
||||
public:
|
||||
virtual ~IComponent() = default;
|
||||
virtual QWidget* createWidget(LogosAPI* logosAPI = nullptr) = 0;
|
||||
virtual void destroyWidget(QWidget* widget) = 0;
|
||||
};
|
||||
|
||||
#define IComponent_iid "com.logos.component.IComponent"
|
||||
Q_DECLARE_INTERFACE(IComponent, IComponent_iid)
|
||||
@ -1,22 +1,26 @@
|
||||
{
|
||||
"name": "logos_execution_zone_wallet_ui",
|
||||
"name": "lez_wallet_ui",
|
||||
"version": "1.0.0",
|
||||
"type": "ui_qml",
|
||||
"category": "wallet",
|
||||
"description": "Execution Zone Wallet UI module for the Logos application",
|
||||
"author": "Logos Blockchain Team",
|
||||
"type": "ui",
|
||||
"main": "logos_execution_zone_wallet_ui",
|
||||
"icon": "",
|
||||
"dependencies": ["liblogos_execution_zone_wallet_module"],
|
||||
"category": "wallet",
|
||||
"build": {
|
||||
"type": "cmake",
|
||||
"files": [
|
||||
"src/LEZWalletPlugin.cpp",
|
||||
"src/LEZWalletPlugin.h",
|
||||
"src/LEZWalletBackend.cpp",
|
||||
"src/LEZWalletBackend.h",
|
||||
"src/wallet_resources.qrc"
|
||||
]
|
||||
},
|
||||
"capabilities": ["ui_components", "wallet"]
|
||||
"main": "lez_wallet_ui_plugin",
|
||||
"icon": "src/qml/icons/copy.svg",
|
||||
"view": "qml/ExecutionZoneWalletView.qml",
|
||||
"dependencies": ["lez_wallet_module"],
|
||||
|
||||
"nix": {
|
||||
"packages": {
|
||||
"build": [],
|
||||
"runtime": ["qt6.qtdeclarative", "zstd", "krb5", "abseil-cpp"]
|
||||
},
|
||||
"external_libraries": [],
|
||||
"cmake": {
|
||||
"find_packages": [],
|
||||
"extra_sources": [],
|
||||
"extra_include_dirs": [],
|
||||
"extra_link_libraries": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
189
nix/app.nix
189
nix/app.nix
@ -1,189 +0,0 @@
|
||||
# Builds the logos-execution-zone-wallet-ui-app standalone application
|
||||
{ pkgs, common, src, logosLiblogos, logosExecutionZoneModule, logosCapabilityModule, logosExecutionZoneWalletUI, logosDesignSystem, lgpm, logosCapabilityModuleLgx, logosExecutionZoneModuleLgx }:
|
||||
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
pname = "logos-execution-zone-wallet-ui-app";
|
||||
version = common.version;
|
||||
|
||||
inherit src;
|
||||
inherit (common) buildInputs meta;
|
||||
|
||||
nativeBuildInputs = common.nativeBuildInputs ++ [ pkgs.patchelf pkgs.removeReferencesTo ];
|
||||
|
||||
qtLibPath = pkgs.lib.makeLibraryPath (
|
||||
[
|
||||
pkgs.qt6.qtbase
|
||||
pkgs.qt6.qtremoteobjects
|
||||
pkgs.zstd
|
||||
pkgs.krb5
|
||||
pkgs.zlib
|
||||
pkgs.glib
|
||||
pkgs.stdenv.cc.cc
|
||||
pkgs.freetype
|
||||
pkgs.fontconfig
|
||||
]
|
||||
++ pkgs.lib.optionals pkgs.stdenv.isLinux [
|
||||
pkgs.libglvnd
|
||||
pkgs.mesa.drivers
|
||||
pkgs.xorg.libX11
|
||||
pkgs.xorg.libXext
|
||||
pkgs.xorg.libXrender
|
||||
pkgs.xorg.libXrandr
|
||||
pkgs.xorg.libXcursor
|
||||
pkgs.xorg.libXi
|
||||
pkgs.xorg.libXfixes
|
||||
pkgs.xorg.libxcb
|
||||
]
|
||||
);
|
||||
qtPluginPath = "${pkgs.qt6.qtbase}/lib/qt-6/plugins";
|
||||
qmlImportPath = "${placeholder "out"}/lib:${pkgs.qt6.qtbase}/lib/qt-6/qml";
|
||||
|
||||
dontWrapQtApps = false;
|
||||
dontStrip = true;
|
||||
|
||||
qtWrapperArgs = [
|
||||
"--prefix" "LD_LIBRARY_PATH" ":" qtLibPath
|
||||
"--prefix" "QT_PLUGIN_PATH" ":" qtPluginPath
|
||||
"--prefix" "QML2_IMPORT_PATH" ":" qmlImportPath
|
||||
];
|
||||
|
||||
preConfigure = ''
|
||||
runHook prePreConfigure
|
||||
export MACOSX_DEPLOYMENT_TARGET=12.0
|
||||
runHook postPreConfigure
|
||||
'';
|
||||
|
||||
preFixup = ''
|
||||
runHook prePreFixup
|
||||
|
||||
export QT_PLUGIN_PATH="${pkgs.qt6.qtbase}/lib/qt-6/plugins"
|
||||
export QML_IMPORT_PATH="${pkgs.qt6.qtbase}/lib/qt-6/qml"
|
||||
|
||||
find $out -type f -executable -exec sh -c '
|
||||
if file "$1" | grep -q "ELF.*executable"; then
|
||||
if patchelf --print-rpath "$1" 2>/dev/null | grep -q "/build/"; then
|
||||
echo "Cleaning RPATH for $1"
|
||||
patchelf --remove-rpath "$1" 2>/dev/null || true
|
||||
fi
|
||||
if echo "$1" | grep -q "/logos-execution-zone-wallet-ui-app$"; then
|
||||
echo "Setting RPATH for $1"
|
||||
patchelf --set-rpath "$out/lib" "$1" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
' _ {} \;
|
||||
|
||||
find $out -name "*.so" -exec sh -c '
|
||||
if patchelf --print-rpath "$1" 2>/dev/null | grep -q "/build/"; then
|
||||
echo "Cleaning RPATH for $1"
|
||||
patchelf --remove-rpath "$1" 2>/dev/null || true
|
||||
fi
|
||||
' _ {} \;
|
||||
|
||||
runHook prePostFixup
|
||||
'';
|
||||
|
||||
configurePhase = ''
|
||||
runHook preConfigure
|
||||
|
||||
echo "Configuring logos-execution-zone-wallet-ui-app..."
|
||||
|
||||
test -d "${logosLiblogos}" || (echo "liblogos not found" && exit 1)
|
||||
test -d "${logosExecutionZoneModule}" || (echo "execution-zone-module not found" && exit 1)
|
||||
test -d "${logosCapabilityModule}" || (echo "capability-module not found" && exit 1)
|
||||
test -d "${logosExecutionZoneWalletUI}" || (echo "execution-zone-wallet-ui not found" && exit 1)
|
||||
test -d "${logosDesignSystem}" || (echo "logos-design-system not found" && exit 1)
|
||||
|
||||
cmake -S app -B build \
|
||||
-GNinja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 \
|
||||
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE \
|
||||
-DCMAKE_INSTALL_RPATH="" \
|
||||
-DCMAKE_SKIP_BUILD_RPATH=TRUE \
|
||||
-DLOGOS_LIBLOGOS_ROOT=${logosLiblogos}
|
||||
|
||||
runHook postConfigure
|
||||
'';
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
cmake --build build
|
||||
echo "logos-execution-zone-wallet-ui-app built successfully!"
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/bin $out/lib $out/modules
|
||||
|
||||
if [ -f "build/bin/logos-execution-zone-wallet-ui-app" ]; then
|
||||
cp build/bin/logos-execution-zone-wallet-ui-app "$out/bin/"
|
||||
echo "Installed logos-execution-zone-wallet-ui-app binary"
|
||||
fi
|
||||
|
||||
if [ -f "${logosLiblogos}/bin/logoscore" ]; then
|
||||
cp -L "${logosLiblogos}/bin/logoscore" "$out/bin/"
|
||||
echo "Installed logoscore binary"
|
||||
fi
|
||||
if [ -f "${logosLiblogos}/bin/logos_host" ]; then
|
||||
cp -L "${logosLiblogos}/bin/logos_host" "$out/bin/"
|
||||
echo "Installed logos_host binary"
|
||||
fi
|
||||
|
||||
if ls "${logosLiblogos}/lib/"liblogos_core.* >/dev/null 2>&1; then
|
||||
cp -L "${logosLiblogos}/lib/"liblogos_core.* "$out/lib/" || true
|
||||
fi
|
||||
|
||||
OS_EXT="so"
|
||||
case "$(uname -s)" in
|
||||
Darwin) OS_EXT="dylib";;
|
||||
Linux) OS_EXT="so";;
|
||||
MINGW*|MSYS*|CYGWIN*) OS_EXT="dll";;
|
||||
esac
|
||||
|
||||
for lgxFile in ${logosCapabilityModuleLgx}/*.lgx; do
|
||||
echo "Installing $lgxFile via lgpm..."
|
||||
${lgpm}/bin/lgpm --modules-dir "$out/modules" install --file "$lgxFile"
|
||||
done
|
||||
for lgxFile in ${logosExecutionZoneModuleLgx}/*.lgx; do
|
||||
echo "Installing $lgxFile via lgpm..."
|
||||
${lgpm}/bin/lgpm --modules-dir "$out/modules" install --file "$lgxFile"
|
||||
done
|
||||
|
||||
if [ -f "${logosExecutionZoneWalletUI}/lib/logos_execution_zone_wallet_ui.$OS_EXT" ]; then
|
||||
cp -L "${logosExecutionZoneWalletUI}/lib/logos_execution_zone_wallet_ui.$OS_EXT" "$out/"
|
||||
fi
|
||||
|
||||
if [ -d "${logosDesignSystem}/lib/Logos/Theme" ]; then
|
||||
mkdir -p "$out/lib/Logos"
|
||||
cp -R "${logosDesignSystem}/lib/Logos/Theme" "$out/lib/Logos/"
|
||||
echo "Copied Logos.Theme to lib/Logos/Theme/"
|
||||
fi
|
||||
if [ -d "${logosDesignSystem}/lib/Logos/Controls" ]; then
|
||||
mkdir -p "$out/lib/Logos"
|
||||
cp -R "${logosDesignSystem}/lib/Logos/Controls" "$out/lib/Logos/"
|
||||
echo "Copied Logos.Controls to lib/Logos/Controls/"
|
||||
fi
|
||||
|
||||
cat > $out/README.txt <<EOF
|
||||
Logos Execution Zone Wallet UI App
|
||||
==================================
|
||||
liblogos: ${logosLiblogos}
|
||||
execution-zone-module: ${logosExecutionZoneModule}
|
||||
capability-module: ${logosCapabilityModule}
|
||||
execution-zone-wallet-ui: ${logosExecutionZoneWalletUI}
|
||||
design-system: ${logosDesignSystem}
|
||||
|
||||
Layout:
|
||||
bin/logos-execution-zone-wallet-ui-app
|
||||
lib/
|
||||
modules/
|
||||
logos_execution_zone_wallet_ui.$OS_EXT
|
||||
EOF
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
# Common build configuration shared across all packages
|
||||
{ pkgs, logosSdk, logosLiblogos }:
|
||||
|
||||
{
|
||||
pname = "logos-execution-zone-wallet-ui";
|
||||
version = "1.0.0";
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.cmake
|
||||
pkgs.ninja
|
||||
pkgs.pkg-config
|
||||
pkgs.qt6.wrapQtAppsHook
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
pkgs.qt6.qtbase
|
||||
pkgs.qt6.qtremoteobjects
|
||||
pkgs.zstd
|
||||
pkgs.krb5
|
||||
pkgs.abseil-cpp
|
||||
];
|
||||
|
||||
cmakeFlags = [
|
||||
"-GNinja"
|
||||
"-DLOGOS_CPP_SDK_ROOT=${logosSdk}"
|
||||
"-DLOGOS_LIBLOGOS_ROOT=${logosLiblogos}"
|
||||
];
|
||||
|
||||
env = {
|
||||
LOGOS_LIBLOGOS_ROOT = "${logosLiblogos}";
|
||||
};
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
description = "Logos Execution Zone Wallet UI - A Qt UI plugin for Logos Execution Zone Wallet Module";
|
||||
platforms = platforms.unix ++ platforms.windows;
|
||||
};
|
||||
}
|
||||
47
nix/lib.nix
47
nix/lib.nix
@ -1,47 +0,0 @@
|
||||
# Builds the logos-execution-zone-wallet-ui library
|
||||
{ pkgs, common, src, logosExecutionZoneModule }:
|
||||
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "${common.pname}-lib";
|
||||
version = common.version;
|
||||
|
||||
inherit src;
|
||||
inherit (common) buildInputs cmakeFlags meta env;
|
||||
nativeBuildInputs = common.nativeBuildInputs;
|
||||
|
||||
dontWrapQtApps = true;
|
||||
|
||||
configurePhase = ''
|
||||
runHook preConfigure
|
||||
cmake -S . -B build \
|
||||
-GNinja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
''${cmakeFlags}
|
||||
runHook postConfigure
|
||||
'';
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
cmake --build build
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/lib
|
||||
if [ -f build/modules/logos_execution_zone_wallet_ui.dylib ]; then
|
||||
cp build/modules/logos_execution_zone_wallet_ui.dylib $out/lib/
|
||||
elif [ -f build/modules/logos_execution_zone_wallet_ui.so ]; then
|
||||
cp build/modules/logos_execution_zone_wallet_ui.so $out/lib/
|
||||
elif [ -f build/modules/logos_execution_zone_wallet_ui.dll ]; then
|
||||
cp build/modules/logos_execution_zone_wallet_ui.dll $out/lib/
|
||||
else
|
||||
echo "Error: No library file found in build/modules/"
|
||||
ls -la build/modules/ 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
}
|
||||
@ -1,21 +1,27 @@
|
||||
#include "LEZWalletBackend.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QClipboard>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
#include <QJsonArray>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
#include "logos_api.h"
|
||||
#include "logos_sdk.h"
|
||||
|
||||
namespace {
|
||||
const char SETTINGS_ORG[] = "Logos";
|
||||
const char SETTINGS_APP[] = "ExecutionZoneWalletUI";
|
||||
const char CONFIG_PATH_KEY[] = "configPath";
|
||||
const char STORAGE_PATH_KEY[] = "storagePath";
|
||||
const QString WALLET_MODULE_NAME = QStringLiteral("liblogos_execution_zone_wallet_module");
|
||||
const int WALLET_FFI_SUCCESS = 0;
|
||||
|
||||
// Convert decimal amount string to 32-char hex (16 bytes little-endian) for transfer_public/transfer_private.
|
||||
// Convert a decimal amount string to 32-char hex (16 bytes little-endian)
|
||||
// for transfer_public/transfer_private/transfer_private_owned.
|
||||
QString amountToLe16Hex(const QString& amountStr) {
|
||||
const QString trimmed = amountStr.trimmed();
|
||||
if (trimmed.isEmpty()) return QString();
|
||||
@ -27,119 +33,102 @@ namespace {
|
||||
bytes[i] = static_cast<uint8_t>((value >> (i * 8)) & 0xff);
|
||||
return QByteArray(reinterpret_cast<const char*>(bytes), 16).toHex();
|
||||
}
|
||||
|
||||
// Normalise file:// URLs and OS paths to a plain local path.
|
||||
QString toLocalPath(const QString& path) {
|
||||
if (path.startsWith("file://") || path.contains("/"))
|
||||
return QUrl::fromUserInput(path).toLocalFile();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
LEZWalletBackend::LEZWalletBackend(LogosAPI* logosAPI, QObject* parent)
|
||||
: QObject(parent),
|
||||
m_isWalletOpen(false),
|
||||
m_lastSyncedBlock(0),
|
||||
m_currentBlockHeight(0),
|
||||
: LEZWalletBackendSimpleSource(parent),
|
||||
m_accountModel(new LEZWalletAccountModel(this)),
|
||||
m_filteredAccountModel(new LEZAccountFilterModel(this)),
|
||||
m_logosAPI(nullptr),
|
||||
m_walletClient(nullptr)
|
||||
m_logosAPI(logosAPI ? logosAPI : new LogosAPI("lez_wallet_ui", this)),
|
||||
m_logos(new LogosModules(m_logosAPI))
|
||||
{
|
||||
m_filteredAccountModel->setSourceModel(m_accountModel);
|
||||
|
||||
// Initialise PROP defaults via the generated setters.
|
||||
setIsWalletOpen(false);
|
||||
setLastSyncedBlock(0);
|
||||
setCurrentBlockHeight(0);
|
||||
|
||||
// Load persisted config/storage paths.
|
||||
QSettings s(SETTINGS_ORG, SETTINGS_APP);
|
||||
m_configPath = s.value(CONFIG_PATH_KEY).toString();
|
||||
m_storagePath = s.value(STORAGE_PATH_KEY).toString();
|
||||
setConfigPath(s.value(CONFIG_PATH_KEY).toString());
|
||||
setStoragePath(s.value(STORAGE_PATH_KEY).toString());
|
||||
|
||||
if (!logosAPI) {
|
||||
logosAPI = new LogosAPI("logos_execution_zone_wallet_ui", this);
|
||||
}
|
||||
m_logosAPI = logosAPI;
|
||||
m_walletClient = m_logosAPI->getClient(WALLET_MODULE_NAME);
|
||||
// ui-host runs our constructor inside initLogos(), synchronously, BEFORE
|
||||
// it enables remoting and emits READY. Any blocking RPC here (open,
|
||||
// list_accounts, block-height queries, sequencer lookup) would stall
|
||||
// ui-host startup past ViewModuleHost's 30s ready watchdog and get the
|
||||
// child SIGTERM'd. Defer the whole open+refresh chain to the first
|
||||
// event-loop tick so ui-host finishes wiring itself up first.
|
||||
QTimer::singleShot(0, this, [this]() { openIfPathsConfigured(); });
|
||||
|
||||
if (!m_walletClient) {
|
||||
qWarning() << "LEZWalletBackend: could not get client for" << WALLET_MODULE_NAME;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_configPath.isEmpty() && !m_storagePath.isEmpty()) {
|
||||
qDebug() << "LEZWalletBackend: opening wallet with config path" << m_configPath << "and storage path" << m_storagePath;
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "open", m_configPath, m_storagePath);
|
||||
int err = result.isValid() ? result.toInt() : -1;
|
||||
if (err == WALLET_FFI_SUCCESS) {
|
||||
qWarning() << "LEZWalletBackend: wallet opened successfully";
|
||||
setWalletOpen(true);
|
||||
refreshAccounts();
|
||||
refreshBlockHeights();
|
||||
refreshSequencerAddr();
|
||||
}
|
||||
}
|
||||
|
||||
// Save wallet when app quits; host may not call destroyWidget() so destructor might not run.
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, this, [this]() { saveWallet(); }, Qt::DirectConnection);
|
||||
// Save wallet when app quits; host may not call destructors so this is best-effort.
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, this,
|
||||
[this]() { saveWallet(); }, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
LEZWalletBackend::~LEZWalletBackend()
|
||||
{
|
||||
saveWallet();
|
||||
delete m_logos;
|
||||
}
|
||||
|
||||
void LEZWalletBackend::saveWallet()
|
||||
{
|
||||
if (m_walletClient && m_isWalletOpen) {
|
||||
m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "save");
|
||||
if (isWalletOpen()) {
|
||||
m_logos->lez_wallet_module.save();
|
||||
}
|
||||
}
|
||||
|
||||
void LEZWalletBackend::setWalletOpen(bool open)
|
||||
void LEZWalletBackend::persistConfigPath(const QString& path)
|
||||
{
|
||||
if (m_isWalletOpen != open) {
|
||||
m_isWalletOpen = open;
|
||||
emit isWalletOpenChanged();
|
||||
}
|
||||
const QString localPath = toLocalPath(path);
|
||||
setConfigPath(localPath);
|
||||
QSettings(SETTINGS_ORG, SETTINGS_APP).setValue(CONFIG_PATH_KEY, localPath);
|
||||
}
|
||||
|
||||
void LEZWalletBackend::setConfigPath(const QString& path)
|
||||
void LEZWalletBackend::persistStoragePath(const QString& path)
|
||||
{
|
||||
QString localPath = path;
|
||||
if (path.startsWith("file://") || path.contains("/")) {
|
||||
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();
|
||||
}
|
||||
const QString localPath = toLocalPath(path);
|
||||
setStoragePath(localPath);
|
||||
QSettings(SETTINGS_ORG, SETTINGS_APP).setValue(STORAGE_PATH_KEY, localPath);
|
||||
}
|
||||
|
||||
void LEZWalletBackend::setStoragePath(const QString& path)
|
||||
void LEZWalletBackend::openIfPathsConfigured()
|
||||
{
|
||||
QString localPath = path;
|
||||
if (path.startsWith("file://") || path.contains("/")) {
|
||||
localPath = QUrl::fromUserInput(path).toLocalFile();
|
||||
}
|
||||
if (m_storagePath != localPath) {
|
||||
m_storagePath = localPath;
|
||||
QSettings s(SETTINGS_ORG, SETTINGS_APP);
|
||||
s.setValue(STORAGE_PATH_KEY, m_storagePath);
|
||||
emit storagePathChanged();
|
||||
if (configPath().isEmpty() || storagePath().isEmpty()) return;
|
||||
|
||||
qDebug() << "LEZWalletBackend: opening wallet with config" << configPath()
|
||||
<< "storage" << storagePath();
|
||||
int err = m_logos->lez_wallet_module.open(configPath(), storagePath());
|
||||
if (err == WALLET_FFI_SUCCESS) {
|
||||
qDebug() << "LEZWalletBackend: wallet opened successfully";
|
||||
setIsWalletOpen(true);
|
||||
refreshAccounts();
|
||||
refreshBlockHeights();
|
||||
refreshSequencerAddr();
|
||||
}
|
||||
}
|
||||
|
||||
void LEZWalletBackend::refreshAccounts()
|
||||
{
|
||||
if (!m_walletClient) return;
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "list_accounts");
|
||||
QJsonArray arr;
|
||||
if (result.isValid() && result.canConvert<QJsonArray>()) {
|
||||
arr = result.toJsonArray();
|
||||
}
|
||||
QJsonArray arr = m_logos->lez_wallet_module.list_accounts();
|
||||
m_accountModel->replaceFromJsonArray(arr);
|
||||
emit accountModelChanged();
|
||||
refreshBalances();
|
||||
}
|
||||
|
||||
void LEZWalletBackend::refreshBalances()
|
||||
{
|
||||
refreshBlockHeights();
|
||||
syncToBlock(m_currentBlockHeight);
|
||||
if (!m_walletClient || !m_accountModel) return;
|
||||
syncToBlock(currentBlockHeight());
|
||||
if (!m_accountModel) return;
|
||||
for (int i = 0; i < m_accountModel->count(); ++i) {
|
||||
const QModelIndex idx = m_accountModel->index(i, 0);
|
||||
const QString addr = m_accountModel->data(idx, LEZWalletAccountModel::AddressRole).toString();
|
||||
@ -151,178 +140,116 @@ void LEZWalletBackend::refreshBalances()
|
||||
|
||||
void LEZWalletBackend::fetchAndUpdateBlockHeights()
|
||||
{
|
||||
if (!m_walletClient) return;
|
||||
const int lastVal = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "get_last_synced_block").toInt();
|
||||
const int currentVal = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "get_current_block_height").toInt();
|
||||
if (m_lastSyncedBlock != lastVal) {
|
||||
m_lastSyncedBlock = lastVal;
|
||||
emit lastSyncedBlockChanged();
|
||||
}
|
||||
if (m_currentBlockHeight != currentVal) {
|
||||
m_currentBlockHeight = currentVal;
|
||||
emit currentBlockHeightChanged();
|
||||
}
|
||||
const int lastVal = m_logos->lez_wallet_module.get_last_synced_block();
|
||||
const int currentVal = m_logos->lez_wallet_module.get_current_block_height();
|
||||
if (lastSyncedBlock() != lastVal)
|
||||
setLastSyncedBlock(lastVal);
|
||||
if (currentBlockHeight() != currentVal)
|
||||
setCurrentBlockHeight(currentVal);
|
||||
}
|
||||
|
||||
void LEZWalletBackend::refreshBlockHeights()
|
||||
{
|
||||
fetchAndUpdateBlockHeights();
|
||||
if (m_currentBlockHeight > 0 && m_lastSyncedBlock < m_currentBlockHeight && syncToBlock(m_currentBlockHeight))
|
||||
if (currentBlockHeight() > 0
|
||||
&& lastSyncedBlock() < currentBlockHeight()
|
||||
&& syncToBlock(currentBlockHeight()))
|
||||
{
|
||||
fetchAndUpdateBlockHeights();
|
||||
}
|
||||
}
|
||||
|
||||
void LEZWalletBackend::refreshSequencerAddr()
|
||||
{
|
||||
if (!m_walletClient) return;
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "get_sequencer_addr");
|
||||
QString addr = result.isValid() ? result.toString() : QString();
|
||||
if (m_sequencerAddr != addr) {
|
||||
m_sequencerAddr = std::move(addr);
|
||||
emit sequencerAddrChanged();
|
||||
}
|
||||
const QString addr = m_logos->lez_wallet_module.get_sequencer_addr();
|
||||
if (sequencerAddr() != addr)
|
||||
setSequencerAddr(addr);
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::createAccountPublic()
|
||||
{
|
||||
if (!m_walletClient) return QString();
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "create_account_public");
|
||||
if (result.isValid()) {
|
||||
QString result = m_logos->lez_wallet_module.create_account_public();
|
||||
if (!result.isEmpty())
|
||||
refreshAccounts();
|
||||
return result.toString();
|
||||
}
|
||||
return QString();
|
||||
return result;
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::createAccountPrivate()
|
||||
{
|
||||
if (!m_walletClient) return QString();
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(WALLET_MODULE_NAME, "create_account_private");
|
||||
if (result.isValid()) {
|
||||
QString result = m_logos->lez_wallet_module.create_account_private();
|
||||
if (!result.isEmpty())
|
||||
refreshAccounts();
|
||||
return result.toString();
|
||||
}
|
||||
return QString();
|
||||
return result;
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::getBalance(const QString& accountIdHex, bool isPublic)
|
||||
QString LEZWalletBackend::getBalance(QString accountIdHex, bool isPublic)
|
||||
{
|
||||
if (!m_walletClient) return QStringLiteral("Error: Module not initialized.");
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "get_balance", accountIdHex, isPublic);
|
||||
return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed.");
|
||||
return m_logos->lez_wallet_module.get_balance(accountIdHex, isPublic);
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::getPublicAccountKey(const QString& accountIdHex)
|
||||
QString LEZWalletBackend::getPublicAccountKey(QString accountIdHex)
|
||||
{
|
||||
if (!m_walletClient) return QString();
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "get_public_account_key", accountIdHex);
|
||||
return result.isValid() ? result.toString() : QString();
|
||||
return m_logos->lez_wallet_module.get_public_account_key(accountIdHex);
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::getPrivateAccountKeys(const QString& accountIdHex)
|
||||
QString LEZWalletBackend::getPrivateAccountKeys(QString accountIdHex)
|
||||
{
|
||||
if (!m_walletClient) return QString();
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "get_private_account_keys", accountIdHex);
|
||||
return result.isValid() ? result.toString() : QString();
|
||||
return m_logos->lez_wallet_module.get_private_account_keys(accountIdHex);
|
||||
}
|
||||
|
||||
bool LEZWalletBackend::syncToBlock(quint64 blockId)
|
||||
{
|
||||
if (!m_walletClient) return false;
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "sync_to_block", blockId);
|
||||
int err = result.isValid() ? result.toInt() : -1;
|
||||
int err = m_logos->lez_wallet_module.sync_to_block(blockId);
|
||||
return err == WALLET_FFI_SUCCESS;
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::transferPublic(
|
||||
const QString& fromHex,
|
||||
const QString& toHex,
|
||||
const QString& amountLe16Hex)
|
||||
QString LEZWalletBackend::transferPublic(QString fromHex, QString toHex, QString amountStr)
|
||||
{
|
||||
if (!m_walletClient) return QStringLiteral("Error: Module not initialized.");
|
||||
const QString amountHex = amountToLe16Hex(amountLe16Hex);
|
||||
const QString amountHex = amountToLe16Hex(amountStr);
|
||||
if (amountHex.isEmpty()) return QStringLiteral("Error: Invalid amount.");
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "transfer_public", fromHex, toHex, amountHex);
|
||||
return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed.");
|
||||
return m_logos->lez_wallet_module.transfer_public(fromHex, toHex, amountHex);
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::transferPrivate(
|
||||
const QString& fromHex,
|
||||
const QString& toHex,
|
||||
const QString& amountLe16Hex)
|
||||
QString LEZWalletBackend::transferPrivate(QString fromHex, QString toHex, QString amountStr)
|
||||
{
|
||||
if (!m_walletClient) return QStringLiteral("Error: Module not initialized.");
|
||||
const QString amountHex = amountToLe16Hex(amountLe16Hex);
|
||||
const QString amountHex = amountToLe16Hex(amountStr);
|
||||
if (amountHex.isEmpty()) return QStringLiteral("Error: Invalid amount.");
|
||||
|
||||
QString keysPayload = toHex.trimmed();
|
||||
// If "To" is not JSON (e.g. user pasted account id hex), resolve to keys via get_private_account_keys.
|
||||
// If "To" is not JSON (e.g. user pasted account id hex), resolve to keys.
|
||||
if (!keysPayload.startsWith(QLatin1Char('{'))) {
|
||||
qDebug() << "LEZWalletBackend::transferPrivate: keysPayload is not JSON, resolving to keys via get_private_account_keys";
|
||||
qDebug() << "LEZWalletBackend::transferPrivate: resolving keys via get_private_account_keys";
|
||||
const QString resolved = getPrivateAccountKeys(keysPayload);
|
||||
if (!resolved.isEmpty())
|
||||
keysPayload = resolved;
|
||||
}
|
||||
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "transfer_private", fromHex, keysPayload, amountHex, Timeout(6*60*1000)); // 6 minutes timeout
|
||||
return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed.");
|
||||
return m_logos->lez_wallet_module.transfer_private(fromHex, keysPayload, amountHex);
|
||||
}
|
||||
|
||||
QString LEZWalletBackend::transferPrivateOwned(
|
||||
const QString& fromHex,
|
||||
const QString& toHex,
|
||||
const QString& amountLe16Hex)
|
||||
QString LEZWalletBackend::transferPrivateOwned(QString fromHex, QString toHex, QString amountStr)
|
||||
{
|
||||
if (!m_walletClient) return QStringLiteral("Error: Module not initialized.");
|
||||
const QString amountHex = amountToLe16Hex(amountLe16Hex);
|
||||
const QString amountHex = amountToLe16Hex(amountStr);
|
||||
if (amountHex.isEmpty()) return QStringLiteral("Error: Invalid amount.");
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "transfer_private_owned", fromHex, toHex.trimmed(), amountHex);
|
||||
return result.isValid() ? result.toString() : QStringLiteral("Error: Call failed.");
|
||||
return m_logos->lez_wallet_module.transfer_private_owned(fromHex, toHex.trimmed(), amountHex);
|
||||
}
|
||||
|
||||
bool LEZWalletBackend::createNew(
|
||||
const QString& configPath,
|
||||
const QString& storagePath,
|
||||
const QString& password)
|
||||
bool LEZWalletBackend::createNew(QString configPath, QString storagePath, QString password)
|
||||
{
|
||||
const QString localPath = QUrl::fromUserInput(configPath).toLocalFile();
|
||||
if (!m_walletClient) return false;
|
||||
QVariant result = m_walletClient->invokeRemoteMethod(
|
||||
WALLET_MODULE_NAME, "create_new", localPath, storagePath, password);
|
||||
int err = result.isValid() ? result.toInt() : -1;
|
||||
const QString localPath = toLocalPath(configPath);
|
||||
int err = m_logos->lez_wallet_module.create_new(localPath, storagePath, password);
|
||||
if (err != WALLET_FFI_SUCCESS) return false;
|
||||
|
||||
setConfigPath(localPath);
|
||||
setStoragePath(storagePath);
|
||||
setWalletOpen(true);
|
||||
persistConfigPath(localPath);
|
||||
persistStoragePath(storagePath);
|
||||
setIsWalletOpen(true);
|
||||
refreshAccounts();
|
||||
refreshBlockHeights();
|
||||
refreshSequencerAddr();
|
||||
return true;
|
||||
}
|
||||
|
||||
int LEZWalletBackend::indexOfAddressInModel(QObject* model, const QString& address) const
|
||||
{
|
||||
auto* m = qobject_cast<QAbstractItemModel*>(model);
|
||||
if (!m || address.isEmpty())
|
||||
return -1;
|
||||
const int role = m->roleNames().key("address", -1);
|
||||
if (role < 0)
|
||||
return -1;
|
||||
for (int i = 0; i < m->rowCount(); ++i) {
|
||||
if (m->data(m->index(i, 0), role).toString() == address)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void LEZWalletBackend::copyToClipboard(const QString& text)
|
||||
void LEZWalletBackend::copyToClipboard(QString text)
|
||||
{
|
||||
if (QGuiApplication::clipboard())
|
||||
QGuiApplication::clipboard()->setText(text);
|
||||
|
||||
@ -1,95 +1,62 @@
|
||||
#pragma once
|
||||
#ifndef LEZ_WALLET_BACKEND_H
|
||||
#define LEZ_WALLET_BACKEND_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "rep_LEZWalletBackend_source.h"
|
||||
|
||||
#include "LEZAccountFilterModel.h"
|
||||
#include "LEZWalletAccountModel.h"
|
||||
#include "logos_api.h"
|
||||
#include "logos_api_client.h"
|
||||
|
||||
class QAbstractItemModel;
|
||||
class LogosAPI;
|
||||
struct LogosModules;
|
||||
|
||||
class LEZWalletBackend : public QObject {
|
||||
// Source-side implementation of the LEZWalletBackend .rep interface.
|
||||
// Inheriting from LEZWalletBackendSimpleSource gives us the generated PROPs
|
||||
// and SLOTs from LEZWalletBackend.rep — all the simple ones flow over QtRO.
|
||||
class LEZWalletBackend : public LEZWalletBackendSimpleSource {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(LEZWalletAccountModel* accountModel READ accountModel CONSTANT)
|
||||
Q_PROPERTY(LEZAccountFilterModel* filteredAccountModel READ filteredAccountModel CONSTANT)
|
||||
|
||||
public:
|
||||
Q_PROPERTY(bool isWalletOpen READ isWalletOpen NOTIFY isWalletOpenChanged)
|
||||
Q_PROPERTY(QString configPath READ configPath WRITE setConfigPath NOTIFY configPathChanged)
|
||||
Q_PROPERTY(QString storagePath READ storagePath WRITE setStoragePath NOTIFY storagePathChanged)
|
||||
Q_PROPERTY(LEZWalletAccountModel* accountModel READ accountModel NOTIFY accountModelChanged)
|
||||
Q_PROPERTY(LEZAccountFilterModel* filteredAccountModel READ filteredAccountModel NOTIFY filteredAccountModelChanged)
|
||||
Q_PROPERTY(int lastSyncedBlock READ lastSyncedBlock NOTIFY lastSyncedBlockChanged)
|
||||
Q_PROPERTY(int currentBlockHeight READ currentBlockHeight NOTIFY currentBlockHeightChanged)
|
||||
Q_PROPERTY(QString sequencerAddr READ sequencerAddr NOTIFY sequencerAddrChanged)
|
||||
|
||||
explicit LEZWalletBackend(LogosAPI* logosAPI = nullptr, QObject* parent = nullptr);
|
||||
~LEZWalletBackend();
|
||||
~LEZWalletBackend() override;
|
||||
|
||||
bool isWalletOpen() const { return m_isWalletOpen; }
|
||||
QString configPath() const { return m_configPath; }
|
||||
QString storagePath() const { return m_storagePath; }
|
||||
LEZWalletAccountModel* accountModel() const { return m_accountModel; }
|
||||
LEZAccountFilterModel* filteredAccountModel() const { return m_filteredAccountModel; }
|
||||
int lastSyncedBlock() const { return m_lastSyncedBlock; }
|
||||
int currentBlockHeight() const { return m_currentBlockHeight; }
|
||||
QString sequencerAddr() const { return m_sequencerAddr; }
|
||||
|
||||
void setConfigPath(const QString& path);
|
||||
void setStoragePath(const QString& path);
|
||||
|
||||
Q_INVOKABLE QString createAccountPublic();
|
||||
Q_INVOKABLE QString createAccountPrivate();
|
||||
Q_INVOKABLE void refreshAccounts();
|
||||
Q_INVOKABLE QString getBalance(const QString& accountIdHex, bool isPublic);
|
||||
Q_INVOKABLE void refreshBalances();
|
||||
Q_INVOKABLE QString getPublicAccountKey(const QString& accountIdHex);
|
||||
Q_INVOKABLE QString getPrivateAccountKeys(const QString& accountIdHex);
|
||||
Q_INVOKABLE bool syncToBlock(quint64 blockId);
|
||||
Q_INVOKABLE QString transferPublic(
|
||||
const QString& fromHex,
|
||||
const QString& toHex,
|
||||
const QString& amountLe16Hex);
|
||||
Q_INVOKABLE QString transferPrivate(
|
||||
const QString& fromHex,
|
||||
const QString& toHex,
|
||||
const QString& amountLe16Hex);
|
||||
Q_INVOKABLE QString transferPrivateOwned(
|
||||
const QString& fromHex,
|
||||
const QString& toHex,
|
||||
const QString& amountLe16Hex);
|
||||
Q_INVOKABLE bool createNew(
|
||||
const QString& configPath,
|
||||
const QString& storagePath,
|
||||
const QString& password);
|
||||
Q_INVOKABLE int indexOfAddressInModel(QObject* model, const QString& address) const;
|
||||
Q_INVOKABLE void copyToClipboard(const QString& text);
|
||||
|
||||
signals:
|
||||
void isWalletOpenChanged();
|
||||
void configPathChanged();
|
||||
void storagePathChanged();
|
||||
void accountModelChanged();
|
||||
void filteredAccountModelChanged();
|
||||
void lastSyncedBlockChanged();
|
||||
void currentBlockHeightChanged();
|
||||
void sequencerAddrChanged();
|
||||
public slots:
|
||||
// Overrides of the pure-virtual slots generated from the .rep.
|
||||
QString createAccountPublic() override;
|
||||
QString createAccountPrivate() override;
|
||||
void refreshAccounts() override;
|
||||
QString getBalance(QString accountIdHex, bool isPublic) override;
|
||||
void refreshBalances() override;
|
||||
QString getPublicAccountKey(QString accountIdHex) override;
|
||||
QString getPrivateAccountKeys(QString accountIdHex) override;
|
||||
bool syncToBlock(quint64 blockId) override;
|
||||
QString transferPublic(QString fromHex, QString toHex, QString amountStr) override;
|
||||
QString transferPrivate(QString fromHex, QString toHex, QString amountStr) override;
|
||||
QString transferPrivateOwned(QString fromHex, QString toHex, QString amountStr) override;
|
||||
bool createNew(QString configPath, QString storagePath, QString password) override;
|
||||
void copyToClipboard(QString text) override;
|
||||
|
||||
private:
|
||||
void setWalletOpen(bool open);
|
||||
void persistConfigPath(const QString& path);
|
||||
void persistStoragePath(const QString& path);
|
||||
void refreshBlockHeights();
|
||||
void refreshSequencerAddr();
|
||||
void saveWallet();
|
||||
void fetchAndUpdateBlockHeights();
|
||||
void openIfPathsConfigured();
|
||||
|
||||
bool m_isWalletOpen;
|
||||
QString m_configPath;
|
||||
QString m_storagePath;
|
||||
LEZWalletAccountModel* m_accountModel;
|
||||
LEZAccountFilterModel* m_filteredAccountModel;
|
||||
int m_lastSyncedBlock;
|
||||
int m_currentBlockHeight;
|
||||
QString m_sequencerAddr;
|
||||
|
||||
LogosAPI* m_logosAPI;
|
||||
LogosAPIClient* m_walletClient;
|
||||
LogosModules* m_logos;
|
||||
};
|
||||
|
||||
#endif // LEZ_WALLET_BACKEND_H
|
||||
|
||||
26
src/LEZWalletBackend.rep
Normal file
26
src/LEZWalletBackend.rep
Normal file
@ -0,0 +1,26 @@
|
||||
class LEZWalletBackend
|
||||
{
|
||||
PROP(bool isWalletOpen READONLY)
|
||||
PROP(QString configPath READONLY)
|
||||
PROP(QString storagePath READONLY)
|
||||
PROP(int lastSyncedBlock READONLY)
|
||||
PROP(int currentBlockHeight READONLY)
|
||||
PROP(QString sequencerAddr READONLY)
|
||||
|
||||
SLOT(QString createAccountPublic())
|
||||
SLOT(QString createAccountPrivate())
|
||||
SLOT(void refreshAccounts())
|
||||
SLOT(QString getBalance(QString accountIdHex, bool isPublic))
|
||||
SLOT(void refreshBalances())
|
||||
SLOT(QString getPublicAccountKey(QString accountIdHex))
|
||||
SLOT(QString getPrivateAccountKeys(QString accountIdHex))
|
||||
SLOT(bool syncToBlock(quint64 blockId))
|
||||
|
||||
SLOT(QString transferPublic(QString fromHex, QString toHex, QString amountStr))
|
||||
SLOT(QString transferPrivate(QString fromHex, QString toHex, QString amountStr))
|
||||
SLOT(QString transferPrivateOwned(QString fromHex, QString toHex, QString amountStr))
|
||||
|
||||
SLOT(bool createNew(QString configPath, QString storagePath, QString password))
|
||||
|
||||
SLOT(void copyToClipboard(QString text))
|
||||
}
|
||||
@ -1,54 +1,19 @@
|
||||
#include "LEZWalletPlugin.h"
|
||||
#include "LEZWalletBackend.h"
|
||||
#include "LEZAccountFilterModel.h"
|
||||
#include <QQuickWidget>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QUrl>
|
||||
|
||||
QWidget* LEZWalletPlugin::createWidget(LogosAPI* logosAPI) {
|
||||
qDebug() << "LEZWalletPlugin::createWidget called";
|
||||
|
||||
QQuickWidget* quickWidget = new QQuickWidget();
|
||||
quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
|
||||
qmlRegisterType<LEZWalletBackend>("LEZWalletBackend", 1, 0, "LEZWalletBackend");
|
||||
qmlRegisterType<LEZAccountFilterModel>("LEZWalletBackend", 1, 0, "LEZAccountFilterModel");
|
||||
|
||||
LEZWalletBackend* backend = new LEZWalletBackend(logosAPI, quickWidget);
|
||||
quickWidget->rootContext()->setContextProperty("backend", backend);
|
||||
|
||||
QString qmlSource = "qrc:/lezwallet/qml/ExecutionZoneWalletView.qml";
|
||||
QString importPath = "qrc:/lezwallet/qml";
|
||||
|
||||
QString envPath = QString::fromUtf8(qgetenv("DEV_QML_PATH")).trimmed();
|
||||
if (!envPath.isEmpty()) {
|
||||
QFileInfo info(envPath);
|
||||
if (info.isDir()) {
|
||||
QString main = QDir(info.absoluteFilePath()).absoluteFilePath("ExecutionZoneWalletView.qml");
|
||||
if (QFile::exists(main)) {
|
||||
importPath = info.absoluteFilePath();
|
||||
qmlSource = QUrl::fromLocalFile(main).toString();
|
||||
} else {
|
||||
qWarning() << "DEV_QML_PATH: ExecutionZoneWalletView.qml not found in" << info.absoluteFilePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quickWidget->engine()->addImportPath(importPath);
|
||||
quickWidget->setSource(QUrl(qmlSource));
|
||||
|
||||
if (quickWidget->status() == QQuickWidget::Error) {
|
||||
qWarning() << "LEZWalletPlugin: Failed to load QML:" << quickWidget->errors();
|
||||
}
|
||||
|
||||
return quickWidget;
|
||||
LEZWalletPlugin::LEZWalletPlugin(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void LEZWalletPlugin::destroyWidget(QWidget* widget) {
|
||||
delete widget;
|
||||
LEZWalletPlugin::~LEZWalletPlugin() = default;
|
||||
|
||||
void LEZWalletPlugin::initLogos(LogosAPI* api)
|
||||
{
|
||||
if (m_backend) return;
|
||||
m_backend = new LEZWalletBackend(api, this);
|
||||
setBackend(m_backend);
|
||||
qDebug() << "LEZWalletPlugin: backend initialized";
|
||||
}
|
||||
|
||||
@ -1,14 +1,38 @@
|
||||
#pragma once
|
||||
#ifndef LEZ_WALLET_PLUGIN_H
|
||||
#define LEZ_WALLET_PLUGIN_H
|
||||
|
||||
#include <IComponent.h>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QtPlugin> // for Q_PLUGIN_METADATA, Q_INTERFACES
|
||||
#include "LEZWalletPluginInterface.h"
|
||||
#include "LogosViewPluginBase.h"
|
||||
|
||||
class LEZWalletPlugin : public QObject, public IComponent {
|
||||
class LogosAPI;
|
||||
class LEZWalletBackend;
|
||||
|
||||
// Thin plugin entry point. Holds an LEZWalletBackend and lets the
|
||||
// generated view-plugin base expose it to ui-host.
|
||||
class LEZWalletPlugin : public QObject,
|
||||
public LEZWalletPluginInterface,
|
||||
public LEZWalletBackendViewPluginBase
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(IComponent)
|
||||
Q_PLUGIN_METADATA(IID IComponent_iid FILE "metadata.json")
|
||||
Q_PLUGIN_METADATA(IID LEZWalletPluginInterface_iid FILE "../metadata.json")
|
||||
Q_INTERFACES(LEZWalletPluginInterface)
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QWidget* createWidget(LogosAPI* logosAPI = nullptr) override;
|
||||
void destroyWidget(QWidget* widget) override;
|
||||
explicit LEZWalletPlugin(QObject* parent = nullptr);
|
||||
~LEZWalletPlugin() override;
|
||||
|
||||
QString name() const override { return "lez_wallet_ui"; }
|
||||
QString version() const override { return "1.0.0"; }
|
||||
|
||||
// Called by ui-host after plugin load. Creates the backend and wires
|
||||
// it up with the provided LogosAPI.
|
||||
Q_INVOKABLE void initLogos(LogosAPI* api);
|
||||
|
||||
private:
|
||||
LEZWalletBackend* m_backend = nullptr;
|
||||
};
|
||||
|
||||
#endif // LEZ_WALLET_PLUGIN_H
|
||||
|
||||
19
src/LEZWalletPluginInterface.h
Normal file
19
src/LEZWalletPluginInterface.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef LEZ_WALLET_PLUGIN_INTERFACE_H
|
||||
#define LEZ_WALLET_PLUGIN_INTERFACE_H
|
||||
|
||||
#include <QtPlugin> // for Q_DECLARE_INTERFACE
|
||||
#include "interface.h"
|
||||
|
||||
// Marker interface used by Qt's plugin loader to identify the LEZ wallet UI
|
||||
// plugin. The actual API surface (Q_INVOKABLE methods, properties, signals)
|
||||
// lives in LEZWalletBackend.rep — this header only carries the IID.
|
||||
class LEZWalletPluginInterface : public PluginInterface
|
||||
{
|
||||
public:
|
||||
virtual ~LEZWalletPluginInterface() = default;
|
||||
};
|
||||
|
||||
#define LEZWalletPluginInterface_iid "org.logos.LEZWalletPluginInterface"
|
||||
Q_DECLARE_INTERFACE(LEZWalletPluginInterface, LEZWalletPluginInterface_iid)
|
||||
|
||||
#endif // LEZ_WALLET_PLUGIN_INTERFACE_H
|
||||
@ -2,7 +2,6 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import LEZWalletBackend
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
import "views"
|
||||
@ -10,6 +9,18 @@ import "views"
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
readonly property var backend: logos.module("lez_wallet_ui")
|
||||
readonly property var accountModel: logos.model("lez_wallet_ui", "accountModel")
|
||||
property bool ready: false
|
||||
|
||||
Connections {
|
||||
target: logos
|
||||
function onViewModuleReadyChanged(moduleName, isReady) {
|
||||
if (moduleName === "lez_wallet_ui")
|
||||
root.ready = isReady && root.backend !== null
|
||||
}
|
||||
}
|
||||
|
||||
// Map wallet FFI error codes to user-facing strings. Matches lssa/wallet-ffi WalletFfiError enum.
|
||||
QtObject {
|
||||
id: ffiErrors
|
||||
@ -46,23 +57,45 @@ Rectangle {
|
||||
}
|
||||
return errorMessage
|
||||
}
|
||||
|
||||
// Parse a transfer result JSON string and write to dashboardView.
|
||||
// Used by all three transfer handlers below.
|
||||
function applyTransferResult(dashboardView, raw) {
|
||||
var msg = raw || ""
|
||||
var isError = false
|
||||
try {
|
||||
var obj = JSON.parse(raw)
|
||||
if (obj.success) {
|
||||
msg = obj.tx_hash ? qsTr("Success. Tx: %1").arg(obj.tx_hash) : qsTr("Success.")
|
||||
} else if (obj.error) {
|
||||
msg = ffiErrors.format(obj.error)
|
||||
isError = true
|
||||
}
|
||||
} catch (e) {
|
||||
if (msg.length > 0) isError = true
|
||||
}
|
||||
dashboardView.transferResult = msg
|
||||
dashboardView.transferResultIsError = isError
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property bool isWalletOpen: backend && backend.isWalletOpen
|
||||
onIsWalletOpenChanged: updateStack(isWalletOpen)
|
||||
onIsWalletOpenChanged: if (root.ready) updateStack(isWalletOpen)
|
||||
|
||||
function updateStack(walletOpen) {
|
||||
if(walletOpen) {
|
||||
stackView.push(mainView)
|
||||
} else {
|
||||
stackView.push(onboardingView)
|
||||
}
|
||||
stackView.replace(walletOpen ? mainView : onboardingView)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: d.updateStack(backend && backend.isWalletOpen)
|
||||
onReadyChanged: if (ready) d.updateStack(d.isWalletOpen)
|
||||
|
||||
Component.onCompleted: {
|
||||
root.ready = root.backend !== null
|
||||
&& logos.isViewModuleReady("lez_wallet_ui")
|
||||
if (root.ready) d.updateStack(d.isWalletOpen)
|
||||
}
|
||||
|
||||
color: Theme.palette.background
|
||||
|
||||
@ -73,11 +106,19 @@ Rectangle {
|
||||
Component {
|
||||
id: onboardingView
|
||||
OnboardingView {
|
||||
storePath: backend.storagePath
|
||||
configPath: backend.configPath
|
||||
storePath: backend ? backend.storagePath : ""
|
||||
configPath: backend ? backend.configPath : ""
|
||||
onCreateWallet: function(configPath, storagePath, password) {
|
||||
if (!backend || !backend.createNew(configPath, storagePath, password))
|
||||
createError = qsTr("Failed to create wallet. Check paths and try again.")
|
||||
if (!backend) return
|
||||
logos.watch(backend.createNew(configPath, storagePath, password),
|
||||
function(ok) {
|
||||
if (!ok)
|
||||
createError = qsTr("Failed to create wallet. Check paths and try again.")
|
||||
},
|
||||
function(error) {
|
||||
createError = qsTr("Error creating wallet: %1").arg(error)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,87 +127,56 @@ Rectangle {
|
||||
id: mainView
|
||||
DashboardView {
|
||||
id: dashboardView
|
||||
accountModel: backend ? backend.accountModel : null
|
||||
accountModel: root.accountModel
|
||||
|
||||
onCreatePublicAccountRequested: {
|
||||
if (!backend) {
|
||||
console.warning("backend is null")
|
||||
return
|
||||
}
|
||||
backend.createAccountPublic()
|
||||
if (!backend) { console.warn("backend is null"); return }
|
||||
// Result not consumed here — accountModel updates via NOTIFY when
|
||||
// the backend's refreshAccounts() runs after creation.
|
||||
logos.watch(backend.createAccountPublic(),
|
||||
function(_id) { /* ignored */ },
|
||||
function(error) { console.warn("createAccountPublic failed:", error) })
|
||||
}
|
||||
onCreatePrivateAccountRequested: {
|
||||
if (!backend) {
|
||||
console.warning("backend is null")
|
||||
return
|
||||
}
|
||||
backend.createAccountPrivate()
|
||||
if (!backend) { console.warn("backend is null"); return }
|
||||
logos.watch(backend.createAccountPrivate(),
|
||||
function(_id) { /* ignored */ },
|
||||
function(error) { console.warn("createAccountPrivate failed:", error) })
|
||||
}
|
||||
onFetchBalancesRequested: {
|
||||
if (!backend) {
|
||||
console.warning("backend is null")
|
||||
return
|
||||
}
|
||||
backend.refreshBalances()
|
||||
if (!backend) { console.warn("backend is null"); return }
|
||||
backend.refreshBalances() // void slot, fire-and-forget
|
||||
}
|
||||
onTransferPublicRequested: (fromId, toAddress, amount) => {
|
||||
if (!backend) return
|
||||
var raw = backend.transferPublic(fromId, toAddress, amount)
|
||||
var msg = raw || ""
|
||||
var isError = false
|
||||
try {
|
||||
var obj = JSON.parse(raw)
|
||||
if (obj.success) {
|
||||
msg = obj.tx_hash ? qsTr("Success. Tx: %1").arg(obj.tx_hash) : qsTr("Success.")
|
||||
} else if (obj.error) {
|
||||
msg = ffiErrors.format(obj.error)
|
||||
isError = true
|
||||
}
|
||||
} catch (e) {
|
||||
if (msg.length > 0) isError = true
|
||||
}
|
||||
dashboardView.transferResult = msg
|
||||
dashboardView.transferResultIsError = isError
|
||||
logos.watch(backend.transferPublic(fromId, toAddress, amount),
|
||||
function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) },
|
||||
function(error) {
|
||||
dashboardView.transferResult = qsTr("Error: %1").arg(error)
|
||||
dashboardView.transferResultIsError = true
|
||||
})
|
||||
}
|
||||
onTransferPrivateRequested: (fromId, toKeysJsonOrAddress, amount) => {
|
||||
if (!backend) return
|
||||
var raw = backend.transferPrivate(fromId, toKeysJsonOrAddress, amount)
|
||||
var msg = raw || ""
|
||||
var isError = false
|
||||
try {
|
||||
var obj = JSON.parse(raw)
|
||||
if (obj.success) {
|
||||
msg = obj.tx_hash ? qsTr("Success. Tx: %1").arg(obj.tx_hash) : qsTr("Success.")
|
||||
} else if (obj.error) {
|
||||
msg = ffiErrors.format(obj.error)
|
||||
isError = true
|
||||
}
|
||||
} catch (e) {
|
||||
if (msg.length > 0) isError = true
|
||||
}
|
||||
dashboardView.transferResult = msg
|
||||
dashboardView.transferResultIsError = isError
|
||||
logos.watch(backend.transferPrivate(fromId, toKeysJsonOrAddress, amount),
|
||||
function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) },
|
||||
function(error) {
|
||||
dashboardView.transferResult = qsTr("Error: %1").arg(error)
|
||||
dashboardView.transferResultIsError = true
|
||||
})
|
||||
}
|
||||
onTransferPrivateOwnedRequested: (fromId, toAccountId, amount) => {
|
||||
if (!backend) return
|
||||
var raw = backend.transferPrivateOwned(fromId, toAccountId, amount)
|
||||
var msg = raw || ""
|
||||
var isError = false
|
||||
try {
|
||||
var obj = JSON.parse(raw)
|
||||
if (obj.success) {
|
||||
msg = obj.tx_hash ? qsTr("Success. Tx: %1").arg(obj.tx_hash) : qsTr("Success.")
|
||||
} else if (obj.error) {
|
||||
msg = ffiErrors.format(obj.error)
|
||||
isError = true
|
||||
}
|
||||
} catch (e) {
|
||||
if (msg.length > 0) isError = true
|
||||
}
|
||||
dashboardView.transferResult = msg
|
||||
dashboardView.transferResultIsError = isError
|
||||
logos.watch(backend.transferPrivateOwned(fromId, toAccountId, amount),
|
||||
function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) },
|
||||
function(error) {
|
||||
dashboardView.transferResult = qsTr("Error: %1").arg(error)
|
||||
dashboardView.transferResultIsError = true
|
||||
})
|
||||
}
|
||||
onCopyRequested: (copyText) => {
|
||||
if (backend) backend.copyToClipboard(copyText)
|
||||
}
|
||||
onCopyRequested: (copyText) => backend.copyToClipboard(copyText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,10 @@ import Logos.Controls
|
||||
ComboBox {
|
||||
id: root
|
||||
|
||||
// Forwarded from AccountDelegate's copy button — bubble up to the parent
|
||||
// view, which calls backend.copyToClipboard().
|
||||
signal copyRequested(string text)
|
||||
|
||||
leftPadding: 12
|
||||
rightPadding: 12
|
||||
implicitHeight: 40
|
||||
@ -55,6 +59,7 @@ ComboBox {
|
||||
delegate: AccountDelegate {
|
||||
width: root.popup ? (root.popup.width - root.popup.leftPadding - root.popup.rightPadding) : 368
|
||||
highlighted: root.highlightedIndex === index
|
||||
onCopyRequested: (text) => root.copyRequested(text)
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
|
||||
@ -8,6 +8,12 @@ import Logos.Controls
|
||||
ItemDelegate {
|
||||
id: root
|
||||
|
||||
// Emitted when the user clicks the copy icon. The parent connects this
|
||||
// to backend.copyToClipboard(...) — AccountDelegate doesn't reach into
|
||||
// the global QML scope for `backend` since it now lives behind the
|
||||
// logos.module() bridge in the parent view.
|
||||
signal copyRequested(string text)
|
||||
|
||||
leftPadding: Theme.spacing.medium
|
||||
rightPadding: Theme.spacing.medium
|
||||
topPadding: Theme.spacing.medium
|
||||
@ -70,7 +76,7 @@ ItemDelegate {
|
||||
LogosCopyButton {
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
onCopyText: backend.copyToClipboard(model.address)
|
||||
onCopyText: root.copyRequested(model.address)
|
||||
visible: addressLabel.text
|
||||
icon.color: Theme.palette.textMuted
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ Button {
|
||||
display: AbstractButton.IconOnly
|
||||
flat: true
|
||||
|
||||
property string iconSource: "qrc:/lezwallet/icons/copy.svg"
|
||||
property string iconSource: Qt.resolvedUrl("../icons/copy.svg")
|
||||
|
||||
icon.source: root.iconSource
|
||||
icon.width: 24
|
||||
@ -21,7 +21,7 @@ Button {
|
||||
icon.color: Theme.palette.textSecondary
|
||||
|
||||
function reset() {
|
||||
iconSource = "qrc:/lezwallet/icons/copy.svg"
|
||||
iconSource = Qt.resolvedUrl("../icons/copy.svg")
|
||||
}
|
||||
|
||||
Timer {
|
||||
@ -33,7 +33,7 @@ Button {
|
||||
|
||||
onClicked: {
|
||||
root.copyText()
|
||||
root.iconSource = "qrc:/lezwallet/icons/checkmark.svg"
|
||||
root.iconSource = Qt.resolvedUrl("../icons/checkmark.svg")
|
||||
resetTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 462 B |
|
Before Width: | Height: | Size: 976 B After Width: | Height: | Size: 976 B |
@ -18,6 +18,7 @@ Rectangle {
|
||||
signal createPublicAccountRequested()
|
||||
signal createPrivateAccountRequested()
|
||||
signal fetchBalancesRequested()
|
||||
signal copyRequested(string text)
|
||||
|
||||
radius: Theme.spacing.radiusXlarge
|
||||
color: Theme.palette.backgroundSecondary
|
||||
@ -81,6 +82,7 @@ Rectangle {
|
||||
|
||||
delegate: AccountDelegate {
|
||||
width: listView.width
|
||||
onCopyRequested: (text) => root.copyRequested(text)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -39,6 +39,7 @@ Rectangle {
|
||||
onCreatePublicAccountRequested: root.createPublicAccountRequested()
|
||||
onCreatePrivateAccountRequested: root.createPrivateAccountRequested()
|
||||
onFetchBalancesRequested: root.fetchBalancesRequested()
|
||||
onCopyRequested: (text) => root.copyRequested(text)
|
||||
}
|
||||
|
||||
TransferPanel {
|
||||
|
||||
@ -149,7 +149,7 @@ Control {
|
||||
id: configFileDialog
|
||||
modality: Qt.NonModal
|
||||
nameFilters: ["JSON files (*.json)"]
|
||||
currentFolder: root.configPath ? d.configParentFolderUrl(oot.configPath) : ""
|
||||
currentFolder: root.configPath ? d.configParentFolderUrl(root.configPath) : ""
|
||||
onAccepted: {
|
||||
if (selectedFile) configPathField.text = selectedFile.toString().replace(/^file:\/\//, "")
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ Rectangle {
|
||||
signal transferPrivateOwnedRequested(string fromAccountId, string toAccountId, string amount)
|
||||
signal copyRequested(string copyText)
|
||||
|
||||
readonly property int fromFilterCount: fromAccountModel ? fromAccountModel.count : 0
|
||||
readonly property int fromFilterCount: (fromAccountModel && fromAccountModel.count) ? fromAccountModel.count : 0
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
@ -94,6 +94,7 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
model: fromAccountModel
|
||||
visible: fromFilterCount > 0
|
||||
onCopyRequested: (text) => root.copyRequested(text)
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,6 +131,7 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
model: fromAccountModel
|
||||
visible: d.isPrivateTab && d.useOwnedAccountForTo && fromFilterCount > 0
|
||||
onCopyRequested: (text) => root.copyRequested(text)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/lezwallet">
|
||||
<file>qml/ExecutionZoneWalletView.qml</file>
|
||||
<file>qml/controls/AccountComboBox.qml</file>
|
||||
<file>qml/controls/AccountDelegate.qml</file>
|
||||
<file>qml/popups/CreateAccountDialog.qml</file>
|
||||
<file>qml/views/qmldir</file>
|
||||
<file>qml/views/OnboardingView.qml</file>
|
||||
<file>qml/views/DashboardView.qml</file>
|
||||
<file>qml/views/AccountsPanel.qml</file>
|
||||
<file>qml/views/TransferPanel.qml</file>
|
||||
<file>qml/controls/LogosCopyButton.qml</file>
|
||||
<file>icons/checkmark.svg</file>
|
||||
<file>icons/copy.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
Loading…
x
Reference in New Issue
Block a user