fix(apps/amm): ensure changing endpoint works

This commit is contained in:
r4bbit 2026-06-26 13:00:02 +02:00
parent fe4c7a96da
commit 70a5a2e136
No known key found for this signature in database
GPG Key ID: E95F1E9447DC91A9
4 changed files with 120 additions and 19 deletions

View File

@ -45,7 +45,7 @@ Item {
height: show ? 32 : 0 height: show ? 32 : 0
visible: height > 0 visible: height > 0
clip: true clip: true
color: Theme.palette.warning color: Theme.palette.error
Behavior on height { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } } Behavior on height { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } }
@ -56,7 +56,7 @@ Item {
elide: Text.ElideMiddle elide: Text.ElideMiddle
font.pixelSize: 12 font.pixelSize: 12
font.weight: Font.Medium font.weight: Font.Medium
color: Theme.palette.background color: Theme.palette.text
text: qsTr("Unable to connect to network") text: qsTr("Unable to connect to network")
} }
} }

View File

@ -428,14 +428,29 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
height: 40 height: 40
text: qsTr("Save") text: qsTr("Save")
onClicked: { // The new endpoint only goes live after an app restart (the
// module can't re-open an already-open wallet), so confirm
// the user understands that before persisting.
onClicked: restartDialog.open()
}
// Persist the change; the running wallet keeps the old endpoint
// until the user restarts (we can't reliably quit this host).
RestartRequiredDialog {
id: restartDialog
title: qsTr("Restart to apply")
confirmLabel: qsTr("Save")
message: qsTr("Changing the network endpoint only takes effect after restarting the app. "
+ "Save now, then quit and reopen the app to apply it.")
onConfirmed: {
if (!root.backend) return if (!root.backend) return
seqStatus.text = "" seqStatus.text = ""
logos.watch(root.backend.changeSequencerAddr(seqField.text), logos.watch(root.backend.changeSequencerAddr(seqField.text),
function(ok) { function(ok) {
seqStatus.ok = ok seqStatus.ok = ok
seqStatus.text = ok ? qsTr("Network updated.") seqStatus.text = ok
: qsTr("Failed to update network.") ? qsTr("Saved — quit and reopen the app to apply.")
: qsTr("Invalid network URL.")
}, },
function(error) { function(error) {
seqStatus.ok = false seqStatus.ok = false

View File

@ -0,0 +1,73 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Logos.Theme
import Logos.Controls
// Modal shown before applying a setting that only takes effect after a restart.
// The caller handles `confirmed` (persist the change, then close the app).
Popup {
id: root
property string title: qsTr("Restart required")
property string message: ""
property string confirmLabel: qsTr("Save & close")
signal confirmed()
modal: true
dim: true
padding: Theme.spacing.large
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
// Center on the full-window overlay rather than the small control this is
// declared inside.
parent: Overlay.overlay
anchors.centerIn: parent
width: 360
background: Rectangle {
color: Theme.palette.backgroundSecondary
radius: Theme.spacing.radiusXlarge
border.color: Theme.palette.backgroundElevated
}
contentItem: ColumnLayout {
width: root.availableWidth
spacing: Theme.spacing.large
LogosText {
text: root.title
font.pixelSize: Theme.typography.titleText
font.weight: Theme.typography.weightBold
color: Theme.palette.text
}
LogosText {
Layout.fillWidth: true
text: root.message
wrapMode: Text.WordWrap
font.pixelSize: Theme.typography.secondaryText
color: Theme.palette.textSecondary
}
RowLayout {
Layout.topMargin: Theme.spacing.medium
Layout.fillWidth: true
spacing: Theme.spacing.medium
LogosButton {
text: qsTr("Cancel")
Layout.fillWidth: true
onClicked: root.close()
}
LogosButton {
text: root.confirmLabel
Layout.fillWidth: true
onClicked: {
root.confirmed()
root.close()
}
}
}
}
}

View File

@ -354,12 +354,28 @@ void AmmUiBackend::persistStoragePath(const QString& path)
bool AmmUiBackend::changeSequencerAddr(QString url) bool AmmUiBackend::changeSequencerAddr(QString url)
{ {
const QString trimmed = url.trimmed(); QString normalized = url.trimmed();
if (trimmed.isEmpty()) { if (normalized.isEmpty()) {
qWarning() << "AmmUiBackend: refusing to set empty sequencer_addr"; qWarning() << "AmmUiBackend: refusing to set empty sequencer_addr";
return false; return false;
} }
// The wallet config parses sequencer_addr as a strict URL — a missing
// scheme makes the whole config fail to deserialize (and would leave the
// wallet unopenable). Default to http:// so users can type just host:port,
// then validate before writing anything.
if (!normalized.contains(QStringLiteral("://")))
normalized.prepend(QStringLiteral("http://"));
const QUrl parsed(normalized, QUrl::StrictMode);
if (!parsed.isValid() || parsed.host().isEmpty()
|| (parsed.scheme() != QStringLiteral("http")
&& parsed.scheme() != QStringLiteral("https"))) {
qWarning() << "AmmUiBackend: invalid sequencer URL" << url;
return false;
}
normalized = parsed.toString();
const QString cfg = configPath().isEmpty() ? defaultConfigPath() : configPath(); const QString cfg = configPath().isEmpty() ? defaultConfigPath() : configPath();
// Preserve the other config fields (poll timeouts, retries) — only swap the // Preserve the other config fields (poll timeouts, retries) — only swap the
@ -370,7 +386,7 @@ bool AmmUiBackend::changeSequencerAddr(QString url)
obj = QJsonDocument::fromJson(in.readAll()).object(); obj = QJsonDocument::fromJson(in.readAll()).object();
in.close(); in.close();
} }
obj.insert(QStringLiteral("sequencer_addr"), trimmed); obj.insert(QStringLiteral("sequencer_addr"), normalized);
QFile out(cfg); QFile out(cfg);
if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) { if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
@ -380,17 +396,14 @@ bool AmmUiBackend::changeSequencerAddr(QString url)
out.write(QJsonDocument(obj).toJson(QJsonDocument::Indented)); out.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
out.close(); out.close();
// Re-open so the live wallet uses the new endpoint right away. // Config is now the source of truth — reflect the change in the UI.
if (isWalletOpen()) { if (sequencerAddr() != normalized)
const QString stg = storagePath().isEmpty() ? defaultStoragePath() : storagePath(); setSequencerAddr(normalized);
const int err = m_logos->logos_execution_zone.open(cfg, stg); checkReachability();
if (err != WALLET_FFI_SUCCESS) {
qWarning() << "AmmUiBackend: reopen after sequencer change failed, code" << err; // The module can't re-open an already-open wallet, so the new endpoint only
return false; // takes effect on the next launch. The UI confirms a restart before calling
} // this and closes the app afterwards.
refreshSequencerAddr();
refreshAccounts();
}
return true; return true;
} }