mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-26 14:41:12 +00:00
The `*Raw` suffix on the module's amount/price/balance/LP fields was
redundant — every such field is already a base-unit integer, and there was
no formatted sibling to disambiguate from. Drop it across the whole wire
contract in lockstep: the amm_ffi request/response fields (snake_case
`amount_in_raw` → `amount_in`, serde `rename_all="camelCase"` keeps the JSON
keys mapped), the C++ module API, the QtRO `.rep`, the QML/app that consumes
it, the mjs tests, and the module README.
Examples: expectedOutRaw→expectedOut, minReceivedRaw→minReceived,
maxInRaw→maxIn, requiredInRaw→requiredIn, priceRaw→price, reserve{A,B}Raw→
reserve{A,B}, amount{In,Out}Raw→amount{In,Out}, expectedLpRaw→expectedLp,
lpAmountRaw→lpAmount, {max,min,minimum,actual}Amount{A,B}Raw, minimumLpRaw,
minLpRaw, selectedBalance*Raw, totalSupplyRaw, quote*Raw. This also unifies a
pre-existing inconsistency where resolvePoolAccount already emitted `reserveA`
and resolveTokens already emitted `balance`.
Kept where a formatted UI sibling of the same base name exists, so `Raw`
still disambiguates the base-unit value: amountARaw / amountBRaw (vs the
user-input `amountA`/`amountB`), balanceRaw (vs display `balance`), and
initialPriceRaw (vs formatted `initialPrice`). Also kept the format-boundary
helpers formatRaw / rawLpText / probeRaw / displayRaw / displayQuoteRaw /
boundRaw.
BREAKING: the `amm_module` public API field names change (logoscore /
Basecamp / QtRO consumers must update).
192 lines
6.6 KiB
QML
192 lines
6.6 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
|
|
import Logos.Wallet
|
|
|
|
import "../shared"
|
|
|
|
AmmTokenAmountSurface {
|
|
id: root
|
|
|
|
property string text: ""
|
|
property string balance: ""
|
|
property string helperText: ""
|
|
property bool showMaxButton: true
|
|
property var tokenData: null
|
|
property var tokens: []
|
|
property string selectedTokenId: ""
|
|
property bool tokenInvalid: false
|
|
property bool tokenSelectionEnabled: true
|
|
property bool editPending: false
|
|
property string pendingValue: ""
|
|
// Account selector (create-pool only): the wallet holdings to pick from, the
|
|
// token's base58 definitionId to filter by, and whether to show it at all. The
|
|
// chosen holding id is exposed as selectedHoldingId. Mirrors the swap card, where
|
|
// the selector sits inside the input card below the token button.
|
|
property var holdings: []
|
|
property string holdingDefinitionId: ""
|
|
property bool showHoldingSelector: false
|
|
// objectName forwarded to the account selector, so UI tests can pick the
|
|
// funding holding for this side deterministically.
|
|
property string selectorObjectName: ""
|
|
readonly property string selectedHoldingId: root.footerItem && root.footerItem.selectedAccountId
|
|
? String(root.footerItem.selectedAccountId) : ""
|
|
// Base-unit balance of the selected funding holding (the lean quotes don't check funding,
|
|
// so the form compares this against the entered amount). "0" when nothing is selected.
|
|
readonly property string selectedBalance: root.footerItem && root.footerItem.selectedBalance
|
|
? String(root.footerItem.selectedBalance) : "0"
|
|
|
|
footer: root.showHoldingSelector ? accountFooter : null
|
|
footerHeight: root.footerItem ? root.footerItem.implicitHeight : 0
|
|
property var disabledReasonForCode: function(code) {
|
|
return qsTr("This token is unavailable (%1).").arg(code || "unknown")
|
|
}
|
|
property var detailForToken: function(token) { return "" }
|
|
property alias popup: tokenModal
|
|
property alias query: tokenModal.searchText
|
|
readonly property var rows: tokenModal.rows
|
|
|
|
signal editingChanged(string value)
|
|
signal editingCommitted(string value)
|
|
signal maxClicked
|
|
signal tokenSelected(string tokenId)
|
|
signal tokenEntered(string value)
|
|
|
|
amount: root.text
|
|
supportingText: root.helperText
|
|
supportingActionText: root.showMaxButton ? qsTr("MAX") : ""
|
|
accessory: tokenActions
|
|
accessoryWidth: width < 360 ? 132 : 180
|
|
accessoryHeight: root.balance.length > 0 ? 58 : 40
|
|
|
|
onAmountEdited: function(value) {
|
|
root.pendingValue = value
|
|
root.editPending = true
|
|
root.editingChanged(value)
|
|
commitTimer.restart()
|
|
}
|
|
onAmountEditingFinished: function(value) {
|
|
root.pendingValue = value
|
|
root.commitPendingEdit()
|
|
}
|
|
onSupportingActionClicked: root.maxClicked()
|
|
onTextChanged: {
|
|
if (root.editPending && root.text !== root.pendingValue) {
|
|
commitTimer.stop()
|
|
root.editPending = false
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
id: commitTimer
|
|
|
|
interval: 250
|
|
repeat: false
|
|
onTriggered: root.commitPendingEdit()
|
|
}
|
|
|
|
Component {
|
|
id: accountFooter
|
|
|
|
// The Loader stretches this wrapper to the card width; the selector takes the
|
|
// right half, right-aligned, matching the swap card's account selector.
|
|
Item {
|
|
implicitHeight: footerSelector.implicitHeight
|
|
|
|
property alias selectedAccountId: footerSelector.selectedAccountId
|
|
property alias selectedBalance: footerSelector.selectedBalance
|
|
|
|
ProgramAccountSelector {
|
|
id: footerSelector
|
|
|
|
objectName: root.selectorObjectName
|
|
width: Math.round(parent.width / 2)
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
sourceModel: root.holdings
|
|
accountType: "TokenHolding"
|
|
stateField: "definitionId"
|
|
stateValue: root.holdingDefinitionId
|
|
selectionMode: ProgramAccountSelector.Input
|
|
showWhenSingle: true
|
|
textAlignment: Text.AlignRight
|
|
backgroundColor: root.theme.colors.panelBg
|
|
hoverColor: root.theme.colors.panelHoverBg
|
|
textColor: root.theme.colors.textPrimary
|
|
secondaryTextColor: root.theme.colors.textSecondary
|
|
borderColor: root.theme.colors.borderStrong
|
|
focusColor: root.theme.colors.ctaBg
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: tokenActions
|
|
|
|
AmmTokenAccessory {
|
|
theme: root.theme
|
|
enabled: root.tokenSelectionEnabled
|
|
invalid: root.tokenInvalid
|
|
hasToken: root.tokenData !== null
|
|
tokenColor: root.tokenColor(root.tokenData)
|
|
tokenLetter: root.tokenLetter(root.tokenData)
|
|
tokenText: root.tokenText(root.tokenData)
|
|
balance: root.balance
|
|
accessibleName: qsTr("Select %1").arg(root.label)
|
|
onClicked: tokenModal.open()
|
|
}
|
|
}
|
|
|
|
TokenSelectorModal {
|
|
id: tokenModal
|
|
|
|
theme: root.theme
|
|
tokens: root.tokens
|
|
title: qsTr("Select a token")
|
|
searchPlaceholder: qsTr("Search name or address")
|
|
popularTitle: qsTr("Quick select")
|
|
listTitle: qsTr("All tokens")
|
|
allowCustomEntry: true
|
|
disabledReasonForCode: root.disabledReasonForCode
|
|
detailForToken: root.detailForToken
|
|
|
|
onTokenSelected: function(token) {
|
|
root.tokenSelected(String(token.definitionId || token.address || ""))
|
|
}
|
|
onTokenEntered: function(value) { root.tokenEntered(value) }
|
|
}
|
|
|
|
function acceptInput(value) {
|
|
tokenModal.acceptInput(value)
|
|
}
|
|
|
|
function commitPendingEdit() {
|
|
if (!root.editPending)
|
|
return
|
|
commitTimer.stop()
|
|
root.editPending = false
|
|
root.editingCommitted(root.pendingValue)
|
|
}
|
|
|
|
function tokenText(token) {
|
|
if (!token)
|
|
return qsTr("Select token")
|
|
return String(token.symbol || token.name || root.shortId(root.selectedTokenId))
|
|
}
|
|
|
|
function tokenLetter(token) {
|
|
var text = root.tokenText(token)
|
|
return token ? String(token.letter || text.charAt(0).toUpperCase()) : ""
|
|
}
|
|
|
|
function tokenColor(token) {
|
|
return token && token.color ? token.color : root.theme.colors.noTokenCircle
|
|
}
|
|
|
|
function shortId(value) {
|
|
var text = String(value || "")
|
|
return text.length > 14 ? text.slice(0, 7) + "..." + text.slice(-5) : text
|
|
}
|
|
}
|