feat(@desktop/wallet): Implements Edit slippage

fixes #14829
This commit is contained in:
Khushboo Mehta 2024-06-04 13:58:37 +02:00 committed by Khushboo-dev-cpp
parent dbfa9b0b39
commit 3010fb58cb
9 changed files with 298 additions and 8 deletions

View File

@ -47,8 +47,6 @@ SplitView {
PopupBackground {
id: popupBg
property var popupIntance: null
SplitView.fillWidth: true
SplitView.fillHeight: true
@ -80,14 +78,19 @@ SplitView {
}
return ""
}
toTokenAmount: swapOutputAmount.text
}
Component {
id: swapModal
SwapModal {
visible: true
modal: false
closePolicy: Popup.CloseOnEscape
swapInputParamsForm: swapInputForm
swapAdaptor: SwapModalAdaptor {
swapProposalLoading: loadingCheckBox.checked
swapProposalReady: swapProposalReadyCheckBox.checked
swapStore: SwapStore {
readonly property var accounts: d.accountsModel
readonly property var flatNetworks: d.flatNetworksModel
@ -183,6 +186,25 @@ SplitView {
model: d.tokenBySymbolModel
currentIndex: 1
}
StatusInput {
id: swapOutputAmount
Layout.preferredWidth: 100
label: "Token amount to receive"
text: "100"
}
CheckBox {
id: loadingCheckBox
text: "swap proposal loading"
checked: false
}
CheckBox {
id: swapProposalReadyCheckBox
text: "swap proposal ready"
checked: false
}
}
}
}

View File

@ -385,5 +385,64 @@ Item {
networkComboBox.control.popup.close()
closeAndVerfyModal()
}
function test_edit_slippage() {
// by default the max slippage button should show no values and the edit button shouldnt be visible
// Launch popup
launchAndVerfyModal()
// test default values for the various footer items for slippage
const maxSlippageText = findChild(controlUnderTest, "maxSlippageText")
verify(!!maxSlippageText)
compare(maxSlippageText.text, qsTr("Max slippage:"))
const maxSlippageValue = findChild(controlUnderTest, "maxSlippageValue")
verify(!!maxSlippageValue)
const editSlippageButton = findChild(controlUnderTest, "editSlippageButton")
verify(!!editSlippageButton)
const editSlippagePanel = findChild(controlUnderTest, "editSlippagePanel")
verify(!!editSlippagePanel)
verify(!editSlippagePanel.visible)
// set swap proposal to ready and check state of the edit slippage buttons and max slippage values
root.swapAdaptor.swapProposalReady = true
compare(maxSlippageValue.text, "%1%".arg(0.5))
verify(editSlippageButton.visible)
// clicking on editSlippageButton should show the edit slippage panel
mouseClick(editSlippageButton)
verify(!editSlippageButton.visible)
verify(editSlippagePanel.visible)
const slippageSelector = findChild(editSlippagePanel, "slippageSelector")
verify(!!slippageSelector)
verify(slippageSelector.valid)
compare(slippageSelector.value, 0.5)
const buttonsRepeater = findChild(slippageSelector, "buttonsRepeater")
verify(!!buttonsRepeater)
waitForRendering(buttonsRepeater)
for(let i =0; i< buttonsRepeater.count; i++) {
let buttonUnderTest = buttonsRepeater.itemAt(i)
verify(!!buttonUnderTest)
// the mouseClick(buttonUnderTest) doesnt seem to work
buttonUnderTest.clicked()
verify(slippageSelector.valid)
compare(slippageSelector.value, buttonUnderTest.value)
compare(maxSlippageValue.text, "%1%".arg(buttonUnderTest.value))
}
const signButton = findChild(controlUnderTest, "signButton")
verify(!!signButton)
verify(signButton.enabled)
}
}
}

View File

@ -7,6 +7,7 @@ Column {
property real topPadding: 0
property real bottomPadding: 0
property alias dividerColor: divider.color
width: 480

View File

@ -0,0 +1,107 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Popups 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import shared.controls 1.0
import utils 1.0
Control {
id: root
property bool loading
property var selectedToToken
property string toTokenAmount
property alias slippageValue: slippageSelector.value
property alias valid: slippageSelector.valid
QtObject {
id: d
readonly property string selectedToTokenSymbol: !!root.selectedToToken && !!root.selectedToToken.symbol ?
root.selectedToToken.symbol : ""
}
horizontalPadding: Style.current.padding
verticalPadding: Style.current.bigPadding
background: Rectangle {
radius: 16
border.width: 1
border.color: Theme.palette.directColor8
color: Theme.palette.indirectColor3
}
contentItem: ColumnLayout {
id: baseLayout
spacing: Style.current.bigPadding
RowLayout {
StatusBaseText {
Layout.fillWidth: true
text: qsTr("Slippage tolerance")
font.weight: Font.Medium
lineHeight: button.implicitHeight
lineHeightMode: Text.FixedHeight
verticalAlignment: Text.AlignVCenter
}
StatusLinkText {
id: button
visible: slippageSelector.isEdited
text: qsTr("Use default")
normalColor: Theme.palette.primaryColor1
onClicked: slippageSelector.reset()
}
}
StatusBaseText {
Layout.fillWidth: true
Layout.topMargin: -12
text: qsTr("Maximum deviation in price due to market volatility and liquidity allowed before the swap is cancelled. (0.5% default).")
wrapMode: Text.Wrap
color: Theme.palette.directColor5
}
/* TODO: error conditions for custom enteries missing will be done under -
https://github.com/status-im/status-desktop/issues/15017 */
SlippageSelector {
id: slippageSelector
objectName: "slippageSelector"
Layout.fillWidth: true
}
StatusModalDivider {
Layout.fillWidth: true
Layout.topMargin: 0
Layout.bottomMargin: -Style.current.smallPadding
dividerColor: Theme.palette.directColor8
}
RowLayout {
spacing: 4
StatusBaseText {
text: qsTr("Receive at least")
font.pixelSize: 13
font.weight: Font.Medium
}
StatusSmartIdenticon {
Layout.preferredWidth: Style.current.padding
Layout.preferredHeight: Style.current.padding
asset.name: !!root.selectedToToken && !!root.selectedToToken.image
? root.selectedToToken.image
: Constants.tokenIcon(d.selectedToTokenSymbol)
asset.isImage: true
}
StatusTextWithLoadingState {
text: {
let amount = parseFloat(root.toTokenAmount)
let percentageAmount = (amount - ((amount/100) * slippageSelector.value))
return ("%1 %2").arg(LocaleUtils.numberToLocaleString(percentageAmount)).arg(d.selectedToTokenSymbol)
}
font.pixelSize: 13
font.weight: Font.Medium
loading: root.loading
}
}
}
}

View File

@ -16,3 +16,4 @@ CollectibleBalanceTag 1.0 CollectibleBalanceTag.qml
ConnectedDappsButton 1.0 ConnectedDappsButton.qml
CollectibleLinksTags 1.0 CollectibleLinksTags.qml
SwapExchangeButton 1.0 SwapExchangeButton.qml
EditSlippagePanel 1.0 EditSlippagePanel.qml

View File

@ -7,7 +7,8 @@ QtObject {
property int selectedAccountIndex: 0
property int selectedNetworkChainId: -1
property string fromTokensKey: ""
property string fromTokenAmount: ""
property string fromTokenAmount: "0"
property string toTokenKey: ""
property string toTokenAmount
property string toTokenAmount: "0"
property double selectedSlippage: 0.5
}

View File

@ -1,12 +1,13 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import utils 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import StatusQ.Popups.Dialog 0.1
import StatusQ.Controls 0.1
import shared.popups.send.controls 1.0
@ -24,11 +25,15 @@ StatusDialog {
implicitWidth: 556
topPadding: 0
bottomPadding: Style.current.padding
bottomPadding: Style.current.xlPadding
leftPadding: Style.current.xlPadding
rightPadding: Style.current.xlPadding
backgroundColor: Theme.palette.baseColor3
Behavior on implicitHeight {
NumberAnimation { duration: 1000; easing.type: Easing.OutExpo; alwaysRunToEnd: true}
}
header: AccountsModalHeader {
anchors.top: parent.top
anchors.topMargin: -height - 18
@ -46,6 +51,7 @@ StatusDialog {
contentItem: ColumnLayout {
spacing: 5
clip: true
RowLayout {
Layout.fillWidth: true
@ -100,7 +106,90 @@ StatusDialog {
text: qsTr("Selected to token: %1").arg(swapInputParamsForm.toTokenKey)
}
StatusBaseText {
text: qsTr("Selected network chainId: %1").arg(swapInputParamsForm.selectedNetworkChainId)
text: qsTr("to token amount: %1").arg(swapInputParamsForm.toTokenAmount)
}
// End temporary placeholders
EditSlippagePanel {
id: editSlippagePanel
objectName: "editSlippagePanel"
Layout.fillWidth: true
Layout.topMargin: Style.current.padding
visible: editSlippageButton.checked
selectedToToken: root.swapAdaptor.toToken
toTokenAmount: root.swapInputParamsForm.toTokenAmount
loading: root.swapAdaptor.swapProposalLoading
onSlippageValueChanged: {
root.swapInputParamsForm.selectedSlippage = slippageValue
}
}
}
footer: StatusDialogFooter {
color: Theme.palette.baseColor3
dropShadowEnabled: true
leftButtons: ObjectModel {
ColumnLayout {
Layout.leftMargin: Style.current.padding
spacing: 0
StatusBaseText {
objectName: "maxSlippageText"
text: qsTr("Max slippage:")
color: Theme.palette.directColor5
font.pixelSize: 15
font.weight: Font.Medium
}
RowLayout {
StatusBaseText {
objectName: "maxSlippageValue"
text: "%1%".arg(LocaleUtils.numberToLocaleString(root.swapInputParamsForm.selectedSlippage))
color: Theme.palette.directColor4
font.pixelSize: 15
font.weight: Font.Medium
}
StatusFlatButton {
id: editSlippageButton
objectName: "editSlippageButton"
checkable: true
checked: false
icon.name: "edit_pencil"
textColor: editSlippageButton.hovered ? Theme.palette.directColor1 : Theme.palette.directColor5
size: StatusBaseButton.Size.Tiny
hoverColor: Theme.palette.transparent
visible: !checked
}
}
}
}
rightButtons: ObjectModel {
RowLayout {
Layout.rightMargin: Style.current.padding
spacing: Style.current.bigPadding
ColumnLayout {
StatusBaseText {
text:qsTr("Max fees:")
color: Theme.palette.directColor5
font.pixelSize: 15
font.weight: Font.Medium
}
StatusTextWithLoadingState {
text: loading ? Constants.dummyText : "--"
customColor: Theme.palette.directColor4
font.pixelSize: 15
font.weight: Font.Medium
loading: root.swapAdaptor.swapProposalLoading
}
}
StatusButton {
objectName: "signButton"
/* TODO: there maybe a different icon shown here in case of approval of spending cap
needed TBD under https://github.com/status-im/status-desktop/issues/14833 */
icon.name: "password"
text: qsTr("Swap")
disabledColor: Theme.palette.directColor8
enabled: root.swapAdaptor.swapProposalReady && editSlippagePanel.valid
}
}
}
}
}

View File

@ -17,6 +17,15 @@ QObject {
required property WalletStore.SwapStore swapStore
required property SwapInputParamsForm swapFormData
/* TODO: link to the actually api to get swap proposal from backend under
https://github.com/status-im/status-desktop/issues/14828 */
property bool swapProposalReady: false
property bool swapProposalLoading: false
// To expose the selected from and to Token from the SwapModal
readonly property var fromToken: ModelUtils.getByKey(root.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel, "key", root.swapFormData.fromTokensKey)
readonly property var toToken: ModelUtils.getByKey(root.walletAssetsStore.walletTokensStore.plainTokensBySymbolModel, "key", root.swapFormData.toTokenKey)
readonly property var nonWatchAccounts: SortFilterProxyModel {
sourceModel: root.swapStore.accounts
filters: ValueFilter {
@ -33,7 +42,7 @@ QObject {
},
FastExpressionRole {
name: "fromToken"
expression: root.__fromToken
expression: root.fromToken
}
]
}

View File

@ -10,6 +10,7 @@ Control {
property double value: d.defaultValue
readonly property bool valid: customInput.activeFocus && customInput.valid
|| buttons.value !== null
readonly property bool isEdited: root.value !== d.defaultValue
function reset() {
value = d.defaultValue