mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-03 05:29:50 +00:00
Merge 00e595a3445e59fef4b80b191d49dd038ba62222 into fe4c7a96da393808946d0ffdb9ef44a5da9d8ef0
This commit is contained in:
commit
f853b1aca1
@ -27,10 +27,12 @@ Account/keystore sharing follows the runtime:
|
||||
startup the backend **adopts** the already-open wallet (see
|
||||
`openOrAdoptWallet()`), surfacing **shared** accounts across apps.
|
||||
|
||||
> Follow-up: the wallet FFI requires explicit `config_path`/`storage_path` even
|
||||
> though the wallet crate already defines defaults (`~/.lee/wallet`,
|
||||
> `from_path_or_initialize_default`). A `wallet_ffi_create_new_default()` /
|
||||
> `_open_default()` upstream would let the app drop its path handling entirely.
|
||||
> Follow-up: the app reconstructs the wallet paths itself because the
|
||||
> `logos_execution_zone` module only exposes path-taking `create_new`/`open`.
|
||||
> LEZ's wallet FFI now provides path-free variants (`wallet_ffi_create_new_default`,
|
||||
> `wallet_ffi_open_default`, plus `wallet_ffi_default_config_path` /
|
||||
> `_storage_path` / `wallet_ffi_wallet_exists_default`). Once the module surfaces
|
||||
> those over QtRO, the app can drop its `defaultWalletHome/Config/Storage` logic.
|
||||
|
||||
## Setup
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ Item {
|
||||
height: show ? 32 : 0
|
||||
visible: height > 0
|
||||
clip: true
|
||||
color: Theme.palette.warning
|
||||
color: Theme.palette.error
|
||||
|
||||
Behavior on height { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } }
|
||||
|
||||
@ -56,7 +56,7 @@ Item {
|
||||
elide: Text.ElideMiddle
|
||||
font.pixelSize: 12
|
||||
font.weight: Font.Medium
|
||||
color: Theme.palette.background
|
||||
color: Theme.palette.text
|
||||
text: qsTr("Unable to connect to network")
|
||||
}
|
||||
}
|
||||
@ -89,5 +89,10 @@ Item {
|
||||
anchors.fill: parent
|
||||
visible: navbar.currentIndex === 1
|
||||
}
|
||||
|
||||
CreatePoolPage {
|
||||
anchors.fill: parent
|
||||
visible: navbar.currentIndex === 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ Item {
|
||||
id: root
|
||||
|
||||
property int currentIndex: 0
|
||||
readonly property var tabs: ["Trade", "Liquidity"]
|
||||
readonly property var tabs: ["Trade", "Liquidity", "Create Pool"]
|
||||
|
||||
// Wallet wiring, passed down from Main.qml.
|
||||
property var backend: null
|
||||
|
||||
116
apps/amm/qml/components/pool/PoolStepRail.qml
Normal file
116
apps/amm/qml/components/pool/PoolStepRail.qml
Normal file
@ -0,0 +1,116 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
// Vertical progress rail for the pool-creation flow (Uniswap-style): numbered
|
||||
// steps connected by a line, with the active step highlighted and completed
|
||||
// steps marked done. Read currentStep to drive which step is active. Clicking an
|
||||
// already-reached step (index <= currentStep) emits stepClicked so the page can
|
||||
// navigate back to it.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property int currentStep: 0
|
||||
readonly property var steps: [
|
||||
{ title: qsTr("Select token pair"), subtitle: qsTr("Pick the two tokens for the pool.") },
|
||||
{ title: qsTr("Deposit amounts"), subtitle: qsTr("Set the initial liquidity.") }
|
||||
]
|
||||
|
||||
signal stepClicked(int index)
|
||||
|
||||
implicitWidth: 240
|
||||
implicitHeight: column.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: root.steps
|
||||
|
||||
delegate: Item {
|
||||
id: stepItem
|
||||
|
||||
readonly property bool active: index === root.currentStep
|
||||
readonly property bool done: index < root.currentStep
|
||||
readonly property bool last: index === root.steps.length - 1
|
||||
// Only steps already reached can be clicked (no jumping ahead).
|
||||
readonly property bool reachable: index <= root.currentStep
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: stepRow.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: stepRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: Theme.spacing.medium
|
||||
|
||||
// Indicator: numbered dot + connector line down to the next dot.
|
||||
Item {
|
||||
Layout.preferredWidth: 28
|
||||
Layout.fillHeight: true
|
||||
|
||||
Rectangle {
|
||||
id: dot
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: (stepItem.active || stepItem.done) ? Theme.palette.primary : Theme.palette.backgroundSecondary
|
||||
border.width: 1
|
||||
border.color: (stepItem.active || stepItem.done) ? Theme.palette.primary : Theme.palette.border
|
||||
|
||||
LogosText {
|
||||
anchors.centerIn: parent
|
||||
text: stepItem.done ? "✓" : (index + 1)
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
color: (stepItem.active || stepItem.done) ? Theme.palette.background : Theme.palette.textSecondary
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
visible: !stepItem.last
|
||||
width: 2
|
||||
anchors.top: dot.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: dot.horizontalCenter
|
||||
color: stepItem.done ? Theme.palette.primary : Theme.palette.border
|
||||
}
|
||||
}
|
||||
|
||||
// Step text.
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: stepItem.last ? 0 : Theme.spacing.xlarge
|
||||
spacing: 2
|
||||
|
||||
LogosText {
|
||||
text: modelData.title
|
||||
font.pixelSize: Theme.typography.primaryText
|
||||
font.bold: stepItem.active
|
||||
color: (stepItem.active || stepItem.done) ? Theme.palette.text : Theme.palette.textSecondary
|
||||
}
|
||||
LogosText {
|
||||
Layout.fillWidth: true
|
||||
text: modelData.subtitle
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: stepItem.reachable
|
||||
cursorShape: stepItem.reachable ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: root.stepClicked(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -428,14 +428,29 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
height: 40
|
||||
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
|
||||
seqStatus.text = ""
|
||||
logos.watch(root.backend.changeSequencerAddr(seqField.text),
|
||||
function(ok) {
|
||||
seqStatus.ok = ok
|
||||
seqStatus.text = ok ? qsTr("Network updated.")
|
||||
: qsTr("Failed to update network.")
|
||||
seqStatus.text = ok
|
||||
? qsTr("Saved — quit and reopen the app to apply.")
|
||||
: qsTr("Invalid network URL.")
|
||||
},
|
||||
function(error) {
|
||||
seqStatus.ok = false
|
||||
|
||||
73
apps/amm/qml/components/wallet/RestartRequiredDialog.qml
Normal file
73
apps/amm/qml/components/wallet/RestartRequiredDialog.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
270
apps/amm/qml/pages/CreatePoolPage.qml
Normal file
270
apps/amm/qml/pages/CreatePoolPage.qml
Normal file
@ -0,0 +1,270 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
import "../components/pool"
|
||||
|
||||
// Two-column pool-creation flow (Uniswap-style): a vertical step rail on the
|
||||
// left and the active step's panel on the right. Step 1 selects the token pair;
|
||||
// step 2 enters deposit amounts. Both panels stay alive so the entered values
|
||||
// persist when navigating between steps via the rail.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property int pageMargin: 24
|
||||
// Breathing room below the navbar. The Trade page fully centers its card
|
||||
// (gap = leftover space / 2); this uses a quarter of the leftover so it sits
|
||||
// roughly half as far down, scaling with the window, with a sensible floor.
|
||||
readonly property int topMargin: Math.max(48, Math.round((scroll.height - content.implicitHeight) / 4))
|
||||
readonly property int contentWidth: 760
|
||||
|
||||
// 0x123456…cdef style truncation for showing token addresses compactly.
|
||||
function truncated(addr) {
|
||||
const a = (addr || "").trim()
|
||||
return a.length > 13 ? (a.substring(0, 6) + "…" + a.substring(a.length - 4)) : a
|
||||
}
|
||||
|
||||
// Numbers only (digits + a single decimal point) for the deposit amounts.
|
||||
RegularExpressionValidator {
|
||||
id: amountValidator
|
||||
regularExpression: /^[0-9]*\.?[0-9]*$/
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Theme.palette.background
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: scroll
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
contentWidth: width
|
||||
contentHeight: Math.max(height, content.implicitHeight + root.topMargin + root.pageMargin)
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
x: Math.max(root.pageMargin, (scroll.width - width) / 2)
|
||||
y: root.topMargin
|
||||
width: Math.min(scroll.width - root.pageMargin * 2, root.contentWidth)
|
||||
spacing: Theme.spacing.xxlarge
|
||||
|
||||
PoolStepRail {
|
||||
id: rail
|
||||
currentStep: 0
|
||||
Layout.preferredWidth: 240
|
||||
Layout.alignment: Qt.AlignTop
|
||||
// Jump back to an already-reached step (e.g. step 1 to re-pick
|
||||
// tokens). Selections persist because both panels stay alive.
|
||||
onStepClicked: (index) => { rail.currentStep = index }
|
||||
}
|
||||
|
||||
// ── Right: active step's panel (both kept alive to preserve state) ──
|
||||
Item {
|
||||
id: rightPane
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.preferredHeight: rail.currentStep === 0
|
||||
? selectCard.implicitHeight
|
||||
: depositCard.implicitHeight
|
||||
|
||||
// ── Step 1: select pair ──────────────────────────────────
|
||||
Rectangle {
|
||||
id: selectCard
|
||||
width: parent.width
|
||||
visible: rail.currentStep === 0
|
||||
implicitHeight: selectCol.implicitHeight + Theme.spacing.large * 2
|
||||
radius: Theme.spacing.radiusLarge
|
||||
color: Theme.palette.backgroundSecondary
|
||||
border.width: 1
|
||||
border.color: Theme.palette.borderSecondary
|
||||
|
||||
ColumnLayout {
|
||||
id: selectCol
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.medium
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Select pair")
|
||||
font.pixelSize: Theme.typography.panelTitleText
|
||||
font.weight: Theme.typography.weightBold
|
||||
color: Theme.palette.text
|
||||
}
|
||||
LogosText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Choose the two tokens for your pool by entering each token's address.")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
|
||||
LogosText {
|
||||
Layout.topMargin: Theme.spacing.small
|
||||
text: qsTr("Token A address")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
LogosTextField {
|
||||
id: tokenAField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "0x…"
|
||||
}
|
||||
|
||||
LogosText {
|
||||
Layout.topMargin: Theme.spacing.small
|
||||
text: qsTr("Token B address")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
LogosTextField {
|
||||
id: tokenBField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "0x…"
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Theme.spacing.medium
|
||||
height: 44
|
||||
text: qsTr("Continue")
|
||||
enabled: tokenAField.text.trim().length > 0
|
||||
&& tokenBField.text.trim().length > 0
|
||||
&& tokenAField.text.trim() !== tokenBField.text.trim()
|
||||
onClicked: rail.currentStep = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 2: deposit amounts ──────────────────────────────
|
||||
Rectangle {
|
||||
id: depositCard
|
||||
width: parent.width
|
||||
visible: rail.currentStep === 1
|
||||
implicitHeight: depositCol.implicitHeight + Theme.spacing.large * 2
|
||||
radius: Theme.spacing.radiusLarge
|
||||
color: Theme.palette.backgroundSecondary
|
||||
border.width: 1
|
||||
border.color: Theme.palette.borderSecondary
|
||||
|
||||
ColumnLayout {
|
||||
id: depositCol
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.medium
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Deposit amounts")
|
||||
font.pixelSize: Theme.typography.panelTitleText
|
||||
font.weight: Theme.typography.weightBold
|
||||
color: Theme.palette.text
|
||||
}
|
||||
LogosText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Set the initial liquidity for the pool.")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
|
||||
// Token pair carried over from step 1.
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Theme.spacing.small
|
||||
implicitHeight: pairCol.implicitHeight + Theme.spacing.medium * 2
|
||||
radius: Theme.spacing.radiusLarge
|
||||
color: Theme.palette.backgroundTertiary
|
||||
border.width: 1
|
||||
border.color: Theme.palette.borderSecondary
|
||||
|
||||
ColumnLayout {
|
||||
id: pairCol
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacing.medium
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Selected pair")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
LogosText {
|
||||
text: qsTr("Token A")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
LogosText {
|
||||
text: root.truncated(tokenAField.text)
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.text
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
LogosText {
|
||||
text: qsTr("Token B")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
LogosText {
|
||||
text: root.truncated(tokenBField.text)
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Amount inputs, one per token.
|
||||
LogosText {
|
||||
Layout.topMargin: Theme.spacing.small
|
||||
text: qsTr("Token A amount")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
LogosTextField {
|
||||
id: amountAField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "0.0"
|
||||
Component.onCompleted: textInput.validator = amountValidator
|
||||
}
|
||||
|
||||
LogosText {
|
||||
Layout.topMargin: Theme.spacing.small
|
||||
text: qsTr("Token B amount")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
}
|
||||
LogosTextField {
|
||||
id: amountBField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "0.0"
|
||||
Component.onCompleted: textInput.validator = amountValidator
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Theme.spacing.medium
|
||||
height: 44
|
||||
text: qsTr("Create pool")
|
||||
enabled: parseFloat(amountAField.text) > 0
|
||||
&& parseFloat(amountBField.text) > 0
|
||||
// Wiring to the AMM new_definition instruction is a follow-up.
|
||||
onClicked: console.log("create pool",
|
||||
tokenAField.text, amountAField.text,
|
||||
tokenBField.text, amountBField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -354,12 +354,28 @@ void AmmUiBackend::persistStoragePath(const QString& path)
|
||||
|
||||
bool AmmUiBackend::changeSequencerAddr(QString url)
|
||||
{
|
||||
const QString trimmed = url.trimmed();
|
||||
if (trimmed.isEmpty()) {
|
||||
QString normalized = url.trimmed();
|
||||
if (normalized.isEmpty()) {
|
||||
qWarning() << "AmmUiBackend: refusing to set empty sequencer_addr";
|
||||
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();
|
||||
|
||||
// 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();
|
||||
in.close();
|
||||
}
|
||||
obj.insert(QStringLiteral("sequencer_addr"), trimmed);
|
||||
obj.insert(QStringLiteral("sequencer_addr"), normalized);
|
||||
|
||||
QFile out(cfg);
|
||||
if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
@ -380,17 +396,14 @@ bool AmmUiBackend::changeSequencerAddr(QString url)
|
||||
out.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
|
||||
out.close();
|
||||
|
||||
// Re-open so the live wallet uses the new endpoint right away.
|
||||
if (isWalletOpen()) {
|
||||
const QString stg = storagePath().isEmpty() ? defaultStoragePath() : storagePath();
|
||||
const int err = m_logos->logos_execution_zone.open(cfg, stg);
|
||||
if (err != WALLET_FFI_SUCCESS) {
|
||||
qWarning() << "AmmUiBackend: reopen after sequencer change failed, code" << err;
|
||||
return false;
|
||||
}
|
||||
refreshSequencerAddr();
|
||||
refreshAccounts();
|
||||
}
|
||||
// Config is now the source of truth — reflect the change in the UI.
|
||||
if (sequencerAddr() != normalized)
|
||||
setSequencerAddr(normalized);
|
||||
checkReachability();
|
||||
|
||||
// The module can't re-open an already-open wallet, so the new endpoint only
|
||||
// takes effect on the next launch. The UI confirms a restart before calling
|
||||
// this and closes the app afterwards.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user