diff --git a/src/StorageBackend.cpp b/src/StorageBackend.cpp index ebcda7c..0f591d6 100644 --- a/src/StorageBackend.cpp +++ b/src/StorageBackend.cpp @@ -45,13 +45,35 @@ StorageBackend::~StorageBackend() m_logos = nullptr; } +void StorageBackend::reportError(const QString& message) { + debug(message, "warning"); + emit error(message); +} + +void StorageBackend::debug(const QString& log, const QString& level) { + if (!m_debugLogs.isEmpty()) { + m_debugLogs += "\n"; + } + + QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate); + m_debugLogs += timestamp + ": " + log; + + // Notify the property bind debugLogs + emit debugLogsChanged(); + + if (level == "warning") { + qWarning() << "StorageBackend: " << log; + } else { + qDebug() << "StorageBackend: " << log; + } +} + LogosResult StorageBackend::init(const QString& configJson) { qDebug() << "StorageBackend::initStorage called"; m_config = QJsonDocument::fromJson(configJson.toUtf8()); if (m_config.isNull()) { - qDebug() << "StorageBackend::initStorage invalid json config" << configJson; - reportError("Failed to create the storage: invalid JSON config"); + reportError("Failed to create the storage: invalid JSON config:" + configJson); return {false, "", "Failed to create the storage, invalid json config"}; } @@ -73,15 +95,23 @@ LogosResult StorageBackend::init(const QString& configJson) { if (!success) { QString message = data[1].toString(); setStatus(Stopped); - debug("Failed to start Storage module:" + message); + + // Used in StartNode component to detect + // failure in the onboarding. emit startFailed(message); - reportError("Failed to start: " + message); + + reportError("Failed to start Storage Module: " + message); } else { setStatus(Running); + debug("Storage module started."); + // Fetch data to in order to come on the app + // with fresh data. StorageBackend::fetchWidgetsData(); + // Used in StartNode component to detect + // success in the onboarding. emit startCompleted(); } })) { @@ -92,14 +122,18 @@ LogosResult StorageBackend::init(const QString& configJson) { bool success = data[0].toBool(); if (!success) { - QString message = data[1].toString(); setStatus(Running); - debug("Failed to stop Storage module:" + message); + + QString message = data[1].toString(); + reportError("Failed to stop Storage module:" + message); } else { setStatus(Stopped); + debug("Storage module stopped."); } + // Send stop completed signal event on failure + // to not block the shutdown process emit stopCompleted(); })) { qWarning() << "StorageWidget: failed to subscribe to storageStop events"; @@ -110,7 +144,8 @@ LogosResult StorageBackend::init(const QString& configJson) { if (!success) { QString message = data[1].toString(); - debug("Failure during upload progress: " + message); + reportError("Failure during upload progress: " + message); + m_uploadStatus = "Error: " + message; emit uploadStatusChanged(); } else { @@ -150,13 +185,12 @@ LogosResult StorageBackend::init(const QString& configJson) { QString cid = data[2].toString(); debug("Upload completed for session " + sessionId + " with CID " + cid); - // Complète la progress bar m_uploadProgress = 100; m_uploadStatus = "Upload completed!"; emit uploadProgressChanged(); emit uploadStatusChanged(); emit uploadCompleted(cid); - QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &StorageBackend::refreshSpace, Qt::QueuedConnection); QMetaObject::invokeMethod(this, &StorageBackend::downloadManifests, Qt::QueuedConnection); } })) { @@ -168,11 +202,12 @@ LogosResult StorageBackend::init(const QString& configJson) { if (!success) { QString message = data[1].toString(); - debug("Failure during download progress: " + message); + reportError("Failure during download progress: " + message); } else { QString sessionId = data[1].toString(); int len = data[2].toInt(); debug("Downloaded " + QString::number(len) + " bytes for session " + sessionId); + // TODO display progress here } })) { qWarning() << "StorageWidget: failed to subscribe to storageDownloadProgress events"; @@ -183,20 +218,24 @@ LogosResult StorageBackend::init(const QString& configJson) { if (!success) { QString message = data[1].toString(); - debug("Failed to download: " + message); + reportError("Failed to download: " + message); } else { QString sessionId = data[1].toString(); QString cid = data[2].toString(); - emit downloadCompleted(data[2].toString()); - QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection); + // Notify the QML to display something (TBU) on download completed + emit downloadCompleted(cid); + + // Call refreshSpace to fetch the new space used by the node + // and reflect on the widget. + QMetaObject::invokeMethod(this, &StorageBackend::refreshSpace, Qt::QueuedConnection); + debug("Download completed for session " + sessionId + " with CID " + cid); } })) { qWarning() << "StorageWidget: failed to subscribe to storageDownloadProgress events"; } - emit initCompleted(); debug("new config is: " + configJson); return {true, ""}; @@ -235,7 +274,7 @@ LogosResult StorageBackend::start(const QString& newConfigJson) { if (!result) { setStatus(Stopped); - debug("Failed to start storage"); + reportError("Failed to start storage"); return {false, "", "Failed to start storage"}; } @@ -249,7 +288,6 @@ void StorageBackend::stop() { if (m_status == StorageStatus::Stopping) { debug("The Storage Module is already stopping."); - emit stopCompleted(); return; } @@ -266,7 +304,7 @@ void StorageBackend::stop() { if (!result.success) { setStatus(Running); - debug(result.getError()); + reportError("Error when trying to stop: " + result.getError()); return; } @@ -276,37 +314,17 @@ void StorageBackend::stop() { void StorageBackend::destroy() { qDebug() << "StorageBackend: destroy method called"; - StorageStatus status = m_status; auto result = m_logos->storage_module.destroy(); if (!result.success) { - debug(result.getError()); + reportError("Error when trying to destroy: " + result.getError()); return; } qDebug() << "StorageBackend: Storage module destroyed."; } -QString StorageBackend::debugLogs() const { return m_debugLogs; }; - -void StorageBackend::reportError(const QString& message) { - debug(message); - emit error(message); -} - -void StorageBackend::debug(const QString& log) { - if (!m_debugLogs.isEmpty()) { - m_debugLogs += "\n"; - } - - QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate); - m_debugLogs += timestamp + ": " + log; - emit debugLogsChanged(); - - qDebug() << "StorageBackend: " << log; -} - -void StorageBackend::tryDebug() { +void StorageBackend::logDebugInfo() { auto result = m_logos->storage_module.debug(); debug("Peer ID: " + result.getString("id")); @@ -335,12 +353,11 @@ void StorageBackend::tryDebug() { emit peersUpdated(nodes.size()); } -void StorageBackend::tryUploadFile(const QUrl& url) { - qDebug() << "StorageBackend: tryUploadFile called"; +void StorageBackend::uploadFile(const QUrl& url) { + qDebug() << "StorageBackend: uploadFile called"; if (!url.isLocalFile()) { - qWarning() << "Not a local file"; - debug("The provided URL is not a local file."); + reportError("The provided URL is not a local file."); return; } @@ -349,6 +366,7 @@ void StorageBackend::tryUploadFile(const QUrl& url) { m_uploadedBytes = 0; m_uploadTotalBytes = QFileInfo(url.toLocalFile()).size(); m_uploadStatus = "Starting upload..."; + emit uploadProgressChanged(); emit uploadStatusChanged(); @@ -357,34 +375,33 @@ void StorageBackend::tryUploadFile(const QUrl& url) { LogosResult result = m_logos->storage_module.uploadUrl(url); if (!result.success) { - debug(result.getError()); + reportError("Failed to upload file:" + result.getError()); return; } QString sessionId = result.value.value(); - qDebug() << "StorageBackend: tryUploadFile result =" << sessionId; + qDebug() << "StorageBackend: uploadFile result =" << sessionId; } -void StorageBackend::tryDownloadFile(const QString& cid, const QUrl& url) { - qDebug() << "StorageBackend: tryDownloadFile called"; +void StorageBackend::downloadFile(const QString& cid, const QUrl& url) { + qDebug() << "StorageBackend: downloadFile called"; if (!url.isLocalFile()) { - qWarning() << "Not a local file"; - debug("The provided URL is not a local file."); + reportError("The provided URL is not a local file."); return; } LogosResult result = m_logos->storage_module.downloadToUrl(cid, url, false); if (!result.success) { - debug(result.getError()); + reportError("Failed to download file:" + result.getError()); return; } QString sessionId = result.value.value(); - qDebug() << "StorageBackend: tryDownloadFile result =" << sessionId; + qDebug() << "StorageBackend: downloadFile result =" << sessionId; } void StorageBackend::exists(const QString& cid) { @@ -393,7 +410,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.getError()); + reportError("StorageBackend::exists failed with error=" + result.getError()); return; } @@ -406,26 +423,25 @@ void StorageBackend::remove(const QString& cid) { LogosResult result = m_logos->storage_module.exists(cid); if (!result.success) { - debug("StorageBackend::remove failed to check exists: " + result.getError()); - emit error("Failed to check exists " + cid + ": " + result.getError()); + reportError("Failed to check exists: " + result.getError()); return; } if (!result.getBool()) { - debug("StorageBackend::remove blocks don't exist in store."); + debug("Blocks don't exist in store."); return; } result = m_logos->storage_module.remove(cid); if (!result.success) { - debug("StorageBackend::remove failed: " + result.getError()); - emit error("Failed to remove " + cid + ": " + result.getError()); + reportError("Failed to remove " + cid + ": " + result.getError()); return; } debug("Cid " + cid + " removed from local storage."); - QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection); + // Refresh space data for Disk widget + QMetaObject::invokeMethod(this, &StorageBackend::refreshSpace, Qt::QueuedConnection); } void StorageBackend::fetch(const QString& cid) { @@ -434,59 +450,59 @@ void StorageBackend::fetch(const QString& cid) { LogosResult result = m_logos->storage_module.fetch(cid); if (!result.success) { - debug("StorageBackend::fetch failed with error=" + result.getError()); + reportError("Failed to fetch cid " + cid + ": " + result.getError()); return; } debug("Cid " + cid + " fetched."); } -void StorageBackend::version() { +void StorageBackend::logVersion() { qDebug() << "StorageBackend::version called"; LogosResult result = m_logos->storage_module.version(); if (!result.success) { - debug("StorageBackend::version failed with error=" + result.getError()); + reportError("Failed to log version: " + result.getError()); return; } debug("Version: " + result.getString()); } -void StorageBackend::showPeerId() { +void StorageBackend::logPeerId() { qDebug() << "StorageBackend::peerId called"; LogosResult result = m_logos->storage_module.peerId(); if (!result.success) { - debug("StorageBackend::peerId failed with error=" + result.getError()); + reportError("Failed to log peerId: " + result.getError()); return; } debug("Peer ID: " + result.getString()); } -void StorageBackend::spr() { +void StorageBackend::logSpr() { qDebug() << "StorageBackend::spr called"; LogosResult result = m_logos->storage_module.spr(); if (!result.success) { - debug("StorageBackend::spr failed with error=" + result.getError()); + reportError("Failed to log spr: " + result.getError()); return; } debug("SPR: " + result.getString()); } -void StorageBackend::dataDir() { +void StorageBackend::logDataDir() { qDebug() << "StorageBackend::dataDir called"; LogosResult result = m_logos->storage_module.dataDir(); if (!result.success) { - debug("StorageBackend::dataDir failed with error=" + result.getError()); + reportError("Failed to log dataDir: " + result.getError()); return; } @@ -499,7 +515,7 @@ void StorageBackend::downloadManifest(const QString& cid) { LogosResult result = m_logos->storage_module.downloadManifest(cid); if (!result.success) { - debug("StorageBackend::downloadManifest failed with error=" + result.getError()); + reportError("Failed to download manifest cid " + cid + ": " + result.getError()); return; } @@ -509,12 +525,6 @@ void StorageBackend::downloadManifest(const QString& cid) { QString filename = result.getString("filename"); QString mimetype = result.getString("mimetype"); - debug("Manifest tree cid: " + treeCid); - debug(QString("Manifest datasetSize %1").arg(datasetSize)); - debug(QString("Manifest blockSize %1").arg(blockSize)); - debug("Manifest filename: " + filename); - debug("Manifest mimetype: " + mimetype); - QVariantMap manifest; manifest["cid"] = cid; manifest["treeCid"] = treeCid; @@ -523,6 +533,8 @@ void StorageBackend::downloadManifest(const QString& cid) { manifest["datasetSize"] = datasetSize; manifest["blockSize"] = blockSize; + // Refresh the table + downloadManifests(); } void StorageBackend::downloadManifests() { @@ -531,20 +543,20 @@ void StorageBackend::downloadManifests() { LogosResult result = m_logos->storage_module.manifests(); if (!result.success) { - debug("StorageBackend::downloadManifests failed with error=" + result.getError()); + reportError("Failed to download manifests: " + result.getError()); return; } emit manifestsUpdated(result.getList()); } -void StorageBackend::space() { - qDebug() << "StorageBackend::space called"; +void StorageBackend::refreshSpace() { + qDebug() << "StorageBackend::refreshSpace called"; LogosResult result = m_logos->storage_module.space(); if (!result.success) { - debug("Space failed with error=" + result.getError()); + reportError("Failed to refresh space: " + result.getError()); return; } @@ -555,14 +567,6 @@ void StorageBackend::space() { emit spaceUpdated(total, used); } -QString StorageBackend::configJson() const { return QString::fromUtf8(m_config.toJson(QJsonDocument::Indented)); } - -StorageBackend::StorageStatus StorageBackend::status() const { return m_status; } - -int StorageBackend::uploadProgress() const { return m_uploadProgress; } - -QString StorageBackend::uploadStatus() const { return m_uploadStatus; } - void StorageBackend::reloadIfChanged(const QString& configJson) { QJsonDocument config = QJsonDocument::fromJson(configJson.toUtf8()); if (config.isNull()) { @@ -587,7 +591,7 @@ void StorageBackend::reloadIfChanged(const QString& configJson) { LogosResult result = m_logos->storage_module.destroy(); if (!result.success) { - debug("Failed to destroy the context error=" + result.getError()); + reportError("Failed to destroy the context error=" + result.getError()); return; } else { setStatus(StorageStatus::Destroyed); @@ -597,7 +601,7 @@ void StorageBackend::reloadIfChanged(const QString& configJson) { LogosResult result = init(configJson); if (!result.success) { - debug("Failed to init context with new config: " + result.getError()); + reportError("Failed to init context with new config: " + result.getError()); return; } @@ -623,12 +627,12 @@ void StorageBackend::saveUserConfig(const QString& configJson) { file.close(); debug("Config saved to " + USER_CONFIG_PATH); } else { - debug("Failed to save config to " + USER_CONFIG_PATH); + reportError("Failed to save config to " + USER_CONFIG_PATH); } QJsonDocument config = QJsonDocument::fromJson(configJson.toUtf8()); if (config.isNull()) { - qDebug() << "StorageBackend::saveUserConfig invalid json config" << configJson; + reportError("Invalid json config" + configJson); return; } @@ -661,6 +665,7 @@ void StorageBackend::enableUpnpConfig() { reloadIfChanged(QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Indented))); } +// Note that debug is not used because the debug panel is not accessible yet void StorageBackend::enableNatExtConfig(int tcpPort) { qDebug() << "StorageBackend::enableNatExtConfig called with tcpPort" << tcpPort; @@ -671,7 +676,7 @@ void StorageBackend::enableNatExtConfig(int tcpPort) { obj["listen-addrs"] = listenAddrs; // Fetch the public IP asynchronously so we can set nat=extip:IP in the config. - debug("Retrieving public IP..."); + qDebug() << "StorageBackend:: Retrieving public IP..."; QNetworkAccessManager* manager = new QNetworkAccessManager(this); QNetworkRequest request(ECHO_PROVIDER); @@ -686,7 +691,7 @@ void StorageBackend::enableNatExtConfig(int tcpPort) { manager->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - debug("Failed to retrieve public IP: " + reply->errorString() + ". Proceeding without extip NAT."); + qWarning() << "Failed to retrieve public IP: " << reply->errorString() << ". Proceeding without extip NAT."; } else { QString ip = QString::fromUtf8(reply->readAll()).trimmed(); debug("Public IP: " + ip); @@ -698,6 +703,7 @@ void StorageBackend::enableNatExtConfig(int tcpPort) { }); } +// Note that debug is not used because the debug panel is not accessible yet void StorageBackend::checkNodeIsUp() { qDebug() << "StorageBackend::checkNodeIsUp called."; @@ -705,7 +711,7 @@ void StorageBackend::checkNodeIsUp() { // the announceAddresses LogosResult result = m_logos->storage_module.debug(); if (!result.success) { - qDebug() << "Failed to get node debug info: " << result.getError(); + qWarning() << "StorageBackend::checkNodeIsUp Failed to get node debug info: " << result.getError(); emit nodeIsntUp("Failed to get node debug info: " + result.getError()); return; } @@ -715,13 +721,15 @@ void StorageBackend::checkNodeIsUp() { QVariantList nodes = table["nodes"].toList(); debug(QString("Connected peers: %1").arg(nodes.size())); + if (nodes.isEmpty()) { + qWarning() << "StorageBackend::checkNodeIsUp Not peers connected"; emit nodeIsntUp("No peers connected. " "Try modifying the discovery port (default 8090) in the advanced settings."); return; } - debug("DHT seems okay, found peers"); + qDebug() << "StorageBackend::checkNodeIsUp DHT seems okay, found peers"; // Extract IP+port pairs from announceAddresses. // Format: "/ip4/1.2.3.4/tcp/PORT" @@ -733,7 +741,7 @@ void StorageBackend::checkNodeIsUp() { const int tcpIndex = parts.indexOf("tcp"); if (tcpIndex >= 1 && tcpIndex + 1 < parts.size()) { const QString ip = parts[tcpIndex - 1]; - const int port = parts[tcpIndex + 1].toInt(); + const int port = parts[tcpIndex + 1].toInt(); if (port > 0 && !ip.isEmpty()) endpoints.append({ ip, port }); } @@ -742,7 +750,8 @@ void StorageBackend::checkNodeIsUp() { QString nat = m_config.object()["nat"].toString(); if (endpoints.isEmpty()) { - debug("No TCP endpoints found in announce addresses"); + qDebug() << "StorageBackend::checkNodeIsUp No TCP endpoints found in announce addresses"; + if (nat == "upnp") { emit nodeIsntUp("UPnP is configured but there is no announced addresses. " "Try going back and configure port forwarding manually on your router."); @@ -753,7 +762,7 @@ void StorageBackend::checkNodeIsUp() { return; } - debug(QString("Checking reachability for %1 endpoint(s)...").arg(endpoints.size())); + qDebug() << "Checking reachability for " << endpoints.size() << "endpoint(s)..."; bool foundReachable = false; for (const auto& [ip, port] : endpoints) { @@ -767,12 +776,16 @@ void StorageBackend::checkNodeIsUp() { if (reply->error() == QNetworkReply::NoError) { const bool reachable = reply->readAll() == "True"; - debug(QString("%1:%2 is %3").arg(ip).arg(port).arg(reachable ? "reachable" : "not reachable")); + + QString status = reachable ? "reachable" : "not reachable"; + qDebug() << "StorageBackend::checkNodeIsUp " << ip << ":" << port << status; + if (reachable) { foundReachable = true; } } else { - debug(QString("Port check failed for %1:%2 : %3").arg(ip).arg(port).arg(reply->errorString())); + qDebug() << "StorageBackend::checkNodeIsUp Port check failed for" << ip << ":" << port + << reply->errorString(); } reply->deleteLater(); @@ -792,13 +805,11 @@ void StorageBackend::checkNodeIsUp() { } void StorageBackend::fetchWidgetsData() { - QMetaObject::invokeMethod(this, &StorageBackend::tryDebug, Qt::QueuedConnection); - QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &StorageBackend::logDebugInfo, Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &StorageBackend::refreshSpace, Qt::QueuedConnection); QMetaObject::invokeMethod(this, &StorageBackend::downloadManifests, Qt::QueuedConnection); } -void StorageBackend::status(StorageStatus status) { m_status = status; } - void StorageBackend::loadUserConfig() { qDebug() << "StorageBackend::loadUserConfig called."; @@ -808,13 +819,23 @@ void StorageBackend::loadUserConfig() { if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) { result = init(QString::fromUtf8(file.readAll())); } else { - qWarning() << "StorageBackend::loadUserConfig Failed to read the user config file, fallback to default config"; + debug("Failed to read the user config file, fallback to default config"); result = init(QString::fromUtf8(defaultConfig().toJson(QJsonDocument::Indented))); } if (!result.success) { - qWarning() << "StorageBackend::loadUserConfig Failed to load the user config: " + result.getError(); + reportError("Failed to load the user config: " + result.getError()); } else { debug("User config loaded successfully"); } } + +QString StorageBackend::debugLogs() const { return m_debugLogs; }; + +QString StorageBackend::configJson() const { return QString::fromUtf8(m_config.toJson(QJsonDocument::Indented)); } + +StorageBackend::StorageStatus StorageBackend::status() const { return m_status; } + +int StorageBackend::uploadProgress() const { return m_uploadProgress; } + +QString StorageBackend::uploadStatus() const { return m_uploadStatus; } diff --git a/src/StorageBackend.h b/src/StorageBackend.h index 58d4087..acd3356 100644 --- a/src/StorageBackend.h +++ b/src/StorageBackend.h @@ -35,7 +35,7 @@ class StorageBackend : public QObject { Q_OBJECT QML_ELEMENT Q_PROPERTY(QString debugLogs READ debugLogs NOTIFY debugLogsChanged) - Q_PROPERTY(StorageStatus status READ status WRITE status NOTIFY statusChanged) + Q_PROPERTY(StorageStatus status READ status NOTIFY statusChanged) Q_PROPERTY(int uploadProgress READ uploadProgress NOTIFY uploadProgressChanged) Q_PROPERTY(QString uploadStatus READ uploadStatus NOTIFY uploadStatusChanged) public: @@ -61,30 +61,71 @@ class StorageBackend : public QObject { QString uploadStatus() const; Q_INVOKABLE QString configJson() const; + // Provide a default config for onboarding static QJsonDocument defaultConfig(); explicit StorageBackend(LogosAPI* logosAPI = nullptr, QObject* parent = nullptr); ~StorageBackend(); public slots: + // Init the Storage Module using the config json + // passed in parameter. + // It subscribes to events: + // 1- storageStart + // 2- storageStop + // 3- storageUploadProgress + // 4- storageUploadDone + // 5- storageDownloadProgress + // 6- storageDownloadProgress + LogosResult init(const QString& configJson); + + // Start the node + // If a configuration is passed (not empty string), + // the configuration will be reloaded before trying + // to start. LogosResult start(const QString& configJson = ""); + + // Destroy the Storage Module void destroy(); + + // Emit stopCompleted() on completion of it the module is not started void stop(); - void tryDebug(); + + // Log debug info + // Emit peersUpdated(int peers) + void logDebugInfo(); + + // Other log methods for debug + void logDataDir(); + void logVersion(); + void logSpr(); + void logPeerId(); + void exists(const QString& cid); void remove(const QString& cid); + + // Fetch a cid in background void fetch(const QString& cid); - void tryUploadFile(const QUrl& url); - void tryDownloadFile(const QString& cid, const QUrl& url); - void dataDir(); - void version(); - void spr(); - void showPeerId(); + + // Upload a file from the url + // Emit uploadProgressChanged and uploadStatusChanged on storageUploadProgress + // Emit uploadProgressChanged, uploadStatusChanged and uploadCompleted(cid) on storageUploadDone + void uploadFile(const QUrl& url); + + // Upload a file from the url + // Emit downloadCompleted(cid) on storageDownloadDone + void downloadFile(const QString& cid, const QUrl& url); + + // Emit manifestsUpdated void downloadManifest(const QString& cid); + + // Download all the manifests and notify + // Emit manifestsUpdated void downloadManifests(); - void space(); - LogosResult init(const QString& configJson); - void status(StorageStatus status); + + // Call space from the Storage Module + // Emit spaceUpdated to refresh the widget + void refreshSpace(); // Save the user config passed in parameter // into the user config json. @@ -108,8 +149,6 @@ class StorageBackend : public QObject { // // On success, the status will be set to Stopped. // - // Emit initCompleted on success. - // Emit initFailed on failure. void reloadIfChanged(const QString& configJson); // Enables the upnp in the config @@ -118,6 +157,7 @@ class StorageBackend : public QObject { // Enables the net external in the config // and re-create a context with the new configuration + // Emit natExtConfigCompleted void enableNatExtConfig(int tcpPort); // This method will ensure that the node is ready to be used. @@ -134,25 +174,48 @@ class StorageBackend : public QObject { // Emit nodeIsntUp(error) on failure void checkNodeIsUp(); + // Fetch multiple data for the widgets: manifests, debug.. void fetchWidgetsData(); signals: + // Used to start the Storage Module + // if the onboarding is already done void ready(); + + // Used in StartNode component to detect + // success in the onboarding. void startCompleted(); + + // Used in StartNode component to detect + // failure in the onboarding. void startFailed(const QString& error); + + // Refresh the node state indicator void statusChanged(); + + // Refresh the debug logs panel. void debugLogsChanged(); + + // Used in the shutdown process void stopCompleted(); + + // Used to refresh the disk widgets + void spaceUpdated(qlonglong total, qlonglong used); + void uploadProgressChanged(); void uploadStatusChanged(); + + // Used to refresh the Manifests table void manifestsUpdated(const QVariantList& manifests); - void quotaChanged(); - void initCompleted(); + + // Used in the on boarding to detect success void natExtConfigCompleted(); + void uploadCompleted(const QString& cid); void downloadCompleted(const QString& cid); + + // Display a toast message on error void error(const QString& message); - void spaceUpdated(qlonglong total, qlonglong used); // Emitted when the node port is reachable from the internet void nodeIsUp(); @@ -166,14 +229,29 @@ class StorageBackend : public QObject { private slots: private: + // Update the status + // Emit statusUpdated if the status was different from the previous status void setStatus(StorageStatus newStatus); - void peerConnect(const QString& peerId); - void debug(const QString& log); + + // Display debug (or message) in the terminal and + // add it to the debugLogs to make it accessible + // from the debug panel. + // Default level is debug, can be "warning" to display warning + // messages. + void debug(const QString& log, const QString& level = "debug"); + + // Display log and add it to debugLogs + // Emit error(message) void reportError(const QString& message); + // Logos related variables LogosAPI* m_logosAPI; LogosModules* m_logos; + + // Status of the Storage Module StorageStatus m_status; + + // List of debug logs displayed to the application. QString m_debugLogs; // TODO: double check if we need all of this parameters @@ -184,5 +262,7 @@ class StorageBackend : public QObject { qint64 m_uploadTotalBytes = 0; qint64 m_uploadedBytes = 0; + // Internal configuration object. It can be updated by + // upnp or port forwarning methods. QJsonDocument m_config; }; diff --git a/src/StorageUIPlugin.cpp b/src/StorageUIPlugin.cpp index 0513c92..22fecbe 100644 --- a/src/StorageUIPlugin.cpp +++ b/src/StorageUIPlugin.cpp @@ -26,19 +26,6 @@ QWidget* StorageUIPlugin::createWidget(LogosAPI* logosAPI) { qDebug() << "StorageUIPlugin: Loading settings..."; - // Default constructor uses QCoreApplication org/domain/app — same path as QML QtCore.Settings - // QSettings settings; - // int discoveryPort = settings.value("Storage/discoveryPort", 8090).toInt(); - // int tcpPort = settings.value("Storage/tcpPort", 0).toInt(); - // QString dataDir = settings.value("Storage/dataDir", "").toString(); - // bool onboardingCompleted = settings.value("Storage/onboardingCompleted", false).toBool(); - - // qDebug() << "StorageUIPlugin: Settings file:" << settings.fileName(); - // qDebug() << "StorageUIPlugin: onboardingCompleted=" << onboardingCompleted; - // qDebug() << "StorageUIPlugin: dataDir=" << dataDir; - // qDebug() << "StorageUIPlugin: discoveryPort=" << discoveryPort; - // qDebug() << "StorageUIPlugin: tcpPort=" << tcpPort; - // Always load Main.qml — QML handles navigation (onboarding vs startNode) StorageBackend* backend = new StorageBackend(logosAPI, quickWidget); @@ -54,18 +41,10 @@ QWidget* StorageUIPlugin::createWidget(LogosAPI* logosAPI) { root->setProperty("backend", QVariant::fromValue(static_cast(backend))); + // Here we emit an event ready because + // if the onboarding is already done, + // the backend can be init using onReady subscription backend->ready(); - // Storage init is done in the QML - // Build config from settings if onboarding was done, otherwise use empty config - // QString configJson = StorageBackend::getUserConfig(); - // qDebug() << "UserConfig" << StorageBackend::getUserConfigPath(); - // qDebug() << "configJson" << configJson; - - // LogosResult result = backend->init(configJson); - - // if (!result.success) { - // qWarning() << "StorageUIPlugin: Failed to init backend:" << result.getError(); - // } return quickWidget; } diff --git a/src/qml/DebugPanel.qml b/src/qml/DebugPanel.qml index 7e32878..2b3a1b6 100644 --- a/src/qml/DebugPanel.qml +++ b/src/qml/DebugPanel.qml @@ -28,27 +28,27 @@ Rectangle { LogosStorageButton { text: "Debug" enabled: root.running - onClicked: root.backend.tryDebug() + onClicked: root.backend.logDebugInfo() } LogosStorageButton { text: "Peer ID" enabled: root.running - onClicked: root.backend.showPeerId() + onClicked: root.backend.logPeerId() } LogosStorageButton { text: "Data dir" enabled: root.running - onClicked: root.backend.dataDir() + onClicked: root.backend.logDataDir() } LogosStorageButton { text: "SPR" enabled: root.running - onClicked: root.backend.spr() + onClicked: root.backend.logSpr() } LogosStorageButton { text: "Version" enabled: root.running - onClicked: root.backend.version() + onClicked: root.backend.logVersion() } Item { @@ -83,7 +83,8 @@ Rectangle { bottomPadding: 20 onTextChanged: Qt.callLater(function () { - logFlick.contentY = Math.max(0, logFlick.contentHeight - logFlick.height) + logFlick.contentY = Math.max( + 0, logFlick.contentHeight - logFlick.height) }) } } diff --git a/src/qml/DiskWidget.qml b/src/qml/DiskWidget.qml index 5bb5446..1d561f6 100644 --- a/src/qml/DiskWidget.qml +++ b/src/qml/DiskWidget.qml @@ -59,7 +59,8 @@ ArcWidget { } LogosText { - text: root.total > 0 ? "/ " + root.formatBytes(root.total) : "STORAGE" + text: root.total > 0 ? "/ " + root.formatBytes( + root.total) : "STORAGE" font.pixelSize: 9 color: Theme.palette.textTertiary font.letterSpacing: 1.3 @@ -72,7 +73,6 @@ ArcWidget { signal spaceUpdated(double total, double used) signal uploadCompleted - signal downloadCompleted function space() { return { diff --git a/src/qml/Main.qml b/src/qml/Main.qml index dabbf65..5d725ca 100644 --- a/src/qml/Main.qml +++ b/src/qml/Main.qml @@ -167,8 +167,6 @@ Item { signal startCompleted signal startFailed - signal stopCompleted - signal initCompleted signal ready signal error signal natExtConfigCompleted diff --git a/src/qml/ManifestTable.qml b/src/qml/ManifestTable.qml index 61c67ac..ee8d01b 100644 --- a/src/qml/ManifestTable.qml +++ b/src/qml/ManifestTable.qml @@ -24,7 +24,7 @@ ColumnLayout { fileMode: FileDialog.SaveFile onAccepted: { if (pendingManifest) { - root.backend.tryDownloadFile(pendingManifest.cid, selectedFile) + root.backend.downloadFile(pendingManifest.cid, selectedFile) pendingManifest = null } } diff --git a/src/qml/StatusWidgets.qml b/src/qml/StatusWidgets.qml index 604be4a..d863718 100644 --- a/src/qml/StatusWidgets.qml +++ b/src/qml/StatusWidgets.qml @@ -11,6 +11,7 @@ ColumnLayout { property var backend property bool running: false + property string _lastUploadedCid: "" spacing: 0 @@ -26,16 +27,17 @@ ColumnLayout { FileDialog { id: uploadDialog - onAccepted: root.backend.tryUploadFile(selectedFile) + onAccepted: root.backend.uploadFile(selectedFile) } Connections { target: root.backend - function onUploadCompleted(cid) { root._lastCid = cid } - function onDownloadCompleted(cid) { root._lastCid = cid } - } + function onUploadCompleted(cid) { + root._lastUploadedCid = cid + } - property string _lastCid: "" + function onDownloadCompleted() {} + } RowLayout { Layout.fillWidth: true @@ -66,7 +68,7 @@ ColumnLayout { Layout.bottomMargin: 10 Layout.preferredHeight: 36 - opacity: root._lastCid.length > 0 ? 1.0 : 0.0 + opacity: root._lastUploadedCid.length > 0 ? 1.0 : 0.0 Behavior on opacity { NumberAnimation { @@ -95,7 +97,7 @@ ColumnLayout { LogosText { text: { - var c = root._lastCid + var c = root._lastUploadedCid return c.length > 20 ? c.substring(0, 8) + "…" + c.slice(-6) : c } font.pixelSize: 11 @@ -139,14 +141,15 @@ ColumnLayout { Rectangle { anchors.fill: parent radius: parent.radius - color: cidBadgeHover.hovered ? Qt.rgba(1, 1, 1, 0.04) : "transparent" + color: cidBadgeHover.hovered ? Qt.rgba(1, 1, 1, + 0.04) : "transparent" } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { - clipHelper.copyText(root._lastCid) + clipHelper.copyText(root._lastUploadedCid) copyFlashAnim.restart() } } diff --git a/src/qml/StorageView.qml b/src/qml/StorageView.qml index 45ef6a1..bb24140 100644 --- a/src/qml/StorageView.qml +++ b/src/qml/StorageView.qml @@ -48,15 +48,15 @@ LogosStorageLayout { status = 0 } function checkNodeIsUp() {} - function tryUploadFile(f) {} + function uploadFile(f) {} function downloadManifest(c) {} function remove(c) {} - function tryDownloadFile(c, d) {} - function tryDebug() {} - function showPeerId() {} - function dataDir() {} - function spr() {} - function version() {} + function downloadFile(c, d) {} + function logDebugInfo() {} + function logPeerId() {} + function logDataDir() {} + function logSpr() {} + function logVersion() {} function saveUserConfig(j) {} function reloadIfChanged(j) {} function configJson() {