Provide an error modal to display error

This commit is contained in:
Arnaud 2026-02-19 17:44:49 +04:00
parent 8c6bd2f4ef
commit 733a43a4f8
No known key found for this signature in database
GPG Key ID: 20E40A5D3110766F
9 changed files with 154 additions and 60 deletions

View File

@ -47,7 +47,7 @@ LogosResult StorageBackend::init(const QString& configJson) {
m_config = QJsonDocument::fromJson(configJson.toUtf8());
if (m_config.isNull()) {
qDebug() << "StorageBackend::initStorage invalid json config" << configJson;
emit initFailed();
reportError("Failed to create the storage: invalid JSON config");
return {false, "", "Failed to create the storage, invalid json config"};
}
@ -57,9 +57,8 @@ LogosResult StorageBackend::init(const QString& configJson) {
if (!result) {
setStatus(Destroyed);
debug("Failed to init storage");
emit initFailed();
return {false, "", "Filed to init storage"};
reportError("Failed to init storage");
return {false, "", "Failed to init storage"};
}
setStatus(Stopped);
@ -72,6 +71,7 @@ LogosResult StorageBackend::init(const QString& configJson) {
setStatus(Stopped);
debug("Failed to start Storage module:" + message);
emit startFailed(message);
reportError("Failed to start: " + message);
} else {
setStatus(Running);
debug("Storage module started.");
@ -237,6 +237,8 @@ LogosResult StorageBackend::start(const QString& newConfigJson) {
setStatus(Starting);
debug("Starting Storage module...");
// TODO trach the start attempts in a file
auto result = m_logos->storage_module.start();
if (!result) {
@ -295,6 +297,11 @@ void StorageBackend::destroy() {
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";
@ -866,36 +873,38 @@ void StorageBackend::enableNatExtConfig(int tcpPort) {
QJsonArray listenAddrs = {QString("/ip4/0.0.0.0/tcp/%1").arg(tcpPort)};
obj["listen-addrs"] = listenAddrs;
reloadIfChanged(QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)));
qDebug() << "StorageBackend::enableNatExtConfig Retrieving the public IP";
emit natExtConfigCompleted();
// Create the network manager
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
QNetworkRequest request(QUrl("https://echo.codex.storage/"));
request.setRawHeader("Accept", "text/plain");
QNetworkReply* reply = manager->get(request);
// QNetworkAccessManager* manager = new QNetworkAccessManager(this);
// QNetworkRequest request(QUrl("https://echo.codex.storage/"));
// request.setRawHeader("Accept", "text/plain");
// QNetworkReply* reply = manager->get(request);
connect(reply, &QNetworkReply::finished, this, [this, reply, manager, obj]() {
reply->deleteLater();
manager->deleteLater();
// connect(reply, &QNetworkReply::finished, this, [this, reply, manager, obj]() {
// reply->deleteLater();
// manager->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
emit natExtConfigFailed(reply->errorString());
return;
}
// if (reply->error() != QNetworkReply::NoError) {
// reportError("NAT config failed: " + reply->errorString());
// return;
// }
QString ip = reply->readAll().trimmed();
// QString ip = reply->readAll().trimmed();
qDebug() << "StorageBackend::enableNatExtConfig ip=" << ip;
// qDebug() << "StorageBackend::enableNatExtConfig ip=" << ip;
obj["nat"] = "extip:" + ip;
// obj["nat"] = "extip:" + ip;
qDebug() << "StorageBackend::enableNatExtConfig config="
<< QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
// qDebug() << "StorageBackend::enableNatExtConfig config="
// << QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
reloadIfChanged(QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)));
// reloadIfChanged(QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)));
emit natExtConfigCompleted();
});
// emit natExtConfigCompleted();
// });
}
void StorageBackend::status(StorageStatus status) { m_status = status; }

View File

@ -133,6 +133,25 @@ class StorageBackend : public QObject {
// and re-create a context with the new configuration
void enableNatExtConfig(int tcpPort);
// This method try to get guidance about the resolution
// of the misconfiguration for the node.
// The idea is to check if:
//
// 1- upnp is enabled. In this case, the user should go back
// and try to configure the port forwarding
// 2- port forwarning is enabled. Indicate that the port has to
// be free and open to remote connection.
void guessResolution();
// This method will ensure that the node is ready to be used.
// 1- Make a call to debug function in the storage module and
// make sure that the node has peer. If not, the UI should suggest
// to modifiy the discovery port (8090) in the advance settings (to come).
// 2- Ensure that the tcp port is open to remote connection. If not,
// the UI should suggest to change go back and try another port and double
// check that the port forwarding is enabled on the router.
void checkNodeIsUp();
signals:
void ready();
void startCompleted();
@ -146,9 +165,8 @@ class StorageBackend : public QObject {
void manifestsChanged();
void quotaChanged();
void initCompleted();
void initFailed();
void natExtConfigFailed(const QString& error);
void natExtConfigCompleted();
void error(const QString& message);
private slots:
@ -156,6 +174,7 @@ class StorageBackend : public QObject {
void setStatus(StorageStatus newStatus);
void peerConnect(const QString& peerId);
void debug(const QString& log);
void reportError(const QString& message);
LogosAPI* m_logosAPI;
LogosModules* m_logos;

View File

@ -102,13 +102,13 @@ void StorageUIPlugin::destroyWidget(QWidget* widget) {
return;
}
if (backend->status() != StorageBackend::StorageStatus::Destroyed) {
qDebug() << "StorageUIPlugin::destroyWidget: backend is not initialised so let's detroy it.";
if (backend->status() == StorageBackend::StorageStatus::Destroyed) {
qDebug() << "StorageUIPlugin::destroyWidget: backend is not initialised so let's delete the widget.";
quickWidget->deleteLater();
return;
}
if (backend->status() == StorageBackend::StorageStatus::Running) {
if (backend->status() != StorageBackend::StorageStatus::Running) {
qDebug() << "StorageUIPlugin::destroyWidget: backend is not running so let's detroy it.";
backend->destroy();

View File

@ -134,6 +134,7 @@ qt_add_qml_module(appqml
LogosStorageButton.qml
LogosStorageLayout.qml
PortForwarding.qml
ErrorToast.qml
)
# Set up QML module directory for runtime

View File

@ -9,26 +9,53 @@ Rectangle {
property alias title: titleText.text
property alias message: messageText.text
function show(t, msg) {
root.title = t
root.message = msg
function show(title, message) {
root.title = title
root.message = message
root.visible = true
slideAnim.restart()
}
function hide() {
root.visible = false
hideAnim.restart()
}
visible: false
opacity: 0
width: 500
radius: Theme.spacing.tiny
color: "#1e1e1e"
color: "#3D2020"
implicitHeight: content.implicitHeight + Theme.spacing.medium * 2
transform: Translate { id: slideTranslate; y: 20 }
transform: Translate {
id: slideTranslate
y: 20
}
ParallelAnimation {
id: hideAnim
NumberAnimation {
target: slideTranslate
property: "y"
from: 0
to: 20
duration: 300
easing.type: Easing.InCubic
}
NumberAnimation {
target: root
property: "opacity"
from: 1
to: 0
duration: 300
easing.type: Easing.InCubic
}
onFinished: root.visible = false
}
ParallelAnimation {
id: slideAnim
@ -52,6 +79,23 @@ Rectangle {
}
}
// Close button top right
LogosText {
text: "x"
font.pixelSize: Theme.typography.primaryText
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: Theme.spacing.small
z: 1
color: Theme.palette.text
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.hide()
}
}
ColumnLayout {
id: content
anchors {
@ -62,28 +106,12 @@ Rectangle {
}
spacing: Theme.spacing.tiny
RowLayout {
LogosText {
id: titleText
Layout.fillWidth: true
LogosText {
id: titleText
Layout.fillWidth: true
color: Theme.palette.error
font.pixelSize: Theme.typography.primaryText
font.bold: true
}
LogosText {
text: "✕"
color: Theme.palette.textMuted
font.pixelSize: Theme.typography.primaryText
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.hide()
}
}
color: Theme.palette.error
font.pixelSize: Theme.typography.primaryText
font.bold: true
}
LogosText {

View File

@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtCore
import Logos.Theme
// qmllint disable unqualified
Item {
@ -20,6 +21,9 @@ Item {
signal startFailed
signal stopCompleted
signal initCompleted
signal ready
signal error
signal natExtConfigCompleted
function start() {
console.log("mock start called")
@ -38,6 +42,8 @@ Item {
function saveCurrentConfig() {}
function stop() {}
function guessResolution() {}
}
Settings {
@ -61,13 +67,23 @@ Item {
initialItem: onboardingComponent
}
ErrorToast {
id: errorToast
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.spacing.medium
}
Component {
id: onboardingComponent
OnBoarding {
onCompleted: function (upnpEnabled) {
console.info("onboarding completed")
if (upnpEnabled) {
root.backend.enableUpnpConfig()
root.backend.start()
stackView.push(startNodeComponent)
} else {
stackView.push(portForwardingComponent)
}
@ -122,16 +138,16 @@ Item {
function onInitCompleted() {}
function onReady() {
console.info("i am ready")
if (settings.onboardingCompleted) {
console.info("onboardingCompleted completed")
root.backend.loadUserConfig()
root.backend.start()
stackView.replace(storageComponent, StackView.Immediate)
}
}
function onNatExtConfigFailed(error) {}
function onError(message) {
errorToast.show("Error", message)
}
function onNatExtConfigCompleted(error) {
root.backend.start()

View File

@ -10,6 +10,7 @@ LogosStorageLayout {
property var backend: mockBackend
property string status: ""
property string title: "Starting your node...."
property string resolution: ""
property bool starting: true
property bool success: false
@ -56,6 +57,8 @@ LogosStorageLayout {
root.title = "Error"
root.status = "Failed to start: " + error
}
function guessResolution() {}
}
ColumnLayout {
@ -76,6 +79,13 @@ LogosStorageLayout {
text: root.status
Layout.alignment: Qt.AlignHCenter
}
LogosText {
id: suggestionText
font.pixelSize: Theme.typography.primaryText
text: root.suggestion
Layout.alignment: Qt.AlignHCenter
}
}
LogosStorageButton {
@ -97,4 +107,14 @@ LogosStorageLayout {
onClicked: root.next()
enabled: root.success == true
}
Connections {
target: root.backend
function onStartFailed(error) {
root.title = "Erreur"
root.status = "Your node failed to start with this error: " + error
root.method = root.backend.guessResolution()
}
}
}

View File

@ -19,7 +19,7 @@ int main(int argc, char* argv[]) {
Qt::QueuedConnection);
engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
engine.loadFromModule("StorageBackend", "OnBoarding");
engine.loadFromModule("StorageBackend", "Main");
return app.exec();
}

View File

@ -8,5 +8,6 @@
<file alias="LogosStorageButton.qml">qml/LogosStorageButton.qml</file>
<file alias="LogosStorageLayout.qml">qml/LogosStorageLayout.qml</file>
<file alias="PortForwarding.qml">qml/PortForwarding.qml</file>
<file alias="ErrorToast.qml">qml/ErrorToast.qml</file>
</qresource>
</RCC>