feat(@desktop/wallet): Send modal update

fixes #8609
This commit is contained in:
Khushboo Mehta 2022-12-14 22:06:14 +01:00 committed by Khushboo-dev-cpp
parent f84404c956
commit 17aaec2d53
19 changed files with 453 additions and 154 deletions

View File

@ -85,6 +85,10 @@ method getFiatValue*(self: AccessInterface, cryptoBalance: string, cryptoSymbol:
{.base.} =
raise newException(ValueError, "No implementation available")
method getCryptoValue*(self: AccessInterface, fiatAmount: string, cryptoSymbol: string, fiatSymbol: string): string
{.base.} =
raise newException(ValueError, "No implementation available")
method getGasEthValue*(self: AccessInterface, gweiValue: string, gasLimit: string): string {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -339,6 +339,20 @@ method getFiatValue*(self: Module, cryptoBalance: string, cryptoSymbol: string,
let value = floatCryptoBalance * price
return fmt"{value}"
method getCryptoValue*(self: Module, fiatAmount: string, cryptoSymbol: string, fiatSymbol: string): string =
var fiatAmountBalance: float = 0
try:
fiatAmountBalance = parseFloat(fiatAmount)
except ValueError:
return "0.00"
if (fiatAmount == "" or cryptoSymbol == "" or fiatSymbol == ""):
return "0.00"
let price = self.controller.getPrice(cryptoSymbol, fiatSymbol)
let value = fiatAmountBalance / price
return fmt"{value}"
method getGasEthValue*(self: Module, gweiValue: string, gasLimit: string): string {.slot.} =
var gasLimitInt:int

View File

@ -126,6 +126,9 @@ QtObject:
proc getFiatValue*(self: View, cryptoBalance: string, cryptoSymbol: string, fiatSymbol: string): string {.slot.} =
return self.delegate.getFiatValue(cryptoBalance, cryptoSymbol, fiatSymbol)
proc getCryptoValue*(self: View, fiatAmount: string, cryptoSymbol: string, fiatSymbol: string): string {.slot.} =
return self.delegate.getCryptoValue(fiatAmount, cryptoSymbol, fiatSymbol)
proc getGasEthValue*(self: View, gweiValue: string, gasLimit: string): string {.slot.} =
return self.delegate.getGasEthValue(gweiValue, gasLimit)

View File

@ -62,6 +62,39 @@ proc getFeesTotal*(paths: seq[TransactionPathDto]): Fees =
fees.totalTime += path.estimatedTime
return fees
proc getTotalAmountToReceive*(paths: seq[TransactionPathDto]): UInt256 =
var totalAmountToReceive: UInt256 = stint.u256(0)
for path in paths:
totalAmountToReceive += path.amountOut
return totalAmountToReceive
proc getToNetworksList*(paths: seq[TransactionPathDto]): seq[SendToNetwork] =
var networkMap: Table[int, SendToNetwork] = initTable[int, SendToNetwork]()
for path in paths:
if(networkMap.hasKey(path.toNetwork.chainId)):
networkMap[path.toNetwork.chainId].amountOut = networkMap[path.toNetwork.chainId].amountOut + path.amountOut
else:
networkMap[path.toNetwork.chainId] = SendToNetwork(chainId: path.toNetwork.chainId, chainName: path.toNetwork.chainName, iconUrl: path.toNetwork.iconURL, amountOut: path.amountOut)
return toSeq(networkMap.values)
proc addFirstSimpleBridgeTxFlag(paths: seq[TransactionPathDto]) : seq[TransactionPathDto] =
let txPaths = paths
var firstSimplePath: bool = false
var firstBridgePath: bool = false
for path in txPaths:
if path.bridgeName == "Simple":
if not firstSimplePath:
firstSimplePath = true
path.isFirstSimpleTx = true
else:
if not firstBridgePath:
firstBridgePath = false
path.isFirstBridgeTx = true
return txPaths
const getSuggestedRoutesTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[GetSuggestedRoutesTaskArg](argEncoded)
@ -86,16 +119,17 @@ const getSuggestedRoutesTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall
bestPaths.sort(sortAsc[TransactionPathDto])
let output = %*{
"suggestedRoutes": SuggestedRoutesDto(
best: bestPaths,
candidates: response["Candidates"].getElems().map(x => x.toTransactionPathDto()),
gasTimeEstimate: getFeesTotal(bestPaths)),
best: addFirstSimpleBridgeTxFlag(bestPaths),
gasTimeEstimate: getFeesTotal(bestPaths),
amountToReceive: getTotalAmountToReceive(bestPaths),
toNetworks: getToNetworksList(bestPaths)),
"error": ""
}
arg.finish(output)
except Exception as e:
let output = %* {
"suggestedRoutes": SuggestedRoutesDto(best: @[], candidates: @[], gasTimeEstimate: Fees()),
"suggestedRoutes": SuggestedRoutesDto(best: @[], gasTimeEstimate: Fees(), amountToReceive: stint.u256(0), toNetworks: @[]),
"error": fmt"Error getting suggested routes: {e.msg}"
}
arg.finish(output)

View File

@ -164,7 +164,8 @@ type
cost*: float
estimatedTime*: int
amountInLocked*: bool
isFirstSimpleTx*: bool
isFirstBridgeTx*: bool
proc `$`*(self: TransactionPathDto): string =
return fmt"""TransactionPath(
@ -179,7 +180,9 @@ proc `$`*(self: TransactionPathDto): string =
bonderFees:{self.bonderFees},
cost:{self.cost},
estimatedTime:{self.estimatedTime},
amountInLocked:{self.amountInLocked}
amountInLocked:{self.amountInLocked},
isFirstSimpleTx:{self.isFirstSimpleTx},
isFirstBridgeTx:{self.isFirstBridgeTx}
)"""
proc toTransactionPathDto*(jsonObj: JsonNode): TransactionPathDto =
@ -197,6 +200,8 @@ proc toTransactionPathDto*(jsonObj: JsonNode): TransactionPathDto =
result.estimatedTime = jsonObj{"EstimatedTime"}.getInt
discard jsonObj.getProp("GasAmount", result.gasAmount)
discard jsonObj.getProp("AmountInLocked", result.amountInLocked)
result.isFirstSimpleTx = false
result.isFirstBridgeTx = false
proc convertToTransactionPathDto*(jsonObj: JsonNode): TransactionPathDto =
result = TransactionPathDto()
@ -219,8 +224,16 @@ type
totalTokenFees*: float
totalTime*: int
type
SendToNetwork* = ref object
chainId*: int
chainName*: string
iconUrl*: string
amountOut*: UInt256
type
SuggestedRoutesDto* = ref object
best*: seq[TransactionPathDto]
candidates*: seq[TransactionPathDto]
gasTimeEstimate*: Fees
amountToReceive*: UInt256
toNetworks*: seq[SendToNetwork]

View File

@ -157,7 +157,7 @@ Item {
\qmlproperty real StatusBaseInput::leftPadding
This property sets the leftComponentLoader's left padding.
*/
property real leftPadding: leftComponentLoader.item ? 8 : 16
property real leftPadding: leftComponentLoader.item ? 6 : 16
/*!
\qmlproperty real StatusBaseInput::rightPadding
This property sets the right padding.
@ -308,7 +308,7 @@ Item {
spacing: 2
anchors {
fill: parent
leftMargin: root.leftPadding ? root.leftPadding : leftComponentLoader.item ? 6 : 16
leftMargin: root.leftPadding
rightMargin: root.rightPadding
}

View File

@ -122,6 +122,12 @@ QtObject {
return ensUsernamesModule.getFiatValue(balance, cryptoSymbol, fiatSymbol)
}
function getCryptoValue(balance, cryptoSymbol, fiatSymbol) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getCryptoValue(balance, cryptoSymbol, fiatSymbol)
}
function getGasEthValue(gweiValue, gasLimit) {
if(!root.ensUsernamesModule)
return ""

View File

@ -5,6 +5,8 @@ import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
StatusInput {
id: cursorInput
@ -16,8 +18,8 @@ StatusInput {
placeholderText: ""
input.edit.objectName: "amountInput"
input.edit.cursorVisible: true
input.edit.font.pixelSize: 32
input.placeholderFont.pixelSize: 32
input.edit.font.pixelSize: Utils.getFontSizeBasedOnLetterCount(text)
input.placeholderFont.pixelSize: 34
input.leftPadding: 0
input.rightPadding: 0
input.topPadding: 0

View File

@ -43,7 +43,10 @@ Item {
id: listItem
color: Theme.palette.statusListItem.backgroundColor
width: parent.width
asset.name: index == 0 ? "tiny/gas" : ""
asset.name: "tiny/gas"
asset.color: Theme.palette.directColor1
statusListItemIcon.active: true
statusListItemIcon.opacity: modelData.isFirstSimpleTx
title: qsTr("%1 transaction fee").arg(modelData.fromNetwork.chainName)
subTitle: "%1 eth".arg(LocaleUtils.numberToLocaleString(parseFloat(totalGasAmount)))
property string totalGasAmount : {
@ -69,12 +72,16 @@ Item {
// Bridge
Repeater {
id: bridgeRepeater
model: root.bestRoutes
StatusListItem {
delegate: StatusListItem {
id: listItem2
color: Theme.palette.statusListItem.backgroundColor
width: parent.width
asset.name: index == 0 ? "tiny/bridge" : ""
asset.name: "tiny/bridge"
asset.color: Theme.palette.directColor1
statusListItemIcon.active: true
statusListItemIcon.opacity: modelData.isFirstBridgeTx
title: qsTr("%1 -> %2 bridge").arg(modelData.fromNetwork.chainName).arg(modelData.toNetwork.chainName)
subTitle: "%1 %2".arg(LocaleUtils.numberToLocaleString(modelData.tokenFees)).arg(root.selectedTokenSymbol)
visible: modelData.bridgeName !== "Simple"

View File

@ -12,6 +12,7 @@ import StatusQ.Components 0.1
import StatusQ.Core.Backpressure 1.0
import shared.controls 1.0
import utils 1.0
Item {
id: root
@ -51,6 +52,7 @@ Item {
property string iconSource: ""
property string text: ""
property string searchString
property bool isTokenSelected: !!root.selectedAsset
readonly property var updateSearchText: Backpressure.debounce(root, 1000, function(inputText) {
d.searchString = inputText
@ -60,8 +62,8 @@ Item {
StatusComboBox {
id: comboBox
objectName: "assetSelectorButton"
width: control.width
height: parent.height
width: d.isTokenSelected ? rowLayout.implicitWidth : 116
height: 34
control.padding: 4
control.popup.width: 342
@ -86,30 +88,17 @@ Item {
control.background: Rectangle {
color: "transparent"
border.width: 1
border.color: comboBox.control.hovered ? Theme.palette.primaryColor2 : Theme.palette.directColor8
radius: 16
width: rowLayout.width
implicitHeight: 48
border.width: d.isTokenSelected ? 0 : 1
border.color: d.isTokenSelected ? "transparent" : Theme.palette.directColor7
radius: 12
}
contentItem: RowLayout {
id: rowLayout
spacing: 8
StatusBaseText {
Layout.leftMargin: 8
Layout.alignment: Qt.AlignVCenter
font.pixelSize: 15
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
color: !!root.selectedAsset ? Theme.palette.directColor1: Theme.palette.baseColor1
font.weight: Font.Medium
text: !!root.selectedAsset ? d.text : placeholderText
}
StatusRoundedImage {
Layout.preferredWidth: 40
Layout.preferredHeight: 40
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 21
Layout.preferredHeight: 21
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
visible: !!d.iconSource
image.source: d.iconSource
image.onStatusChanged: {
@ -118,9 +107,34 @@ Item {
}
}
}
Item {
width: 8
height: 0
StatusBaseText {
Layout.alignment: Qt.AlignVCenter
font.pixelSize: 28
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
color: Theme.palette.miscColor1
text: d.text
visible: d.isTokenSelected
}
StatusIcon {
Layout.leftMargin: -3
Layout.alignment: Qt.AlignVCenter
icon: "chevron-down"
width: 16
height: 16
color: Theme.palette.miscColor1
visible: d.isTokenSelected
}
StatusBaseText {
Layout.maximumWidth: comboBox.width - Style.current.padding
Layout.alignment: Qt.AlignCenter
visible: !d.isTokenSelected
font.pixelSize: 15
font.weight: Font.Medium
verticalAlignment: Text.AlignVCenter
color: Theme.palette.baseColor1
elide: Qt.ElideRight
text: placeholderText
}
}
@ -215,11 +229,9 @@ Item {
placeholderText: qsTr("Search for token or enter token address")
onTextChanged: Qt.callLater(d.updateSearchText, text)
input.clearable: true
leftPadding: 0
rightPadding: 0
input.rightComponent: StatusIcon {
width: 24
height: 24
width: 16
height: 16
color: Theme.palette.baseColor1
icon: "search"
}

View File

@ -60,7 +60,7 @@ StatusDialog {
popup.selectedAccount.address,
recipientAddress,
assetSelector.selectedAsset.symbol,
amountToSendInput.text,
amountToSendInput.cryptoValueToSend,
d.uuid,
JSON.stringify(popup.bestRoutes)
)
@ -69,7 +69,7 @@ StatusDialog {
property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function() {
if(!!popup.selectedAccount && !!assetSelector.selectedAsset && d.recipientReady) {
popup.isLoading = true
let amount = parseFloat(amountToSendInput.text) * Math.pow(10, assetSelector.selectedAsset.decimals)
let amount = Math.round(parseFloat(amountToSendInput.cryptoValueToSend) * Math.pow(10, assetSelector.selectedAsset.decimals))
popup.store.suggestedRoutes(popup.selectedAccount.address, amount.toString(16), assetSelector.selectedAsset.symbol,
store.disabledChainIdsFromList, store.disabledChainIdsToList,
store.preferredChainIds, popup.sendType, store.lockedInAmounts)
@ -78,13 +78,15 @@ StatusDialog {
QtObject {
id: d
readonly property double maxFiatBalance: assetSelector.selectedAsset ? assetSelector.selectedAsset.totalBalance: 0
readonly property double maxFiatBalance: !!assetSelector.selectedAsset ? (amountToSendInput.cryptoFiatFlipped ?
assetSelector.selectedAsset.totalCurrencyBalance :
assetSelector.selectedAsset.totalBalance): 0
onMaxFiatBalanceChanged: {
floatValidator.top = maxFiatBalance
amountToSendInput.validate()
amountToSendInput.floatValidator.top = maxFiatBalance
amountToSendInput.input.validate()
}
readonly property bool isReady: amountToSendInput.valid && !amountToSendInput.pending && recipientReady
readonly property bool errorMode: popup.isLoading || !isReady ? false : (networkSelector.bestRoutes && networkSelector.bestRoutes.length <= 0) || networkSelector.errorMode || isNaN(amountToSendInput.text)
readonly property bool isReady: amountToSendInput.input.valid && !amountToSendInput.input.pending && recipientReady
readonly property bool errorMode: popup.isLoading || !isReady ? false : (networkSelector.bestRoutes && networkSelector.bestRoutes.length <= 0) || networkSelector.errorMode || isNaN(amountToSendInput.input.text)
readonly property bool recipientReady: (isAddressValid || isENSValid) && !recipientSelector.isPending
property bool isAddressValid: Utils.isValidAddress(popup.addressText)
property bool isENSValid: false
@ -97,6 +99,7 @@ StatusDialog {
property string totalTimeEstimate
property string totalFeesInEth
property string totalFeesInFiat
property double totalAmountToReceive: 0
property Timer waitTimer: Timer {
interval: 1500
@ -124,14 +127,14 @@ StatusDialog {
store.setDefaultPreferredDisabledChains()
}
amountToSendInput.input.edit.forceActiveFocus()
amountToSendInput.input.input.edit.forceActiveFocus()
if(!!popup.preSelectedAsset) {
assetSelector.selectedAsset = popup.preSelectedAsset
}
if(!!popup.preDefinedAmountToSend) {
amountToSendInput.text = popup.preDefinedAmountToSend
amountToSendInput.input.text = popup.preDefinedAmountToSend
}
if(!!popup.preSelectedRecipient) {
@ -190,63 +193,30 @@ StatusDialog {
anchors.leftMargin: Style.current.xlPadding
anchors.rightMargin: Style.current.xlPadding
z: 1
spacing: 16
Row {
spacing: 16
RowLayout {
width: parent.width
spacing: 8
StatusBaseText {
id: modalHeader
anchors.verticalCenter: parent.verticalCenter
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
text: popup.isBridgeTx ? qsTr("Bridge") : qsTr("Send")
font.pixelSize: 15
font.pixelSize: 28
lineHeight: 38
lineHeightMode: Text.FixedHeight
font.letterSpacing: -0.4
color: Theme.palette.directColor1
}
StatusListItemTag {
bgColor: d.errorMode ? Theme.palette.dangerColor2 : Theme.palette.primaryColor3
height: 22
width: childrenRect.width
title: d.maxFiatBalance > 0 ? qsTr("Max: %1").arg(LocaleUtils.numberToLocaleString(d.maxFiatBalance)) : qsTr("No balances active")
closeButtonVisible: false
titleText.font.pixelSize: 12
titleText.color: d.errorMode ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1
}
}
Item {
width: parent.width
height: amountToSendInput.height
AmountInputWithCursor {
id: amountToSendInput
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: -Style.current.padding
width: parent.width - assetSelector.width
placeholderText: assetSelector.selectedAsset ? "%1 %2".arg(LocaleUtils.numberToLocaleString(0, 2)).arg(assetSelector.selectedAsset.symbol) : LocaleUtils.numberToLocaleString(0, 2)
input.edit.color: d.errorMode ? Theme.palette.dangerColor1 : Theme.palette.directColor1
input.edit.readOnly: !popup.interactive
validators: [
StatusFloatValidator{
id: floatValidator
bottom: 0
top: d.maxFiatBalance
errorMessage: ""
}
]
Keys.onReleased: {
let amount = amountToSendInput.text.trim()
if (!Utils.containsOnlyDigits(amount) || isNaN(amount)) {
return
}
popup.recalculateRoutesAndFees()
}
Layout.maximumWidth: contentWidth
}
StatusAssetSelector {
id: assetSelector
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
enabled: popup.interactive
assets: popup.selectedAccount && popup.selectedAccount.assets ? popup.selectedAccount.assets : []
defaultToken: Style.png("tokens/DEFAULT-TOKEN@3x")
placeholderText: popup.isBridgeTx ? qsTr("Select token to bridge") : qsTr("Select token to send")
placeholderText: qsTr("Select token")
getCurrencyBalanceString: function (currencyBalance) {
return "%1 %2".arg(Utils.toLocaleString(currencyBalance.toFixed(2), popup.store.locale, {"currency": true})).arg(popup.store.currentCurrency.toUpperCase())
}
@ -260,59 +230,48 @@ StatusDialog {
return ""
}
onSelectedAssetChanged: {
if (!assetSelector.selectedAsset) {
return
}
if (amountToSendInput.text === "" || isNaN(amountToSendInput.text)) {
if (!assetSelector.selectedAsset || !!amountToSendInput.input.text || isNaN(amountToSendInput.input.text)) {
return
}
popup.recalculateRoutesAndFees()
}
}
}
StatusInput {
id: txtFiatBalance
anchors.left: parent.left
anchors.leftMargin: -12
leftPadding: 0
rightPadding: 0
font.weight: Font.Medium
font.pixelSize: 13
input.placeholderFont.pixelSize: 13
input.leftPadding: 0
input.rightPadding: 0
input.topPadding: 0
input.bottomPadding: 0
input.edit.padding: 0
input.background.color: "transparent"
input.background.border.width: 0
input.edit.color: txtFiatBalance.input.edit.activeFocus ? Theme.palette.directColor1 : Theme.palette.baseColor1
input.edit.readOnly: true
text: {
if(!!assetSelector.selectedAsset) {
let fiatValue = popup.store.getFiatValue(amountToSendInput.text, assetSelector.selectedAsset.symbol, popup.store.currentCurrency)
return parseFloat(fiatValue) === 0 ? LocaleUtils.numberToLocaleString(parseFloat(fiatValue), 2) : LocaleUtils.numberToLocaleString(parseFloat(fiatValue))
}
return LocaleUtils.numberToLocaleString(0, 2)
}
input.implicitHeight: Style.current.bigPadding
implicitWidth: txtFiatBalance.input.edit.contentWidth + 50
input.rightComponent: StatusBaseText {
id: currencyText
text: popup.store.currentCurrency.toUpperCase()
font.pixelSize: 13
color: Theme.palette.directColor5
}
Keys.onReleased: {
let balance = txtFiatBalance.text.trim()
if (balance === "" || isNaN(balance)) {
return
}
// To-Do Not refactored yet
// amountToSendInput.text = root.getCryptoValue(balance, popup.store.currentCurrency, assetSelector.selectedAsset.symbol)
StatusListItemTag {
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.preferredHeight: 22
title: d.maxFiatBalance > 0 ? qsTr("Max: %1").arg(LocaleUtils.numberToLocaleString(d.maxFiatBalance)) : qsTr("No balances active")
closeButtonVisible: false
titleText.font.pixelSize: 12
bgColor: d.errorMode ? Theme.palette.dangerColor2 : Theme.palette.primaryColor3
titleText.color: d.errorMode ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1
}
}
RowLayout {
width: parent.width
AmountToSend {
id: amountToSendInput
Layout.fillWidth:true
isBridgeTx: popup.isBridgeTx
interactive: popup.interactive
store: popup.store
selectedAsset: assetSelector.selectedAsset
errorMode: d.errorMode
maxFiatBalance: d.maxFiatBalance
onReCalculateSuggestedRoute: popup.recalculateRoutesAndFees()
}
AmountToReceive {
id: amountToReceive
Layout.alignment: Qt.AlignRight
Layout.fillWidth:true
visible: popup.bestRoutes !== undefined && popup.bestRoutes.length > 0
store: popup.store
isLoading: popup.isLoading
selectedAsset: assetSelector.selectedAsset
isBridgeTx: popup.isBridgeTx
amountToReceive: d.totalAmountToReceive
cryptoFiatFlipped: amountToSendInput.cryptoFiatFlipped
}
}
TokenListView {
id: tokenListRect
anchors.left: parent.left
@ -336,7 +295,7 @@ StatusDialog {
StatusScrollView {
id: scrollView
topPadding: 0
topPadding: 12
Layout.fillHeight: true
Layout.preferredWidth: parent.width
contentHeight: layout.height + Style.current.padding
@ -441,7 +400,7 @@ StatusDialog {
store: popup.store
interactive: popup.interactive
selectedAccount: popup.selectedAccount
amountToSend: isNaN(parseFloat(amountToSendInput.text)) ? 0 : parseFloat(amountToSendInput.text)
amountToSend: isNaN(parseFloat(amountToSendInput.cryptoValueToSend)) ? 0 : parseFloat(amountToSendInput.cryptoValueToSend)
requiredGasInEth:d.totalFeesInEth
selectedAsset: assetSelector.selectedAsset
onReCalculateSuggestedRoute: popup.recalculateRoutesAndFees()
@ -476,7 +435,7 @@ StatusDialog {
maxFiatFees: popup.isLoading ? "..." : "%1 %2".arg(LocaleUtils.numberToLocaleString(d.totalFeesInFiat)).arg(popup.store.currentCurrency.toUpperCase())
totalTimeEstimate: popup.isLoading? "..." : d.totalTimeEstimate
pending: d.isPendingTx || popup.isLoading
visible: d.isReady && !isNaN(amountToSendInput.text) && fees.isValid && !d.errorMode
visible: d.isReady && !isNaN(amountToSendInput.cryptoValueToSend) && fees.isValid && !d.errorMode
onNextButtonClicked: popup.sendTransaction()
}
@ -499,6 +458,8 @@ StatusDialog {
d.totalFeesInEth = gasTimeEstimate.totalFeesInEth
d.totalFeesInFiat = parseFloat(popup.store.getFiatValue( gasTimeEstimate.totalFeesInEth, "ETH", popup.store.currentCurrency)) +
parseFloat(popup.store.getFiatValue(gasTimeEstimate.totalTokenFees, fees.selectedTokenSymbol, popup.store.currentCurrency))
d.totalAmountToReceive = popup.store.getWei2Eth(response.suggestedRoutes.amountToReceive, assetSelector.selectedAsset.decimals)
networkSelector.toNetworksList = response.suggestedRoutes.toNetworks
popup.isLoading = false
}
}

View File

@ -69,6 +69,10 @@ QtObject {
return profileSectionStore.ensUsernamesStore.getFiatValue(balance, cryptoSymbol, fiatSymbol)
}
function getCryptoValue(balance, cryptoSymbol, fiatSymbol) {
return profileSectionStore.ensUsernamesStore.getCryptoValue(balance, cryptoSymbol, fiatSymbol)
}
function getGasEthValue(gweiValue, gasLimit) {
return profileSectionStore.ensUsernamesStore.getGasEthValue(gweiValue, gasLimit)
}

View File

@ -0,0 +1,77 @@
import QtQuick 2.13
import QtQuick.Layouts 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
ColumnLayout {
id: root
property var store
property var selectedAsset
property bool isLoading: false
property string amountToReceive
property bool isBridgeTx: false
property bool cryptoFiatFlipped: false
QtObject {
id: d
function formatValue(value) {
const precision = (value === 0 ? 2 : 0)
return LocaleUtils.numberToLocaleString(value, precision)
}
readonly property string fiatValue: {
if(!root.selectedAsset || !amountToReceive)
return formatValue(0)
let cryptoValue = root.store.getFiatValue(amountToReceive, root.selectedAsset.symbol, root.store.currentCurrency)
return formatValue(parseFloat(cryptoValue))
}
}
StatusBaseText {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
text: root.isBridgeTx ? qsTr("Amount Bridged") : qsTr("Recipient will get")
font.pixelSize: 13
lineHeight: 18
lineHeightMode: Text.FixedHeight
color: Theme.palette.directColor1
}
RowLayout {
Layout.alignment: Qt.AlignRight
Layout.preferredHeight: 42
StatusBaseText {
id: amountToReceiveText
Layout.alignment: Qt.AlignVCenter
text: isLoading ? "..." : cryptoFiatFlipped ? d.fiatValue: amountToReceive
font.pixelSize: Utils.getFontSizeBasedOnLetterCount(text)
color: Theme.palette.directColor1
}
StatusBaseText {
Layout.alignment: Qt.AlignVCenter
text: isLoading ? "..." : root.store.currentCurrency.toUpperCase()
font.pixelSize: amountToReceiveText.font.pixelSize
color: Theme.palette.directColor1
visible: cryptoFiatFlipped
}
}
RowLayout {
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
StatusBaseText {
id: txtFiatBalance
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
text: isLoading ? "..." : cryptoFiatFlipped ? amountToReceive : d.fiatValue
font.pixelSize: 13
color: Theme.palette.directColor5
}
StatusBaseText {
Layout.alignment: Qt.AlignTop
Layout.leftMargin: 4
text: isLoading ? "..." : !cryptoFiatFlipped ? root.store.currentCurrency.toUpperCase() : !!root.selectedAsset ? root.selectedAsset.symbol.toUpperCase() : ""
font.pixelSize: 13
color: Theme.palette.directColor5
}
}
}

View File

@ -0,0 +1,145 @@
import QtQuick 2.13
import QtQuick.Layouts 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls.Validators 0.1
import "../controls"
import utils 1.0
ColumnLayout {
id: root
property alias input: amountToSendInput
property alias floatValidator: floatValidator
property var store
property var selectedAsset
property bool errorMode
property bool isBridgeTx: false
property bool interactive: false
property double maxFiatBalance
property bool cryptoFiatFlipped: false
property string cryptoValueToSend: !cryptoFiatFlipped ? amountToSendInput.text : txtFiatBalance.text
signal reCalculateSuggestedRoute()
QtObject {
id: d
readonly property string zeroString: formatValue(0, 2)
property Timer waitTimer: Timer {
interval: 1000
onTriggered: reCalculateSuggestedRoute()
}
function formatValue(value, precision) {
const precisionVal = !!precision ? precision : (value === 0 ? 2 : 0)
return LocaleUtils.numberToLocaleString(value, precisionVal)
}
function getFiatValue(value) {
if(!root.selectedAsset || !value)
return zeroString
let cryptoValue = root.store.getFiatValue(value, root.selectedAsset.symbol, root.store.currentCurrency)
return formatValue(parseFloat(cryptoValue))
}
function getCryptoValue(value) {
if(!root.selectedAsset || !value)
return zeroString
let cryptoValue = root.store.getCryptoValue(value, root.selectedAsset.symbol, root.store.currentCurrency)
return formatValue(parseFloat(cryptoValue))
}
}
onSelectedAssetChanged: {
if(!!root.selectedAsset) {
txtFiatBalance.text = !cryptoFiatFlipped ? d.getFiatValue(amountToSendInput.text): d.getCryptoValue(amountToSendInput.text)
}
}
StatusBaseText {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
text: root.isBridgeTx ? qsTr("Amount to bridge") : qsTr("Amount to send")
font.pixelSize: 13
lineHeight: 18
lineHeightMode: Text.FixedHeight
color: Theme.palette.directColor1
}
RowLayout {
Layout.alignment: Qt.AlignLeft
AmountInputWithCursor {
id: amountToSendInput
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.maximumWidth: 163
Layout.preferredWidth: (!!text) ? input.edit.paintedWidth : textMetrics.advanceWidth
placeholderText: d.zeroString
input.edit.color: root.errorMode ? Theme.palette.dangerColor1 : Theme.palette.directColor1
input.edit.readOnly: !root.interactive
validators: [
StatusFloatValidator {
id: floatValidator
bottom: 0
top: root.maxFiatBalance
errorMessage: ""
}
]
TextMetrics {
id: textMetrics
text: amountToSendInput.placeholderText
font: amountToSendInput.input.placeholder.font
}
Keys.onReleased: {
const amount = amountToSendInput.text.trim()
if (!Utils.containsOnlyDigits(amount) || isNaN(amount)) {
return
}
txtFiatBalance.text = !cryptoFiatFlipped ? d.getFiatValue(amount): d.getCryptoValue(amount)
d.waitTimer.restart()
}
}
StatusBaseText {
Layout.alignment: Qt.AlignVCenter
text: root.store.currentCurrency.toUpperCase()
font.pixelSize: amountToSendInput.input.edit.font.pixelSize
color: Theme.palette.baseColor1
visible: cryptoFiatFlipped
}
}
Item {
id: fiatBalanceLayout
Layout.alignment: Qt.AlignLeft | Qt.AlignBottom
Layout.preferredWidth: txtFiatBalance.width + currencyText.width
Layout.preferredHeight: txtFiatBalance.height
StatusBaseText {
id: txtFiatBalance
anchors.top: parent.top
anchors.left: parent.left
text: d.getFiatValue(amountToSendInput.text)
font.pixelSize: 13
color: Theme.palette.directColor5
}
StatusBaseText {
id: currencyText
anchors.top: parent.top
anchors.left: txtFiatBalance.right
anchors.leftMargin: 4
text: !cryptoFiatFlipped ? root.store.currentCurrency.toUpperCase() : !!root.selectedAsset ? root.selectedAsset.symbol.toUpperCase() : ""
font.pixelSize: 13
color: Theme.palette.directColor5
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
cryptoFiatFlipped = !cryptoFiatFlipped
amountToSendInput.validate()
if(!!amountToSendInput.text) {
const tempVal = Number.fromLocaleString(txtFiatBalance.text)
txtFiatBalance.text = !!amountToSendInput.text ? amountToSendInput.text : d.zeroString
amountToSendInput.text = tempVal
}
}
}
}
}

View File

@ -36,6 +36,7 @@ Rectangle {
Layout.alignment: Qt.AlignTop
radius: 8
asset.name: "fees"
asset.color: Theme.palette.directColor1
}
Column {
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter

View File

@ -28,6 +28,7 @@ Item {
property bool interactive: true
property bool isBridgeTx: false
property bool showUnpreferredNetworks: advancedNetworkRoutingPage.showUnpreferredNetworks
property var toNetworksList: []
signal reCalculateSuggestedRoute()
@ -79,6 +80,7 @@ Item {
selectedAsset: root.selectedAsset
selectedAccount: root.selectedAccount
errorMode: root.errorMode
toNetworksList: root.toNetworksList
weiToEth: function(wei) {
return "%1 %2".arg(LocaleUtils.numberToLocaleString(parseFloat(store.getWei2Eth(wei, selectedAsset.decimals)))).arg(selectedAsset.symbol)
}

View File

@ -37,6 +37,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignTop
radius: 8
asset.name: "flash"
asset.color: Theme.palette.directColor1
}
ColumnLayout {
Layout.alignment: Qt.AlignTop

View File

@ -22,6 +22,7 @@ RowLayout {
property bool isBridgeTx: false
property var selectedAsset
property var selectedAccount
property var toNetworksList: []
property var weiToEth: function(wei) {}
property var reCalculateSuggestedRoute: function() {}
property bool errorMode: false
@ -31,6 +32,7 @@ RowLayout {
Layout.alignment: Qt.AlignTop
radius: 8
asset.name: "flash"
asset.color: Theme.palette.directColor1
}
ColumnLayout {
Layout.alignment: Qt.AlignTop
@ -61,13 +63,13 @@ RowLayout {
ScrollBar.horizontal.policy: ScrollBar.AsNeeded
clip: true
visible: !root.isLoading ? root.isBridgeTx ? true : root.bestRoutes !== undefined ? root.bestRoutes.length > 0 : true : false
Row {
Column {
id: row
spacing: Style.current.padding
Repeater {
id: repeater
objectName: "networksList"
model: isBridgeTx ? store.allNetworks : root.bestRoutes
model: isBridgeTx ? store.allNetworks : root.toNetworksList
delegate: isBridgeTx ? networkItem : routeItem
}
}
@ -89,15 +91,15 @@ RowLayout {
Component {
id: routeItem
StatusListItem {
objectName: modelData.toNetwork.chainName
objectName: modelData.chainName
leftPadding: 5
rightPadding: 5
implicitWidth: 126
title: modelData.toNetwork.chainName
implicitWidth: 410
title: modelData.chainName
subTitle: {
let index = store.lockedInAmounts.findIndex(lockedItem => lockedItem !== undefined && lockedItem.chainID === modelData.toNetwork.chainId)
let index = store.lockedInAmounts.findIndex(lockedItem => lockedItem !== undefined && lockedItem.chainID === modelData.chainId)
if(!root.errorMode || index === -1)
return root.weiToEth(modelData.amountIn)
return root.weiToEth(modelData.amountOut)
else {
return root.weiToEth(parseInt(store.lockedInAmounts[index].value, 16))
}
@ -105,7 +107,7 @@ RowLayout {
statusListItemSubTitle.color: root.errorMode ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1
asset.width: 32
asset.height: 32
asset.name: Style.svg("tiny/" + modelData.toNetwork.iconUrl)
asset.name: Style.svg("tiny/" + modelData.iconUrl)
asset.isImage: true
color: "transparent"
}

View File

@ -800,6 +800,17 @@ QtObject {
}
}
function getFontSizeBasedOnLetterCount(text) {
if(text.length >= 12)
return 18
if(text.length >= 10)
return 24
if(text.length > 6)
return 28
else
return 34
}
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
function isPunct(c) {
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)