From 5409e13ea38e24e23b7f628d168580522a2f24bd Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 13 Feb 2026 20:16:27 +0400 Subject: [PATCH] Did a bunch of refactoring --- src/StorageBackend.cpp | 300 ++++++++++++++++++++++------------------ src/StorageBackend.h | 53 +++---- src/StorageUIPlugin.cpp | 33 ++++- src/qml/StorageView.qml | 245 ++++++++++++++++++++++---------- 4 files changed, 390 insertions(+), 241 deletions(-) diff --git a/src/StorageBackend.cpp b/src/StorageBackend.cpp index 690e62c..3a82bb9 100644 --- a/src/StorageBackend.cpp +++ b/src/StorageBackend.cpp @@ -27,9 +27,6 @@ StorageBackend::StorageBackend(LogosAPI* logosAPI, QObject* parent) } m_logos = new LogosModules(m_logosAPI); - m_showDebug = false; - - initStorage(); } StorageBackend::~StorageBackend() @@ -38,44 +35,31 @@ StorageBackend::~StorageBackend() m_logos = nullptr; } -void StorageBackend::initStorage() { +LogosResult StorageBackend::init(const QString& configJson = "{}") { qDebug() << "StorageBackend::initStorage called"; - QString jsonConfig = "{}"; - - QFileInfo info("config.json"); - if (info.exists() && info.isFile()) { - debug("config.json is found, let's try to load it..."); - - QFile file("config.json"); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - jsonConfig = QString::fromUtf8(file.readAll()); - - debug("config.json content is: " + jsonConfig); - } else { - debug("Failed to load config.json"); - } - } - - LogosResult result = m_logos->storage_module.init(jsonConfig); + LogosResult result = m_logos->storage_module.init(m_configJson); qDebug() << "StorageBackend::initStorage: init result =" << result.success; if (!result.success) { - setStatus(Destroyed, result.getValue()); - return; + setStatus(Destroyed); + debug(result.getError()); + return result; } - setStatus(Stopped, "Storage module ready."); + setStatus(Stopped); if (!m_logos->storage_module.on("storageStart", [this](const QVariantList& data) { bool success = data[0].toBool(); if (!success) { QString message = data[1].toString(); - setStatus(Stopped, "Failed to start Storage module:" + message); + setStatus(Stopped); + debug("Failed to start Storage module:" + message); } else { - setStatus(Running, "Storage module started."); + setStatus(Running); + debug("Storage module started."); } })) { qWarning() << "StorageWidget: failed to subscribe to storageStart events"; @@ -86,9 +70,11 @@ void StorageBackend::initStorage() { if (!success) { QString message = data[1].toString(); - setStatus(Running, "Failed to stop Storage module:" + message); + setStatus(Running); + debug("Failed to stop Storage module:" + message); } else { - setStatus(Stopped, "Storage module stopped."); + setStatus(Stopped); + debug("Storage module stopped."); emit stopped(); } })) { @@ -171,53 +157,75 @@ void StorageBackend::initStorage() { qWarning() << "StorageWidget: failed to subscribe to storageDownloadProgress events"; } - startStop(); + m_configJson = configJson; + emit configJsonChanged(); + + debug("config.json content is: " + m_configJson); + + return result; } -void StorageBackend::setStatus(StorageStatus newStatus, const QString& statusText) { - if (m_status != newStatus || m_statusText != statusText) { +void StorageBackend::setStatus(StorageStatus newStatus) { + if (m_status != newStatus) { m_status = newStatus; - m_statusText = statusText; emit statusChanged(); - - debug(statusText); } } -void StorageBackend::startStop() { - qDebug() << "StorageBackend: startStop method called"; +LogosResult StorageBackend::start(const QString& newConfigJson) { + qDebug() << "StorageBackend: start method called"; - // QString s = "hello"; - // emit test(0, s); - if (m_status == Destroyed || m_status == Starting || m_status == Stopping) { - // TODO display the m_status properly - debug("StorageBackend: Cannot start/stop Storage in current state:" + m_status); - return; + if (newConfigJson != "") { + reloadIfChanged(newConfigJson); } - if (m_status != Running) { - setStatus(Starting, "Starting Storage module..."); - - auto result = m_logos->storage_module.start(); - - if (!result.success) { - setStatus(Stopped, result.getValue()); - return; - } - - qDebug() << "StorageBackend: start command sent, waiting for events."; - } else { - stop(); + if (m_status != Stopped) { + debug("The Storage Module is not initialised properly."); + return {false, "The Storage Module is not initialised properly."}; } + + if (m_status == Running) { + debug("The Storage Module is already started."); + return {false, "The Storage Module is already started."}; + } + + setStatus(Starting); + debug("Starting Storage module..."); + + auto result = m_logos->storage_module.start(); + + if (!result.success) { + setStatus(Stopped); + debug(result.getError()); + return result; + } + + qDebug() << "StorageBackend: start command sent, waiting for events."; + + return result; } void StorageBackend::stop() { - setStatus(Stopping, "Stopping Storage module..."); + qDebug() << "StorageBackend: stop method called"; + + if (m_status == StorageStatus::Stopping) { + debug("The Storage Module is already stopping."); + return; + } + + if (m_status != StorageStatus::Running) { + debug("The Storage Module is not started."); + return; + } + + setStatus(Stopping); + debug("Stopping Storage module..."); auto result = m_logos->storage_module.stop(); if (!result.success) { - setStatus(Running, result.getValue()); + setStatus(Running); + debug(result.getError()); return; } @@ -231,38 +239,13 @@ void StorageBackend::destroy() { auto result = m_logos->storage_module.destroy(); if (!result.success) { - setStatus(status, result.getValue()); + debug(result.getError()); return; } qDebug() << "StorageBackend: Storage module destroyed."; } -QString StorageBackend::statusText() const { return m_statusText; } - -QString StorageBackend::startStopText() const { - if (m_status != Running) { - return "Start"; - } else { - return "Stop"; - } -} - -bool StorageBackend::canStartStop() const { return m_status == Running || m_status == Stopped; } - -bool StorageBackend::isRunning() { return m_status == Running; } - -QString StorageBackend::peerId() const { return m_peerId; }; - -void StorageBackend::setPeerId(const QString& peerId) { m_peerId = peerId; } - -bool StorageBackend::showDebug() const { return m_showDebug; }; - -void StorageBackend::setShowDebug(const bool showDebug) { - m_showDebug = showDebug; - emit showDebugChanged(); -} - QString StorageBackend::debugLogs() const { return m_debugLogs; }; void StorageBackend::debug(const QString& log) { @@ -277,15 +260,13 @@ void StorageBackend::debug(const QString& log) { qDebug() << "StorageBackend: " << log; } -bool StorageBackend::isInitialised() const { return m_status != Destroyed; } - void StorageBackend::tryDebug() { auto result = m_logos->storage_module.debug(); - debug("Debug " + result.getValue()); + debug("Debug " + result.getString()); } -void StorageBackend::tryPeerConnect() { - qDebug().noquote() << "StorageBackend: tryPeerConnect called with peerId=" << m_peerId; +void StorageBackend::tryPeerConnect(const QString& peerId) { + qDebug().noquote() << "StorageBackend: tryPeerConnect called with peerId=" << peerId; // LogosResult result2 = m_logos->storage_module.space(); // QVariantMap space = result2.getValue(); @@ -301,7 +282,7 @@ void StorageBackend::tryPeerConnect() { // debug("quotaReservedBytes " + QString::number(quotaReservedBytes)); // LogosResult result = m_logos->storage_module.dataDir(); - // QString myDataDir = result.getValue(); + // QString myDataDir = result.getString(); // qDebug() << "StorageBackend: tryPeerConnect dataDir=" << myDataDir; // QString peerId = m_logos->storage_module.peerId(); @@ -310,12 +291,12 @@ void StorageBackend::tryPeerConnect() { // qDebug() << "StorageBackend: Peer ID is empty."; // return; // } - auto result = m_logos->storage_module.connect(m_peerId, QStringList()); + auto result = m_logos->storage_module.connect(peerId, QStringList()); qDebug() << "StorageBackend: peerConnect result =" << result.value; // auto result = m_logos->storage_module.debug(); - // debug("Debug " + result.getValue()); + // debug("Debug " + result.getString()); // QString filename = "test.txt"; // QString sessionId = m_logos->storage_module.uploadInit(filename); @@ -358,8 +339,7 @@ void StorageBackend::tryUploadFile(const QUrl& url) { if (!url.isLocalFile()) { qWarning() << "Not a local file"; - m_statusText = "The provided URL is not a local file."; - emit statusChanged(); + debug("The provided URL is not a local file."); return; } @@ -379,7 +359,7 @@ void StorageBackend::tryUploadFile(const QUrl& url) { // this, // [this, result]() { // if (!result.success) { - // setStatus(m_status, result.getValue()); + // setStatus(m_status, result.getString()); // return; // } @@ -393,7 +373,7 @@ void StorageBackend::tryUploadFile(const QUrl& url) { LogosResult result = m_logos->storage_module.uploadUrl(url); if (!result.success) { - setStatus(m_status, result.getValue()); + debug(result.getError()); return; } @@ -433,12 +413,12 @@ void StorageBackend::tryUploadFile(const QUrl& url) { // LogosResult result = m_logos->storage_module.uploadInit("test.txt", chunkSize); // if (!result.success) { -// debug(result.getValue()); +// debug(result.getString()); // file.close(); // return; // } -// QString sessionId = result.getValue(); +// QString sessionId = result.getString(); // while (!file.atEnd()) { // QByteArray chunk = file.read(chunkSize); @@ -470,7 +450,7 @@ void StorageBackend::tryUploadFile(const QUrl& url) { // return; // } -// qDebug() << "Upload complete, CID:" << result.getValue(); +// qDebug() << "Upload complete, CID:" << result.getString(); // file.close(); // } @@ -480,8 +460,7 @@ void StorageBackend::tryDownloadFile(const QString& cid, const QUrl& url) { if (!url.isLocalFile()) { qWarning() << "Not a local file"; - m_statusText = "The provided URL is not a local file."; - emit statusChanged(); + debug("The provided URL is not a local file."); return; } @@ -495,7 +474,7 @@ void StorageBackend::tryDownloadFile(const QString& cid, const QUrl& url) { LogosResult result = m_logos->storage_module.downloadToUrl(cid, url, false); if (!result.success) { - setStatus(m_status, result.getValue()); + debug(result.getError()); return; } @@ -510,7 +489,7 @@ void StorageBackend::exists(const QString& cid) { LogosResult result = m_logos->storage_module.exists(cid); if (!result.success) { - debug("StorageBackend::exists failed with error=" + result.getValue()); + debug("StorageBackend::exists failed with error=" + result.getError()); return; } @@ -523,7 +502,7 @@ void StorageBackend::remove(const QString& cid) { LogosResult result = m_logos->storage_module.remove(cid); if (!result.success) { - debug("StorageBackend::remove failed with error=" + result.getValue()); + debug("StorageBackend::remove failed with error=" + result.getError()); return; } @@ -536,7 +515,7 @@ void StorageBackend::fetch(const QString& cid) { LogosResult result = m_logos->storage_module.fetch(cid); if (!result.success) { - debug("StorageBackend::fetch failed with error=" + result.getValue()); + debug("StorageBackend::fetch failed with error=" + result.getError()); return; } @@ -549,11 +528,11 @@ void StorageBackend::version() { LogosResult result = m_logos->storage_module.version(); if (!result.success) { - debug("StorageBackend::version failed with error=" + result.getValue()); + debug("StorageBackend::version failed with error=" + result.getError()); return; } - debug("Version: " + result.getValue()); + debug("Version: " + result.getString()); } void StorageBackend::showPeerId() { @@ -562,11 +541,11 @@ void StorageBackend::showPeerId() { LogosResult result = m_logos->storage_module.peerId(); if (!result.success) { - debug("StorageBackend::peerId failed with error=" + result.getValue()); + debug("StorageBackend::peerId failed with error=" + result.getError()); return; } - debug("Peer ID: " + result.getValue()); + debug("Peer ID: " + result.getString()); } void StorageBackend::spr() { @@ -575,11 +554,11 @@ void StorageBackend::spr() { LogosResult result = m_logos->storage_module.spr(); if (!result.success) { - debug("StorageBackend::spr failed with error=" + result.getValue()); + debug("StorageBackend::spr failed with error=" + result.getError()); return; } - debug("SPR: " + result.getValue()); + debug("SPR: " + result.getString()); } void StorageBackend::dataDir() { @@ -588,11 +567,11 @@ void StorageBackend::dataDir() { LogosResult result = m_logos->storage_module.dataDir(); if (!result.success) { - debug("StorageBackend::dataDir failed with error=" + result.getValue()); + debug("StorageBackend::dataDir failed with error=" + result.getError()); return; } - debug("Data dir: " + result.getValue()); + debug("Data dir: " + result.getString()); } void StorageBackend::downloadManifest(const QString& cid) { @@ -601,28 +580,28 @@ void StorageBackend::downloadManifest(const QString& cid) { LogosResult result = m_logos->storage_module.downloadManifest(cid); if (!result.success) { - debug("StorageBackend::downloadManifest failed with error=" + result.getValue()); + debug("StorageBackend::downloadManifest failed with error=" + result.getError()); return; } - debug("Manifest tree cid: " + result.getValue("treeCid")); - debug(QString("Manifest datasetSize %1").arg(result.getValue("datasetSize"))); - debug(QString("Manifest blockSize %1").arg(result.getValue("blockSize"))); - debug("Manifest filename: " + result.getValue("filename")); - debug("Manifest mimetype: " + result.getValue("mimetype")); + debug("Manifest tree cid: " + result.getString("treeCid")); + debug(QString("Manifest datasetSize %1").arg(result.getInt("datasetSize"))); + debug(QString("Manifest blockSize %1").arg(result.getInt("blockSize"))); + debug("Manifest filename: " + result.getString("filename")); + debug("Manifest mimetype: " + result.getString("mimetype")); } void StorageBackend::downloadManifests() { qDebug() << "StorageBackend::downloadManifests called"; LogosResult result = m_logos->storage_module.manifests(); - + QString error = result.getError(); if (!result.success) { - debug("StorageBackend::downloadManifests failed with error=" + result.getValue()); + debug("StorageBackend::downloadManifests failed with error=" + result.getError()); return; } - QVariantList manifestsList = result.getValue(); + QVariantList manifestsList = result.getList(); int count = manifestsList.size(); debug(QString("Found %1 manifests").arg(count)); @@ -648,14 +627,14 @@ void StorageBackend::space() { LogosResult result = m_logos->storage_module.space(); if (!result.success) { - debug("StorageBackend::space failed with error=" + result.getValue()); + debug("StorageBackend::space failed with error=" + result.getError()); return; } - debug(QString("Space datasetSize %1").arg(result.getValue("totalBlocks"))); - debug(QString("Space quotaMaxBytes %1").arg(result.getValue("quotaMaxBytes"))); - debug(QString("Space quotaUsedBytes %1").arg(result.getValue("quotaUsedBytes"))); - debug(QString("Space quotaReservedBytes %1").arg(result.getValue("quotaReservedBytes"))); + debug(QString("Space datasetSize %1").arg(result.getInt("totalBlocks"))); + debug(QString("Space quotaMaxBytes %1").arg(result.getInt("quotaMaxBytes"))); + debug(QString("Space quotaUsedBytes %1").arg(result.getInt("quotaUsedBytes"))); + debug(QString("Space quotaReservedBytes %1").arg(result.getInt("quotaReservedBytes"))); } void StorageBackend::updateLogLevel(const QString& logLevel) { @@ -664,17 +643,74 @@ void StorageBackend::updateLogLevel(const QString& logLevel) { LogosResult result = m_logos->storage_module.updateLogLevel(logLevel); if (!result.success) { - debug("StorageBackend::updateLogLevel failed with error=" + result.getValue()); + debug("StorageBackend::updateLogLevel failed with error=" + result.getError()); return; } debug("Log level updated to " + logLevel); } -QString StorageBackend::cidText() const { - if (m_cid.isEmpty()) { - return "No CID available."; - } else { - return m_cid; +StorageBackend::StorageStatus StorageBackend::status() const { return m_status; } + +QString StorageBackend::cid() const { return m_cid; } + +QString StorageBackend::configJson() const { return m_configJson; } + +void StorageBackend::reloadIfChanged(const QString& configJson) { + if (configJson == m_configJson) { + return; } + + debug("New config detected"); + + QJsonDocument doc = QJsonDocument::fromJson(configJson.toUtf8()); + if (doc.isNull()) { + debug("Invalid json detected !"); + + m_configJson = configJson; + emit configJsonChanged(); + + return; + } + + if (m_status == StorageStatus::Running || m_status == StorageStatus::Stopping || + m_status == StorageStatus::Starting) { + debug("Cannot reload the config while running, stopping or starting..."); + return; + } + + if (m_status == StorageStatus::Stopped) { + LogosResult result = m_logos->storage_module.destroy(); + + if (!result.success) { + debug("Failed to destroy the context error=" + result.getError()); + return; + } else { + setStatus(StorageStatus::Destroyed); + } + } + + LogosResult result = m_logos->storage_module.init(configJson); + + if (!result.success) { + debug("Failed to init context with new config, will rollback, error=" + result.getError()); + + LogosResult result = m_logos->storage_module.init(m_configJson); + + if (!result.success) { + debug("Failed to init context with old config, that's a serious issue, error=" + result.getError()); + } else { + debug("Old config restored"); + setStatus(StorageStatus::Stopped); + + m_configJson = configJson; + emit configJsonChanged(); + } + return; + } + + debug("New config loaded successfully"); + + m_configJson = configJson; + setStatus(StorageStatus::Stopped); } diff --git a/src/StorageBackend.h b/src/StorageBackend.h index cb8370a..8838a7f 100644 --- a/src/StorageBackend.h +++ b/src/StorageBackend.h @@ -13,41 +13,28 @@ static const int RET_PROGRESS = 3; class StorageBackend : public QObject { Q_OBJECT QML_ELEMENT - Q_PROPERTY(bool canStartStop READ canStartStop NOTIFY statusChanged) - Q_PROPERTY(bool isRunning READ isRunning NOTIFY statusChanged) - Q_PROPERTY(bool showDebug READ showDebug WRITE setShowDebug NOTIFY showDebugChanged) - Q_PROPERTY(QString startStopText READ startStopText NOTIFY statusChanged) - Q_PROPERTY(QString statusText READ statusText NOTIFY statusChanged) - Q_PROPERTY(QString cidText READ cidText NOTIFY cidChanged) - Q_PROPERTY(QString peerId READ peerId WRITE setPeerId NOTIFY peerIdChanged) Q_PROPERTY(QString debugLogs READ debugLogs NOTIFY debugLogsChanged) + Q_PROPERTY(StorageStatus status READ status NOTIFY statusChanged) + Q_PROPERTY(QString cid READ cid NOTIFY cidChanged) + Q_PROPERTY(QString configJson READ configJson NOTIFY configJsonChanged) public: enum StorageStatus { Stopped = 0, Starting, Running, Stopping, Destroyed }; Q_ENUM(StorageStatus) - QString startStopText() const; - QString statusText() const; - QString cidText() const; - QString peerId() const; + QString cid() const; QString debugLogs() const; - - bool showDebug() const; - bool canStartStop() const; - bool isRunning() const; - - void setPeerId(const QString& peerId); - void setShowDebug(const bool showDebug); + StorageStatus status() const; + QString configJson() const; explicit StorageBackend(LogosAPI* logosAPI = nullptr, QObject* parent = nullptr); ~StorageBackend(); public slots: - void startStop(); + LogosResult start(const QString& configJson = ""); void destroy(); - bool isRunning(); void stop(); - void tryPeerConnect(); + void tryPeerConnect(const QString& peerId); void tryDebug(); void tryUpload(); void tryUploadFinalize(); @@ -63,36 +50,28 @@ class StorageBackend : public QObject { void downloadManifest(const QString& cid); void downloadManifests(); void space(); + LogosResult init(const QString& configJson); void updateLogLevel(const QString& logLevel); - bool isInitialised() const; - signals: void statusChanged(); - void peerIdChanged(); - void showDebugChanged(); void debugLogsChanged(); - void cidChanged(); void stopped(); - void test(int code, const QString& msg); + void cidChanged(); + void configJsonChanged(); private slots: private: - void setStatus(StorageStatus newStatus, const QString& statusText); + void setStatus(StorageStatus newStatus); void peerConnect(const QString& peerId); void debug(const QString& log); - void initStorage(); + void reloadIfChanged(const QString& configJson); - StorageStatus m_status; LogosAPI* m_logosAPI; LogosModules* m_logos; - - QString m_statusText; - QString m_cid; - QString m_sessionId; - QString m_peerId; + StorageStatus m_status; QString m_debugLogs; - - bool m_showDebug; + QString m_cid; + QString m_configJson; }; diff --git a/src/StorageUIPlugin.cpp b/src/StorageUIPlugin.cpp index c8badd4..6ab6e03 100644 --- a/src/StorageUIPlugin.cpp +++ b/src/StorageUIPlugin.cpp @@ -31,6 +31,35 @@ QWidget* StorageUIPlugin::createWidget(LogosAPI* logosAPI) { root->setProperty("backend", QVariant::fromValue(static_cast(backend))); + QFileInfo info("config.json"); + QString configJson = "{}"; + + if (info.exists() && info.isFile()) { + qDebug() << "StorageUIPlugin: config.json is found, let's try to load it..."; + + QFile file("config.json"); + if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) { + configJson = QString::fromUtf8(file.readAll()); + + qDebug() << "StorageUIPlugin: config.json is found, let's try to load it... configJson=" << configJson; + } else { + qDebug() << "StorageUIPlugin: Failed to load config.json"; + } + } + + LogosResult result = backend->init(configJson); + + if (!result.success) { + QString error = result.getError(); + qWarning() << "StorageUIPlugin: Failed to init backend, will use mock version:" << error; + } else { + result = backend->start(); + + if (!result.success) { + qWarning() << "StorageUIPlugin: Failed to init backend, will use mock version:" << result.getError(); + } + } + return quickWidget; } @@ -66,13 +95,13 @@ void StorageUIPlugin::destroyWidget(QWidget* widget) { return; } - if (!backend->isInitialised()) { + if (backend->status() != StorageBackend::StorageStatus::Destroyed) { qDebug() << "StorageUIPlugin::destroyWidget: backend is not initialised so let's detroy it."; quickWidget->deleteLater(); return; } - if (!backend->isRunning()) { + if (backend->status() == StorageBackend::StorageStatus::Running) { qDebug() << "StorageUIPlugin::destroyWidget: backend is not running so let's detroy it."; backend->destroy(); diff --git a/src/qml/StorageView.qml b/src/qml/StorageView.qml index 5ceefd4..e5846a7 100644 --- a/src/qml/StorageView.qml +++ b/src/qml/StorageView.qml @@ -1,9 +1,14 @@ import QtQuick import QtQuick.Controls import QtQuick.Dialogs +import QtQuick.Layouts -//import QtQuick.Layouts Rectangle { + id: root + width: 400 + height: 700 + color: "#000000" + property var backend: mockBackend readonly property int stopped: 0 readonly property int starting: 1 @@ -14,38 +19,56 @@ Rectangle { property string downloadDestination: "" property url downloadCid: "" property string logLevel: "" + property bool showDebug: false + property url uploadCid: root.backend.cid + property url configJson: root.backend.configJson - id: root - width: 400 - height: 700 - color: "#000000" + function getStatusLabel() { + switch (backend.status) { + case stopped: + return "Logos Storage stopped." + case starting: + return "Logos Storage is starting..." + case running: + return "Logos Storage started." + case stopping: + return "Logos Storage is stopping..." + case destroyed: + return "Logos Storage is not initialised." + } + } + + function startStopText() { + if (backend.status == running) { + return "Stop" + } + return "Start" + } + + function canStartStop() { + return backend.status == running || backend.status == stopped + } + + function isRunning() { + return backend.status == running + } QtObject { id: mockBackend - signal test(int code, string msg) - property var status: root.stopped - property var statusText: "Destroyed" - property var startStopText: "Start" - property var canStartStop: true - property bool showDebug: false - property var debugLogs: "" + property var debugLogs: "Hello !" + property var configJson: "{}" - function startStop() { - console.log("Start") - if (status === root.running) { - status = root.stopped - statusText = "Stopped" - startStopText = "Start" - } else { - status = root.running - statusText = "Started" - startStopText = "Stop" - } + function start(newConfigJson) { + status = root.running } - function tryPeerConnect() { + function stop() { + status = root.stopped + } + + function tryPeerConnect(peerId) { console.log("Attempting peer connection...") } @@ -101,7 +124,7 @@ Rectangle { Text { id: statusTextElement objectName: "status" - text: root.backend.statusText + text: root.getStatusLabel() color: "white" font.pointSize: 20 anchors.top: parent.top @@ -113,9 +136,10 @@ Rectangle { id: startStopButton objectName: "startStopButton" anchors.leftMargin: 50 - text: root.backend.startStopText - enabled: root.backend.canStartStop - onClicked: root.backend.startStop() + text: root.startStopText() + enabled: root.canStartStop() + onClicked: root.backend.status == root.stopped ? root.backend.start( + jsonEditor.text) : root.backend.stop() anchors.horizontalCenter: parent.horizontalCenter anchors.top: statusTextElement.bottom anchors.topMargin: 10 @@ -124,13 +148,13 @@ Rectangle { TextEdit { id: cidTextEdit objectName: "cid" - text: root.backend.cidText color: "white" font.pointSize: 14 readOnly: true anchors.horizontalCenter: parent.horizontalCenter anchors.top: startStopButton.bottom anchors.topMargin: 10 + text: root.uploadCid } Button { @@ -140,7 +164,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.top: cidTextEdit.bottom anchors.topMargin: 15 - enabled: root.backend.isRunning + enabled: root.isRunning } TextField { @@ -160,10 +184,10 @@ Rectangle { id: peerConnectButton objectName: "peerConnectButton" text: "Peer connect" - onClicked: root.backend.tryPeerConnect() + onClicked: root.backend.tryPeerConnect(root.peerId) anchors.top: peerIdField.bottom anchors.horizontalCenter: parent.horizontalCenter - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 10 } @@ -174,7 +198,7 @@ Rectangle { onClicked: root.backend.tryDebug() anchors.top: peerConnectButton.bottom anchors.horizontalCenter: parent.horizontalCenter - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 50 } @@ -185,7 +209,7 @@ Rectangle { onClicked: root.backend.showPeerId() anchors.top: peerConnectButton.bottom anchors.right: debugButton.left - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 50 } @@ -196,7 +220,7 @@ Rectangle { onClicked: root.backend.dataDir() anchors.top: peerConnectButton.bottom anchors.right: peerIdButton.left - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 50 } @@ -207,7 +231,7 @@ Rectangle { onClicked: root.backend.spr() anchors.top: peerConnectButton.bottom anchors.left: debugButton.right - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 50 } @@ -218,7 +242,7 @@ Rectangle { onClicked: root.backend.version() anchors.top: peerConnectButton.bottom anchors.left: sprButton.right - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 50 } @@ -241,7 +265,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.top: cidDownloadField.bottom anchors.topMargin: 15 - enabled: root.backend.isRunning + enabled: root.isRunning } Button { @@ -252,7 +276,7 @@ Rectangle { root.downloadDestination) anchors.top: openFile2.bottom anchors.horizontalCenter: parent.horizontalCenter - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 10 } @@ -263,7 +287,7 @@ Rectangle { onClicked: root.backend.exists(root.downloadCid) anchors.top: openFile2.bottom anchors.left: cidDownloadButton.right - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 10 } @@ -274,7 +298,7 @@ Rectangle { onClicked: root.backend.fetch(root.downloadCid) anchors.top: openFile2.bottom anchors.left: existsButton.right - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 10 } @@ -285,7 +309,7 @@ Rectangle { onClicked: root.backend.remove(root.downloadCid) anchors.top: openFile2.bottom anchors.right: cidDownloadButton.left - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 10 } @@ -296,7 +320,7 @@ Rectangle { onClicked: root.backend.downloadManifest(root.downloadCid) anchors.top: openFile2.bottom anchors.right: removeButton.left - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 10 } @@ -307,7 +331,7 @@ Rectangle { onClicked: root.backend.downloadManifests() anchors.top: cidDownloadButton.bottom anchors.horizontalCenter: parent.horizontalCenter - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 10 } @@ -318,7 +342,7 @@ Rectangle { onClicked: root.backend.space() anchors.top: cidDownloadButton.bottom anchors.right: downloadManifestsButton.left - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 10 } @@ -341,7 +365,7 @@ Rectangle { onClicked: root.backend.updateLogLevel(root.logLevel) anchors.top: logLevelField.bottom anchors.horizontalCenter: parent.horizontalCenter - enabled: root.backend.isRunning + enabled: root.isRunning anchors.topMargin: 10 } @@ -406,38 +430,119 @@ Rectangle { anchors.bottom: parent.bottom height: 150 color: "#111111" - visible: root.backend.showDebug // or: visible: showDebug + visible: root.showDebug // or: visible: showDebug - Flickable { - id: flick - anchors.fill: parent - clip: true + TabBar { + id: bar + width: parent.width - contentWidth: width - contentHeight: debugText.paintedHeight + TabButton { + text: qsTr("Logs") + } - TextEdit { - id: debugText - width: flick.width - text: root.backend.debugLogs - color: "#dddddd" - font.family: "monospace" - font.pixelSize: 12 - wrapMode: Text.WrapAnywhere - readOnly: true - - // ✅ auto-scroll to bottom on update - onTextChanged: Qt.callLater(function () { - flick.contentY = Math.max( - 0, flick.contentHeight - flick.height) - }) + TabButton { + text: qsTr("Config") } } + StackLayout { + id: stackLayout + currentIndex: bar.currentIndex + anchors.top: bar.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + Item { + id: homeTab + + Flickable { + id: flick + anchors.fill: parent + clip: true + + contentWidth: width + contentHeight: debugText.paintedHeight + + TextEdit { + id: debugText + width: flick.width + text: root.backend.debugLogs + color: "#dddddd" + font.family: "monospace" + font.pixelSize: 12 + wrapMode: Text.WrapAnywhere + readOnly: true + + onTextChanged: Qt.callLater(function () { + flick.contentY = Math.max( + 0, flick.contentHeight - flick.height) + }) + } + } + } + Rectangle { + id: discoverTab + + ScrollView { + anchors.fill: parent + + TextArea { + id: jsonEditor + font.family: "monospace" + font.pixelSize: 12 + color: "#d4d4d4" + width: parent.width + height: parent.height + + background: Rectangle { + color: "#1e1e1e" + border.color: jsonEditor.isValid ? "#3a3a3a" : "#ff0000" + border.width: 1 + } + + property bool isValid: true + + Connections { + target: root.backend + + function onConfigJsonChanged() { + jsonEditor.text = root.backend.configJson + try { + const jsonData = JSON.parse(jsonEditor.text) + jsonEditor.isValid = true + } catch (e) { + jsonEditor.isValid = false + } + } + } + + Component.onCompleted: { + text = root.backend.configJson + + try { + const jsonData = JSON.parse(text) + isValid = true + } catch (e) { + isValid = false + } + } + + onTextChanged: { + try { + const jsonData = JSON.parse(text) + isValid = true + } catch (e) { + isValid = false + } + } + } + } + } + } Shortcut { sequence: "Ctrl+D" - onActivated: root.backend.showDebug = !root.backend.showDebug - // if using local var: onActivated: showDebug = !showDebug + onActivated: root.showDebug = !root.showDebug } } }