Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/amm/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ tests/testnet/amm-tokens.json
# Isolated known-pools config written by tests/testnet/setup-amm-testnet.sh
tests/testnet/amm-pools.json

# Isolated single-file registry (AMM_REGISTRY_URL path) written by the setup script
tests/testnet/amm-registry.json

# Isolated custom-token store (CUSTOM_TOKEN_CONFIG) — initialized by the setup script
# and written by the app during tests/custom-token.mjs
tests/testnet/custom-tokens.json
3 changes: 3 additions & 0 deletions apps/amm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ logos_module(
src/AmmUiPlugin.cpp
src/AmmUiBackend.h
src/AmmUiBackend.cpp
src/RegistryLoader.h
src/RegistryLoader.cpp
FIND_PACKAGES
Qt6Gui
LINK_LIBRARIES
Qt6::Gui
Qt6::Network
LINK_TARGETS
logos_wallet_access
)
39 changes: 39 additions & 0 deletions apps/amm/qml/Main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,43 @@ Item {
visible: navbar.currentIndex === 2 && navbar.currentSubIndex === 1
}
}

// App settings: a cogwheel in the bottom-right corner opens the settings modal
// (registry URL + network picker). App-specific, so it lives here rather than
// in the shared wallet UI.
Rectangle {
id: settingsButton
objectName: "appSettingsButton"
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: 20
anchors.bottomMargin: 20
z: 200
width: 44
height: 44
radius: 22
color: settingsMouse.pressed ? Theme.palette.borderSecondary
: Theme.palette.backgroundSecondary
border.color: Theme.palette.borderSecondary
border.width: 1

Text {
anchors.centerIn: parent
text: "⚙" // gear
font.pixelSize: 20
color: Theme.palette.textSecondary
}

MouseArea {
id: settingsMouse
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: settingsModal.open()
}
}

SettingsModal {
id: settingsModal
backend: root.ready ? root.backend : null
}
}
164 changes: 164 additions & 0 deletions apps/amm/qml/chrome/SettingsModal.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

import "../components/liquidity"

// App settings modal, opened from the cogwheel in Main.qml. Currently a single
// "Registry" section: the known-tokens / known-pools registry URL and the network
// picker, bound to the AMM backend (registryUrl / saveRegistryUrl / networks /
// activeNetwork / selectNetwork). This is AMM-specific, so it lives in the app
// rather than the shared wallet UI.
Popup {
id: root

property var backend: null

AmmTheme { id: theme }

parent: Overlay.overlay
modal: true
focus: true
width: parent && parent.width > 32 ? Math.max(0, Math.min(440, parent.width - 32)) : 300
x: parent ? Math.round((parent.width - width) / 2) : 0
y: parent ? Math.round((parent.height - height) / 2) : 0
padding: 20
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside

onOpened: {
registryUrlField.text = root.backend ? (root.backend.registryUrl || "") : ""
networkSelector.syncSelection()
}

Overlay.modal: Rectangle { color: Qt.rgba(0, 0, 0, 0.4) }

background: Rectangle {
radius: 16
color: theme.colors.cardBg
border.color: theme.colors.border
border.width: 1
}

contentItem: ColumnLayout {
spacing: 14

RowLayout {
Layout.fillWidth: true

Label {
Layout.fillWidth: true
text: qsTr("Settings")
color: theme.colors.textPrimary
font.bold: true
font.pixelSize: 17
}

Label {
text: "✕" // close
color: theme.colors.textSecondary
font.pixelSize: 16
MouseArea {
anchors.fill: parent
anchors.margins: -8
cursorShape: Qt.PointingHandCursor
onClicked: root.close()
}
}
}

Label {
text: qsTr("Registry")
color: theme.colors.textPrimary
font.bold: true
}

Label {
Layout.fillWidth: true
text: qsTr("URL of the known-tokens / known-pools registry the app loads. Leave empty to load none.")
color: theme.colors.textSecondary
font.pixelSize: 11
wrapMode: Text.WordWrap
}

TextField {
id: registryUrlField
objectName: "settingsRegistryUrlField"
Layout.fillWidth: true
text: root.backend ? (root.backend.registryUrl || "") : ""
placeholderText: qsTr("https://…/amm-registry.json")
color: theme.colors.textPrimary
background: Rectangle {
radius: 8
color: theme.colors.inputBg
border.color: theme.colors.border
border.width: 1
}
}

Button {
id: saveButton
objectName: "settingsRegistrySaveButton"
Layout.fillWidth: true
text: qsTr("Save")
onClicked: {
if (root.backend)
root.backend.saveRegistryUrl(registryUrlField.text)
}
Comment on lines +100 to +108
contentItem: Text {
text: saveButton.text
color: "#FFFFFF"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
radius: 8
implicitHeight: 36
color: saveButton.pressed ? theme.colors.ctaPressedBg
: saveButton.hovered ? theme.colors.ctaHoverBg
: theme.colors.ctaBg
}
}

Label {
Layout.fillWidth: true
visible: networkSelector.count > 0
text: qsTr("Network")
color: theme.colors.textSecondary
font.pixelSize: 11
}

ComboBox {
id: networkSelector
objectName: "settingsNetworkSelector"
Layout.fillWidth: true
visible: count > 0
textRole: "name"
valueRole: "id"
model: root.backend ? root.backend.networks : []

// Select the active network (which defaults to the first), falling back
// to the first item. Imperative — the model syncs from the backend after
// this is created, so a currentIndex binding would compute -1 before the
// model arrives and never re-run.
function syncSelection() {
if (!root.backend || count === 0)
return
const i = indexOfValue(root.backend.activeNetwork)
currentIndex = i >= 0 ? i : 0
}
Component.onCompleted: syncSelection()
onCountChanged: syncSelection()
Connections {
target: root.backend
function onActiveNetworkChanged() { networkSelector.syncSelection() }
}

onActivated: {
if (root.backend)
root.backend.selectNetwork(currentValue)
}
}
}
}
2 changes: 2 additions & 0 deletions apps/amm/qml/pages/LiquidityPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ onBackendChanged: { root.refreshHoldings(); root.refreshFeeTiers(); root.refresh
Connections {
target: root.backend
function onIsWalletOpenChanged() { root.refreshHoldings(); root.refreshTokens() }
// Re-fetch when the registry snapshot refreshes (e.g. a remote list lands).
function onRegistryRevisionChanged() { root.refreshTokens() }
}

readonly property int pageMargin: width < 640 ? 16 : 24
Expand Down
6 changes: 6 additions & 0 deletions apps/amm/qml/pages/PoolsPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ Item {
onBackendChanged: root.loadPools()
onRuntimeChanged: root.loadPools()

Connections {
target: root.backend
// Re-fetch when the registry snapshot refreshes (e.g. a remote list lands).
function onRegistryRevisionChanged() { root.loadPools() }
}

AmmTheme {
id: theme
}
Expand Down
14 changes: 11 additions & 3 deletions apps/amm/qml/pages/SwapPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,26 @@ Item {
})
}

function loadTokens() {
if (!root.backend)
return
logos.watch(root.backend.tokenList(),
function(list) { root.tokens = list },
function(err) { console.warn("tokenList error:", err) })
}

onBackendChanged: {
if (root.backend) {
logos.watch(root.backend.tokenList(),
function(list) { root.tokens = list },
function(err) { console.warn("tokenList error:", err) })
root.loadTokens()
root.refreshHoldings()
}
}

Connections {
target: root.backend
function onIsWalletOpenChanged() { root.refreshHoldings() }
// Re-fetch when the registry snapshot refreshes (e.g. a remote list lands).
function onRegistryRevisionChanged() { root.loadTokens() }
}

QtObject {
Expand Down
Loading
Loading