feat(@desktop/wallet): Implement Network selection in SwapModal

fixes #14750
This commit is contained in:
Khushboo Mehta 2024-05-29 17:42:26 +02:00 committed by Khushboo-dev-cpp
parent c016333aad
commit 3f77a16317
10 changed files with 245 additions and 80 deletions

View File

@ -32,6 +32,16 @@ SplitView {
sourceModel: d.flatNetworksModel
filters: ValueFilter { roleName: "isTest"; value: areTestNetworksEnabledCheckbox.checked }
}
function launchPopup() {
swapModal.createObject(root)
}
function getNetwork() {
let selectedChain = -1
if (networksComboBox.model.count > 0 && networksComboBox.currentIndex >= 0) {
selectedChain = ModelUtils.get(networksComboBox.model, networksComboBox.currentIndex, "chainId")
}
return selectedChain
}
}
PopupBackground {
@ -48,50 +58,52 @@ SplitView {
text: "Reopen"
enabled: !swapModal.visible
onClicked: swapModal.open()
onClicked: d.launchPopup()
}
SwapModal {
id: swapModal
visible: true
swapInputParamsForm: SwapInputParamsForm {
selectedAccountIndex: accountComboBox.currentIndex
selectedNetworkChainId: {
if (networksComboBox.model.count > 0 && networksComboBox.currentIndex >= 0) {
return ModelUtils.get(networksComboBox.model, networksComboBox.currentIndex, "chainId")
}
return -1
}
fromTokensKey: {
if (d.tokenBySymbolModel.count > 0) {
return ModelUtils.get(d.tokenBySymbolModel, fromTokenComboBox.currentIndex, "key")
}
return ""
}
fromTokenAmount: swapInput.text
toTokenKey: {
if (d.tokenBySymbolModel.count > 0) {
return ModelUtils.get(d.tokenBySymbolModel, toTokenComboBox.currentIndex, "key")
}
return ""
Component.onCompleted: d.launchPopup()
SwapInputParamsForm {
id: swapInputForm
selectedAccountIndex: accountComboBox.currentIndex
selectedNetworkChainId: d.getNetwork()
fromTokensKey: {
if (d.tokenBySymbolModel.count > 0) {
return ModelUtils.get(d.tokenBySymbolModel, fromTokenComboBox.currentIndex, "key")
}
return ""
}
swapAdaptor: SwapModalAdaptor {
swapStore: SwapStore {
readonly property var accounts: d.accountsModel
readonly property var flatNetworks: d.flatNetworksModel
readonly property bool areTestNetworksEnabled: areTestNetworksEnabledCheckbox.checked
fromTokenAmount: swapInput.text
toTokenKey: {
if (d.tokenBySymbolModel.count > 0) {
return ModelUtils.get(d.tokenBySymbolModel, toTokenComboBox.currentIndex, "key")
}
walletAssetsStore: WalletAssetsStore {
id: thisWalletAssetStore
walletTokensStore: TokensStore {
readonly property var plainTokensBySymbolModel: TokensBySymbolModel {}
return ""
}
}
Component {
id: swapModal
SwapModal {
visible: true
swapInputParamsForm: swapInputForm
swapAdaptor: SwapModalAdaptor {
swapStore: SwapStore {
readonly property var accounts: d.accountsModel
readonly property var flatNetworks: d.flatNetworksModel
readonly property bool areTestNetworksEnabled: areTestNetworksEnabledCheckbox.checked
}
walletAssetsStore: WalletAssetsStore {
id: thisWalletAssetStore
walletTokensStore: TokensStore {
readonly property var plainTokensBySymbolModel: TokensBySymbolModel {}
}
readonly property var baseGroupedAccountAssetModel: GroupedAccountsAssetsModel {}
assetsWithFilteredBalances: thisWalletAssetStore.groupedAccountsAssetsModel
}
currencyStore: CurrenciesStore {}
swapFormData: swapInputForm
}
readonly property var baseGroupedAccountAssetModel: GroupedAccountsAssetsModel {}
assetsWithFilteredBalances: thisWalletAssetStore.groupedAccountsAssetsModel
}
currencyStore: CurrenciesStore {}
swapFormData: swapModal.swapInputParamsForm
}
}
}
@ -129,7 +141,7 @@ SplitView {
}
currentIndex: 0
onCurrentIndexChanged: {
swapModal.swapInputParamsForm.selectedAccountIndex = currentIndex
swapInputForm.selectedAccountIndex = currentIndex
}
}
@ -142,6 +154,7 @@ SplitView {
model: d.filteredNetworksModel
currentIndex: 0
onCountChanged: currentIndex = 0
onCurrentIndexChanged: swapInputForm.selectedNetworkChainId = d.getNetwork()
}
StatusBaseText {

View File

@ -80,7 +80,7 @@ Item {
function launchAccountSelectionPopup(accountsModalHeader) {
// Launch account selection popup
verify(!accountsModalHeader.control.popup.opened)
mouseClick(accountsModalHeader, Qt.LeftButton)
mouseClick(accountsModalHeader)
waitForRendering(accountsModalHeader)
verify(!!accountsModalHeader.control.popup.opened)
return accountsModalHeader
@ -206,14 +206,13 @@ Item {
for(let i =0; i< comboBoxList.model.count; i++) {
let delegateUnderTest = comboBoxList.itemAtIndex(i)
verify(!delegateUnderTest.modelData.fromToken)
verify(!delegateUnderTest.modelData.accountBalance)
}
// close account selection dropdown
accountsModalHeader.control.popup.close()
// set network chainId and fromTokensKey and verify balances in account selection dropdown
root.swapFormData.selectedNetworkChainId = root.swapAdaptor.__filteredFlatNetworksModel.get(0).chainId
root.swapFormData.selectedNetworkChainId = root.swapAdaptor.filteredFlatNetworksModel.get(0).chainId
root.swapFormData.fromTokensKey = root.swapAdaptor.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel.get(0).key
compare(controlUnderTest.swapInputParamsForm.selectedNetworkChainId, root.swapFormData.selectedNetworkChainId)
compare(controlUnderTest.swapInputParamsForm.fromTokensKey, root.swapFormData.fromTokensKey)
@ -257,7 +256,7 @@ Item {
let delegateUnderTest = comboBoxList.itemAtIndex(i)
mouseClick(delegateUnderTest, Qt.LeftButton)
mouseClick(delegateUnderTest)
waitForRendering(delegateUnderTest)
verify(accountsModalHeader.control.popup.closed)
@ -279,5 +278,111 @@ Item {
}
closeAndVerfyModal()
}
function test_network_default_and_selection() {
// Launch popup
launchAndVerfyModal()
// get network comboBox
const networkComboBox = findChild(controlUnderTest, "networkFilter")
verify(!!networkComboBox)
// check default value of network comboBox, should be mainnet
compare(root.swapFormData.selectedNetworkChainId, -1)
const networkComboBoxText = findChild(networkComboBox.control.contentItem, "contentItemText")
verify(!!networkComboBoxText)
compare(networkComboBoxText.text, root.swapAdaptor.filteredFlatNetworksModel.get(0).chainName)
// lets ensure that the selected one is correctly set
for (let i=0; i<networkComboBox.control.popup.contentItem.count; i++) {
// launch network selection popup
verify(!networkComboBox.control.popup.opened)
mouseClick(networkComboBox)
verify(networkComboBox.control.popup.opened)
let delegateUnderTest = networkComboBox.control.popup.contentItem.itemAtIndex(i)
verify(!!delegateUnderTest)
// if you try selecting an item already selected it doesnt do anything
if(networkComboBox.control.popup.contentItem.currentIndex === i) {
mouseClick(networkComboBox)
} else {
// select item
mouseClick(delegateUnderTest)
// verify values set
verify(!networkComboBox.control.popup.opened)
compare(root.swapFormData.selectedNetworkChainId, networkComboBox.control.popup.contentItem.model.get(i).chainId)
compare(networkComboBoxText.text, root.swapAdaptor.filteredFlatNetworksModel.get(i).chainName)
}
}
networkComboBox.control.popup.close()
closeAndVerfyModal()
}
function test_network_and_account_header_items() {
root.swapFormData.fromTokensKey = root.swapAdaptor.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel.get(0).key
// Launch popup
launchAndVerfyModal()
// get network comboBox
const networkComboBox = findChild(controlUnderTest, "networkFilter")
verify(!!networkComboBox)
for (let i=0; i<networkComboBox.control.popup.contentItem.count; i++) {
// launch network selection popup
verify(!networkComboBox.control.popup.opened)
mouseClick(networkComboBox)
verify(networkComboBox.control.popup.opened)
let delegateUnderTest = networkComboBox.control.popup.contentItem.itemAtIndex(i)
verify(!!delegateUnderTest)
let networkModelItem = networkComboBox.control.popup.contentItem.model.get(i)
// if you try selecting an item already selected it doesnt do anything
if(networkComboBox.control.popup.contentItem.currentIndex === i) {
mouseClick(networkComboBox)
root.swapFormData.selectedNetworkChainId = networkModelItem.chainId
} else {
// select item
mouseClick(delegateUnderTest)
}
// verify values in accouns modal header dropdown
const accountsModalHeader = getAndVerifyAccountsModalHeader()
launchAccountSelectionPopup(accountsModalHeader)
const comboBoxList = findChild(accountsModalHeader, "accountSelectorList")
verify(!!comboBoxList)
for(let j =0; j< comboBoxList.model.count; j++) {
let accountDelegateUnderTest = comboBoxList.itemAtIndex(j)
verify(!!accountDelegateUnderTest)
const inlineTagDelegate_0 = findChild(accountDelegateUnderTest, "inlineTagDelegate_0")
verify(!!inlineTagDelegate_0)
compare(inlineTagDelegate_0.asset.name, Style.svg("tiny/%1".arg(networkModelItem.iconUrl)))
compare(inlineTagDelegate_0.asset.color.toString().toUpperCase(), networkModelItem.chainColor.toString().toUpperCase())
let balancesModel = SQUtils.ModelUtils.getByKey(root.swapAdaptor.walletAssetsStore.baseGroupedAccountAssetModel, "tokensKey", root.swapFormData.fromTokensKey).balances
verify(!!balancesModel)
let filteredBalances = SQUtils.ModelUtils.modelToArray(balancesModel).filter(balances => balances.chainId === root.swapFormData.selectedNetworkChainId).filter(balances => balances.account === accountDelegateUnderTest.modelData.address)
verify(!!filteredBalances)
let accountBalance = filteredBalances.length > 0 ? filteredBalances[0]: { balance: "0", iconUrl: networkModelItem.iconUrl, chainColor: networkModelItem.chainColor}
verify(!!accountBalance)
let fromToken = SQUtils.ModelUtils.getByKey(root.swapAdaptor.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel, "key", root.swapFormData.fromTokensKey)
verify(!!fromToken)
let bigIntBalance = SQUtils.AmountsArithmetic.toNumber(accountBalance.balance, fromToken.decimals)
compare(inlineTagDelegate_0.title, root.swapAdaptor.formatCurrencyAmount(bigIntBalance, fromToken.symbol))
}
// close account selection dropdown
accountsModalHeader.control.popup.close()
}
root.swapFormData.selectedNetworkChainId = -1
networkComboBox.control.popup.close()
closeAndVerfyModal()
}
}
}

View File

@ -6,7 +6,9 @@ ListModel {
tokensKey: "DAI",
balances: [
{ account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", chainId: 5, balance: "0" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 5, balance: "123456789123456789" }
{ account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", chainId: 11155111, balance: "0" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 5, balance: "123456789123456789" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 11155111, balance: "123456789123456789" }
]
},
{
@ -15,6 +17,7 @@ ListModel {
{ account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", chainId: 420, balance: "1013151281976507736" },
{ account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", chainId: 421613, balance: "473057568699284613" },
{ account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", chainId: 5, balance: "307400931315122839" },
{ account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", chainId: 11155111, balance: "307400931315122839" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 420, balance: "122082928968121891" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 421613, balance: "0" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 5, balance: "559133758939097000" }
@ -24,7 +27,10 @@ ListModel {
tokensKey: "STT",
balances: [
{ account: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240", chainId: 5, balance: "999999999998998500000000000016777216" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 5, balance: "1077000000000000000000" }
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 5, balance: "1077000000000000000000" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 420, balance: "122082928968121891" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 421613, balance: "222000000000000000" },
{ account: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881", chainId: 11155111, balance: "559133758939097000" }
]
},
{

View File

@ -217,6 +217,7 @@ Item {
})
onLaunchSwapModal: {
d.swapFormData.selectedAccountIndex = d.selectedAccountIndex
d.swapFormData.selectedNetworkChainId = StatusQUtils.ModelUtils.getByKey(RootStore.filteredFlatModel, "layer", 1, "chainId")
d.swapFormData.fromTokensKey = tokensKey
Global.openSwapModalRequested(d.swapFormData)
}
@ -334,6 +335,7 @@ Item {
onLaunchSwapModal: {
d.swapFormData.fromTokensKey = ""
d.swapFormData.selectedAccountIndex = d.selectedAccountIndex
d.swapFormData.selectedNetworkChainId = StatusQUtils.ModelUtils.getByKey(RootStore.filteredFlatModel, "layer", 1, "chainId")
if(!!walletStore.currentViewedHoldingTokensKey && walletStore.currentViewedHoldingType === Constants.TokenType.ERC20) {
d.swapFormData.fromTokensKey = walletStore.currentViewedHoldingTokensKey
}

View File

@ -95,6 +95,7 @@ StatusComboBox {
visible: active
}
StatusBaseText {
objectName: "contentItemText"
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
font.pixelSize: Style.current.additionalTextSize

View File

@ -10,6 +10,8 @@ import StatusQ.Popups.Dialog 0.1
import shared.popups.send.controls 1.0
import AppLayouts.Wallet.controls 1.0
StatusDialog {
id: root
@ -19,16 +21,13 @@ StatusDialog {
required property SwapModalAdaptor swapAdaptor
objectName: "swapModal"
title: qsTr("Swap")
bottomPadding: 16
padding: 0
background: StatusDialogBackground {
implicitHeight: 846
implicitWidth: 556
color: Theme.palette.baseColor3
}
implicitWidth: 556
topPadding: 0
bottomPadding: Style.current.padding
leftPadding: Style.current.xlPadding
rightPadding: Style.current.xlPadding
backgroundColor: Theme.palette.baseColor3
header: AccountsModalHeader {
anchors.top: parent.top
@ -45,28 +44,57 @@ StatusDialog {
}
}
// This is a temporary placeholder while each of the components are being added.
contentItem: Column {
contentItem: ColumnLayout {
spacing: 5
RowLayout {
Layout.fillWidth: true
spacing: 12
HeaderTitleText {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
id: modalHeader
text: qsTr("Swap")
}
StatusBaseText {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
text: qsTr("On:")
color: Theme.palette.baseColor1
font.pixelSize: 13
lineHeight: 38
lineHeightMode: Text.FixedHeight
verticalAlignment: Text.AlignVCenter
}
// TODO: update this once https://github.com/status-im/status-desktop/issues/14780 is ready
NetworkFilter {
id: networkFilter
objectName: "networkFilter"
Layout.alignment: Qt.AlignVCenter
multiSelection: false
flatNetworks: root.swapAdaptor.filteredFlatNetworksModel
onToggleNetwork: (network) => {
root.swapInputParamsForm.selectedNetworkChainId = network.chainId
}
Component.onCompleted: {
if(root.swapInputParamsForm.selectedNetworkChainId !== -1)
networkFilter.setChain(root.swapInputParamsForm.selectedNetworkChainId)
}
}
}
// This is a temporary placeholder while each of the components are being added.
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
topPadding: Style.current.padding
text: qsTr("This area is a temporary placeholder")
font.bold: true
}
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Selected network: %1").arg(swapInputParamsForm.selectedNetworkChainId)
}
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Selected from token: %1").arg(swapInputParamsForm.fromTokensKey)
}
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
text: qsTr("from token amount: %1").arg(swapInputParamsForm.fromTokenAmount)
}
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Selected to token: %1").arg(swapInputParamsForm.toTokenKey)
}
}

View File

@ -38,11 +38,16 @@ QObject {
]
}
readonly property SortFilterProxyModel filteredFlatNetworksModel: SortFilterProxyModel {
sourceModel: root.swapStore.flatNetworks
filters: ValueFilter { roleName: "isTest"; value: root.swapStore.areTestNetworksEnabled }
}
function getNetworkShortNames(chainIds) {
var networkString = ""
let chainIdsArray = chainIds.split(":")
for (let i = 0; i< chainIdsArray.length; i++) {
let nwShortName = ModelUtils.getByKey(root.__filteredFlatNetworksModel, "chainId", Number(chainIdsArray[i]), "shortName")
let nwShortName = ModelUtils.getByKey(root.filteredFlatNetworksModel, "chainId", Number(chainIdsArray[i]), "shortName")
if(!!nwShortName) {
networkString = networkString + nwShortName + ':'
}
@ -64,10 +69,6 @@ QObject {
// Internal properties and functions -----------------------------------------------------------------------------------------------------------------------------
readonly property var __fromToken: ModelUtils.getByKey(root.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel, "key", root.swapFormData.fromTokensKey)
readonly property SortFilterProxyModel __filteredFlatNetworksModel: SortFilterProxyModel {
sourceModel: root.swapStore.flatNetworks
filters: ValueFilter { roleName: "isTest"; value: root.swapStore.areTestNetworksEnabled }
}
SubmodelProxyModel {
id: filteredBalancesModel
@ -81,7 +82,7 @@ QObject {
}
readonly property LeftJoinModel joinModel: LeftJoinModel {
leftModel: submodel
rightModel: root.__filteredFlatNetworksModel
rightModel: root.filteredFlatNetworksModel
joinRole: "chainId"
}
@ -89,7 +90,7 @@ QObject {
}
function __processAccountBalance(address) {
let network = ModelUtils.getByKey(root.__filteredFlatNetworksModel, "chainId", root.swapFormData.selectedNetworkChainId)
let network = ModelUtils.getByKey(root.filteredFlatNetworksModel, "chainId", root.swapFormData.selectedNetworkChainId)
if(!!network) {
let balancesModel = ModelUtils.getByKey(filteredBalancesModel, "tokensKey", root.swapFormData.fromTokensKey, "balances")
let accountBalance = ModelUtils.getByKey(balancesModel, "account", address)

View File

@ -248,17 +248,12 @@ StatusDialog {
RowLayout {
spacing: 8
Layout.preferredHeight: 44
StatusBaseText {
HeaderTitleText {
id: modalHeader
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
verticalAlignment: Text.AlignVCenter
text: d.isBridgeTx ? qsTr("Bridge") : qsTr("Send")
font.pixelSize: 28
lineHeight: 38
lineHeightMode: Text.FixedHeight
font.letterSpacing: -0.4
color: Theme.palette.directColor1
Layout.maximumWidth: contentWidth
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
text: d.isBridgeTx ? qsTr("Bridge") : qsTr("Send")
}
HoldingSelector {

View File

@ -0,0 +1,13 @@
import QtQuick 2.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
StatusBaseText {
lineHeight: 38
lineHeightMode: Text.FixedHeight
font.pixelSize: 28
font.letterSpacing: -0.4
verticalAlignment: Text.AlignVCenter
color: Theme.palette.directColor1
}

View File

@ -10,3 +10,4 @@ AmountInputWithCursor 1.0 AmountInputWithCursor.qml
BalanceExceeded 1.0 BalanceExceeded.qml
CollectibleBackButtonWithInfo 1.0 CollectibleBackButtonWithInfo.qml
CollectibleNestedDelegate 1.0 CollectibleNestedDelegate.qml
HeaderTitleText 1.0 HeaderTitleText.qml