mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-10 14:26:34 +00:00
fix/tx-comps: Update assets when tokens changed
When tokens are added/removed, the asset list in the AssetAndAmountInput is updated. The selected asset can be specified by the parent component which is needed for things like sticker market where we need to set SNT as the fixed token. Improved the validation for the component: - validate() can be called externally - validation display is handled internally and messages can be customised - validation error messages are handled by the Input component and validation UX is consistent with other tx components
This commit is contained in:
parent
d07daac377
commit
3ff93c26e6
@ -217,6 +217,8 @@ QtObject:
|
||||
|
||||
proc toggleAsset*(self: WalletView, symbol: string, checked: bool, address: string, name: string, decimals: int, color: string) {.slot.} =
|
||||
self.status.wallet.toggleAsset(symbol, checked, address, name, decimals, color)
|
||||
for account in self.status.wallet.accounts:
|
||||
self.accounts.updateAssetsInList(account.address, account.assetList)
|
||||
|
||||
proc updateView*(self: WalletView) =
|
||||
self.totalFiatBalanceChanged()
|
||||
|
@ -1,7 +1,7 @@
|
||||
import NimQml, Tables, random, strformat, json_serialization
|
||||
import sequtils as sequtils
|
||||
import account_item, asset_list
|
||||
from ../../../status/wallet import WalletAccount
|
||||
from ../../../status/wallet import WalletAccount, Asset
|
||||
|
||||
const accountColors* = ["#9B832F", "#D37EF4", "#1D806F", "#FA6565", "#7CDA00", "#887af9", "#8B3131"]
|
||||
type
|
||||
@ -99,3 +99,18 @@ QtObject:
|
||||
proc forceUpdate*(self: AccountList) =
|
||||
self.beginResetModel()
|
||||
self.endResetModel()
|
||||
|
||||
proc hasAccount*(self: AccountList, address: string): bool =
|
||||
result = self.accounts.anyIt(it.account.address == address)
|
||||
|
||||
proc updateAssetsInList*(self: AccountList, address: string, assets: seq[Asset]) =
|
||||
if not self.hasAccount(address):
|
||||
return
|
||||
|
||||
let topLeft = self.createIndex(0, 0, nil)
|
||||
let bottomRight = self.createIndex(self.accounts.len, 0, nil)
|
||||
self.accounts.apply(proc(it: var AccountView) =
|
||||
if it.account.address == address:
|
||||
it.assets.setNewData(assets))
|
||||
|
||||
self.dataChanged(topLeft, bottomRight, @[AccountRoles.Assets.int])
|
||||
|
@ -35,7 +35,8 @@ Item {
|
||||
}
|
||||
|
||||
function validate() {
|
||||
selectRecipient.validate()
|
||||
const isRecipientValid = selectRecipient.validate()
|
||||
const isAssetAndAmountValid = txtAmount.validate()
|
||||
if (txtPassword.text === "") {
|
||||
//% "You need to enter a password"
|
||||
passwordValidationError = qsTrId("you-need-to-enter-a-password")
|
||||
@ -46,20 +47,7 @@ Item {
|
||||
passwordValidationError = ""
|
||||
}
|
||||
|
||||
if (txtAmount.text === "") {
|
||||
//% "You need to enter an amount"
|
||||
amountValidationError = qsTrId("you-need-to-enter-an-amount")
|
||||
} else if (isNaN(txtAmount.text)) {
|
||||
//% "This needs to be a number"
|
||||
amountValidationError = qsTrId("this-needs-to-be-a-number")
|
||||
} else if (parseFloat(txtAmount.text) > parseFloat(selectAsset.selectedAsset.Value)) {
|
||||
//% "Amount needs to be lower than your balance (%1)"
|
||||
amountValidationError = qsTrId("amount-needs-to-be-lower-than-your-balance-(%1)").arg(selectedAccountValue)
|
||||
} else {
|
||||
amountValidationError = ""
|
||||
}
|
||||
|
||||
return passwordValidationError === "" && toValidationError === "" && amountValidationError === ""
|
||||
return passwordValidationError === "" && toValidationError === "" && amountValidationError === "" && isRecipientValid && isAssetAndAmountValid
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
@ -83,13 +71,12 @@ Item {
|
||||
}
|
||||
|
||||
AssetAndAmountInput {
|
||||
id: txtAmount
|
||||
selectedAccount: walletModel.currentAccount
|
||||
defaultCurrency: walletModel.defaultCurrency
|
||||
errorMessage: amountValidationError
|
||||
anchors.top: parent.top
|
||||
getFiatValue: walletModel.getFiatValue
|
||||
getCryptoValue: walletModel.getCryptoValue
|
||||
id: txtAmount
|
||||
selectedAccount: walletModel.currentAccount
|
||||
defaultCurrency: walletModel.defaultCurrency
|
||||
anchors.top: parent.top
|
||||
getFiatValue: walletModel.getFiatValue
|
||||
getCryptoValue: walletModel.getCryptoValue
|
||||
}
|
||||
|
||||
AccountSelector {
|
||||
@ -101,7 +88,7 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
onSelectedAccountChanged: {
|
||||
txtAmount.selectedAccount = selectFromAccount.selectedAccount
|
||||
txtAmount.selectedAccount = selectFromAccount.selectedAccount
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,157 +5,193 @@ import QtGraphicalEffects 1.13
|
||||
import "../imports"
|
||||
|
||||
Item {
|
||||
property string errorMessage: ""
|
||||
property string defaultCurrency: "USD"
|
||||
property string fiatBalance: "0.00"
|
||||
property alias text: inputAmount.text
|
||||
property var selectedAccount
|
||||
property var getFiatValue: function () {}
|
||||
property var getCryptoValue: function () {}
|
||||
property string balanceErrorMessage: qsTr("Insufficient balance")
|
||||
property string greaterThan0ErrorMessage: qsTr("Must be greater than 0")
|
||||
//% "This needs to be a number"
|
||||
property string invalidInputErrorMessage: qsTrId("this-needs-to-be-a-number")
|
||||
//% "You need to enter an amount"
|
||||
property string noInputErrorMessage: qsTrId("you-need-to-enter-an-amount")
|
||||
property string defaultCurrency: "USD"
|
||||
property string fiatBalance: "0.00"
|
||||
property alias text: inputAmount.text
|
||||
property var selectedAccount
|
||||
property alias selectedAsset: selectAsset.selectedAsset
|
||||
property var getFiatValue: function () {}
|
||||
property var getCryptoValue: function () {}
|
||||
property bool isDirty: false
|
||||
|
||||
id: root
|
||||
id: root
|
||||
|
||||
height: inputAmount.height + txtFiatBalance.height + txtFiatBalance.anchors.topMargin
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
height: inputAmount.height + (inputAmount.validationError ? -16 - inputAmount.validationErrorTopMargin : 0) + txtFiatBalance.height + txtFiatBalance.anchors.topMargin
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
onSelectedAccountChanged: {
|
||||
txtBalance.text = selectAsset.selectedAsset.value
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
height: txtBalanceDesc.height
|
||||
|
||||
StyledText {
|
||||
id: txtBalanceDesc
|
||||
text: qsTr("Balance: ")
|
||||
anchors.right: txtBalance.left
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 13
|
||||
color: parseFloat(inputAmount.text) > parseFloat(txtBalance.text) ? Style.current.red : Style.current.secondaryText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: txtBalance
|
||||
property bool hovered: false
|
||||
text: selectAsset.selectedAsset.value
|
||||
anchors.right: parent.right
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 13
|
||||
color: {
|
||||
if (txtBalance.hovered) {
|
||||
return Style.current.textColor
|
||||
}
|
||||
return parseFloat(inputAmount.text) > parseFloat(txtBalance.text) ? Style.current.red : Style.current.secondaryText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onExited: {
|
||||
txtBalance.hovered = false
|
||||
}
|
||||
onEntered: {
|
||||
txtBalance.hovered = true
|
||||
}
|
||||
onClicked: {
|
||||
inputAmount.text = selectAsset.selectedAsset.value
|
||||
txtFiatBalance.text = root.getFiatValue(inputAmount.text, selectAsset.selectedAsset.symbol, root.defaultCurrency)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Input {
|
||||
id: inputAmount
|
||||
label: qsTr("Asset & Amount")
|
||||
placeholderText: "0.00"
|
||||
validationError: root.errorMessage
|
||||
anchors.top: parent.top
|
||||
customHeight: 56
|
||||
Keys.onReleased: {
|
||||
let amount = inputAmount.text.trim()
|
||||
|
||||
if (isNaN(amount)) {
|
||||
return
|
||||
function validate(checkDirty) {
|
||||
let isValid = true
|
||||
let error = ""
|
||||
const hasTyped = checkDirty ? isDirty : true
|
||||
const balance = parseFloat(txtBalance.text || "0.00")
|
||||
const input = parseFloat(inputAmount.text || "0.00")
|
||||
const noInput = inputAmount.text === ""
|
||||
if (noInput && hasTyped) {
|
||||
error = noInputErrorMessage
|
||||
isValid = false
|
||||
} else if (isNaN(inputAmount.text)) {
|
||||
error = invalidInputErrorMessage
|
||||
isValid = false
|
||||
} else if (input === 0.00 && hasTyped) {
|
||||
error = greaterThan0ErrorMessage
|
||||
isValid = false
|
||||
} else if (input > balance && !noInput) {
|
||||
error = balanceErrorMessage
|
||||
isValid = false
|
||||
}
|
||||
if (amount === "") {
|
||||
txtFiatBalance.text = "0.00"
|
||||
if (!isValid) {
|
||||
inputAmount.validationError = error
|
||||
txtBalanceDesc.color = Style.current.danger
|
||||
txtBalance.color = Style.current.danger
|
||||
} else {
|
||||
txtFiatBalance.text = root.getFiatValue(amount, selectAsset.selectedAsset.symbol, root.defaultCurrency)
|
||||
inputAmount.validationError = ""
|
||||
txtBalanceDesc.color = Style.current.secondaryText
|
||||
txtBalance.color = Qt.binding(function() { return txtBalance.hovered ? Style.current.textColor : Style.current.secondaryText })
|
||||
}
|
||||
}
|
||||
}
|
||||
return isValid
|
||||
}
|
||||
|
||||
AssetSelector {
|
||||
id: selectAsset
|
||||
assets: root.selectedAccount.assets
|
||||
width: 86
|
||||
height: 28
|
||||
anchors.top: inputAmount.top
|
||||
anchors.topMargin: Style.current.bigPadding + 14
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
onSelectedAssetChanged: {
|
||||
inputAmount.text = selectAsset.selectedAsset.value
|
||||
txtBalance.text = selectAsset.selectedAsset.value
|
||||
txtFiatBalance.text = root.getFiatValue(inputAmount.text, selectAsset.selectedAsset.symbol, root.defaultCurrency)
|
||||
}
|
||||
}
|
||||
onSelectedAccountChanged: {
|
||||
if (!selectAsset.selectedAsset) {
|
||||
return
|
||||
}
|
||||
txtBalance.text = selectAsset.selectedAsset.value
|
||||
}
|
||||
|
||||
Item {
|
||||
height: txtFiatBalance.height
|
||||
anchors.left: parent.left
|
||||
anchors.top: inputAmount.bottom
|
||||
anchors.topMargin: inputAmount.labelMargin
|
||||
Item {
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
height: txtBalanceDesc.height
|
||||
|
||||
StyledTextField {
|
||||
id: txtFiatBalance
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
color: txtFiatBalance.activeFocus ? Style.current.textColor : Style.current.secondaryText
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 12
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
text: root.fiatBalance
|
||||
selectByMouse: true
|
||||
background: Rectangle {
|
||||
color: Style.current.transparent
|
||||
}
|
||||
padding: 0
|
||||
Keys.onReleased: {
|
||||
let balance = txtFiatBalance.text.trim()
|
||||
if (balance === "" || isNaN(balance)) {
|
||||
return
|
||||
}
|
||||
inputAmount.text = root.getCryptoValue(balance, root.defaultCurrency, selectAsset.selectedAsset.symbol)
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
id: txtBalanceDesc
|
||||
text: qsTr("Balance: ")
|
||||
anchors.right: txtBalance.left
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 13
|
||||
color: Style.current.secondaryText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: txtFiatSymbol
|
||||
text: root.defaultCurrency.toUpperCase()
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 12
|
||||
color: Style.current.secondaryText
|
||||
anchors.top: parent.top
|
||||
anchors.left: txtFiatBalance.right
|
||||
anchors.leftMargin: 2
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
id: txtBalance
|
||||
property bool hovered: false
|
||||
text: selectAsset.selectedAsset ? selectAsset.selectedAsset.value : "0.00"
|
||||
anchors.right: parent.right
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 13
|
||||
color: hovered ? Style.current.textColor : Style.current.secondaryText
|
||||
onTextChanged: {
|
||||
root.validate(true)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: root.errorMessage != "" ? root.errorMessage : qsTr("Insufficient balance")
|
||||
anchors.right: parent.right
|
||||
anchors.top: inputAmount.bottom
|
||||
anchors.topMargin: inputAmount.labelMargin
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 12
|
||||
color: Style.current.red
|
||||
visible: parseFloat(inputAmount.text) > parseFloat(txtBalance.text) || root.errorMessage != ""
|
||||
}
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onExited: {
|
||||
txtBalance.hovered = false
|
||||
}
|
||||
onEntered: {
|
||||
txtBalance.hovered = true
|
||||
}
|
||||
onClicked: {
|
||||
inputAmount.text = selectAsset.selectedAsset.value
|
||||
txtFiatBalance.text = root.getFiatValue(inputAmount.text, selectAsset.selectedAsset.symbol, root.defaultCurrency)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Input {
|
||||
id: inputAmount
|
||||
label: qsTr("Asset & Amount")
|
||||
placeholderText: "0.00"
|
||||
anchors.top: parent.top
|
||||
customHeight: 56
|
||||
validationErrorAlignment: TextEdit.AlignRight
|
||||
validationErrorTopMargin: 8
|
||||
Keys.onReleased: {
|
||||
root.isDirty = true
|
||||
let amount = inputAmount.text.trim()
|
||||
|
||||
if (isNaN(amount)) {
|
||||
return
|
||||
}
|
||||
if (amount === "") {
|
||||
txtFiatBalance.text = "0.00"
|
||||
} else {
|
||||
txtFiatBalance.text = root.getFiatValue(amount, selectAsset.selectedAsset.symbol, root.defaultCurrency)
|
||||
}
|
||||
}
|
||||
onTextChanged: {
|
||||
root.validate(true)
|
||||
}
|
||||
}
|
||||
|
||||
AssetSelector {
|
||||
id: selectAsset
|
||||
assets: root.selectedAccount.assets
|
||||
width: 86
|
||||
height: 28
|
||||
anchors.top: inputAmount.top
|
||||
anchors.topMargin: Style.current.bigPadding + 14
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
onSelectedAssetChanged: {
|
||||
txtBalance.text = selectAsset.selectedAsset.value
|
||||
if (inputAmount.text === "" || isNan(inputAmount.text)) {
|
||||
return
|
||||
}
|
||||
txtFiatBalance.text = root.getFiatValue(inputAmount.text, selectAsset.selectedAsset.symbol, root.defaultCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: txtFiatBalance.height
|
||||
anchors.left: parent.left
|
||||
anchors.top: inputAmount.bottom
|
||||
anchors.topMargin: inputAmount.validationError ? -16 : inputAmount.validationErrorTopMargin
|
||||
|
||||
StyledTextField {
|
||||
id: txtFiatBalance
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
color: txtFiatBalance.activeFocus ? Style.current.textColor : Style.current.secondaryText
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 12
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
text: root.fiatBalance
|
||||
selectByMouse: true
|
||||
background: Rectangle {
|
||||
color: Style.current.transparent
|
||||
}
|
||||
padding: 0
|
||||
Keys.onReleased: {
|
||||
let balance = txtFiatBalance.text.trim()
|
||||
if (balance === "" || isNaN(balance)) {
|
||||
return
|
||||
}
|
||||
inputAmount.text = root.getCryptoValue(balance, root.defaultCurrency, selectAsset.selectedAsset.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: txtFiatSymbol
|
||||
text: root.defaultCurrency.toUpperCase()
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 12
|
||||
color: Style.current.secondaryText
|
||||
anchors.top: parent.top
|
||||
anchors.left: txtFiatBalance.right
|
||||
anchors.leftMargin: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,13 @@ Item {
|
||||
width: 86
|
||||
height: 24
|
||||
|
||||
onSelectedAssetChanged: {
|
||||
if (selectedAsset && selectedAsset.symbol) {
|
||||
iconImg.source = "../app/img/tokens/" + selectedAsset.symbol.toUpperCase() + ".png"
|
||||
selectedTextField.text = selectedAsset.symbol.toUpperCase()
|
||||
}
|
||||
}
|
||||
|
||||
Select {
|
||||
id: select
|
||||
model: root.assets
|
||||
@ -32,12 +39,10 @@ Item {
|
||||
sourceSize.width: 24
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "../app/img/tokens/" + selectedAsset.symbol.toUpperCase() + ".png"
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: selectedTextField
|
||||
text: selectedAsset.symbol.toUpperCase()
|
||||
anchors.left: iconImg.right
|
||||
anchors.leftMargin: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@ -58,7 +63,7 @@ Item {
|
||||
property bool isLastItem: index === assets.rowCount() - 1
|
||||
|
||||
Component.onCompleted: {
|
||||
if (isFirstItem) {
|
||||
if (!selectedAsset && isFirstItem) {
|
||||
root.selectedAsset = { address, name, value, symbol, fiatBalanceDisplay, fiatBalance }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user