feat(@desktop/wallet): Implementation of One Time & Recurrent tab in Buy Popup

fixes #14819
This commit is contained in:
Khushboo Mehta 2024-05-27 18:55:43 +02:00 committed by Khushboo-dev-cpp
parent f62b6eb348
commit c9641bb0b9
14 changed files with 341 additions and 84 deletions

View File

@ -0,0 +1,37 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import Storybook 1.0
import Models 1.0
import AppLayouts.Wallet.popups 1.0
SplitView {
id: root
orientation: Qt.Horizontal
PopupBackground {
id: popupBg
SplitView.fillWidth: true
SplitView.fillHeight: true
Button {
id: reopenButton
anchors.centerIn: parent
text: "Reopen"
enabled: !buySellModal.visible
onClicked: buySellModal.open()
}
BuyCryptoModal {
id: buySellModal
visible: true
onRampProvidersModel: OnRampProvidersModel{}
}
}
}
// category: Popups

View File

@ -0,0 +1,146 @@
import QtQuick 2.15
import QtTest 1.15
import StatusQ.Core.Theme 0.1
import Models 1.0
import utils 1.0
import AppLayouts.Wallet.popups 1.0
Item {
id: root
width: 600
height: 800
OnRampProvidersModel{
id: onRampProvidersModal
}
Component {
id: componentUnderTest
BuyCryptoModal {
onRampProvidersModel: onRampProvidersModal
onClosed: destroy()
}
}
SignalSpy {
id: notificationSpy
target: Global
signalName: "openLinkWithConfirmation"
}
TestCase {
name: "BuyCryptoModal"
when: windowShown
property BuyCryptoModal controlUnderTest: null
function init() {
controlUnderTest = createTemporaryObject(componentUnderTest, root)
}
function launchPopup() {
verify(!!controlUnderTest)
controlUnderTest.open()
verify(!!controlUnderTest.opened)
}
function test_launchAndCloseModal() {
launchPopup()
// close popup
controlUnderTest.close()
verify(!controlUnderTest.opened)
}
function test_ModalFooter() {
// Launch modal
launchPopup()
// check if footer has Done button and action on button clicked
const footer = findChild(controlUnderTest, "footer")
verify(!!footer)
compare(footer.rightButtons.count, 1)
compare(footer.rightButtons.get(0).text, qsTr("Done"))
mouseClick(footer.rightButtons.get(0), Qt.LeftButton)
// popup should be closed
verify(!controlUnderTest.opened)
}
function test_modalContent() {
// Launch modal
launchPopup()
// find tab bar
const tabBar = findChild(controlUnderTest, "tabBar")
verify(!!tabBar)
// should have 2 items
compare(tabBar.count, 2)
// current index set should be to 0
compare(tabBar.currentIndex, 0)
// item 0 should have text "One time"
compare(tabBar.itemAt(0).text, qsTr("One time"))
// item 1 should have text "Recurrent"
compare(tabBar.itemAt(1).text, qsTr("Recurrent"))
// TODO: this will be implemnted under https://github.com/status-im/status-desktop/issues/14820
// until then this list will be empty
mouseClick(tabBar.itemAt(1), Qt.LeftButton)
compare(tabBar.currentIndex, 1)
const providersList = findChild(controlUnderTest, "providersList")
waitForRendering(providersList)
verify(!!providersList)
compare(providersList.count, 0)
// check data on 1st tab --------------------------------------------------------
mouseClick(tabBar.itemAt(0), Qt.LeftButton)
compare(tabBar.currentIndex, 0)
waitForRendering(providersList)
verify(!!providersList)
// verify that 3 items are listed
compare(providersList.count, 3)
// check if delegate contents are as expected
for(let i =0; i< providersList.count; i++) {
let delegateUnderTest = providersList.itemAtIndex(i)
verify(!!delegateUnderTest)
compare(delegateUnderTest.title, onRampProvidersModal.get(i).name)
compare(delegateUnderTest.subTitle, onRampProvidersModal.get(i).description)
compare(delegateUnderTest.asset.name, onRampProvidersModal.get(i).logoUrl)
const feesText = findChild(delegateUnderTest, "feesText")
verify(!!feesText)
compare(feesText.text, onRampProvidersModal.get(i).fees)
const externalLinkIcon = findChild(delegateUnderTest, "externalLinkIcon")
verify(!!externalLinkIcon)
compare(externalLinkIcon.icon, "tiny/external")
compare(externalLinkIcon.color, Theme.palette.baseColor1)
// Hover over the item and check hovered state
mouseMove(delegateUnderTest, delegateUnderTest.width/2, delegateUnderTest.height/2)
verify(delegateUnderTest.sensor.containsMouse)
compare(externalLinkIcon.color, Theme.palette.directColor1)
verify(delegateUnderTest.color, Theme.palette.baseColor2)
}
// test mouse click
tryCompare(notificationSpy, "count", 0)
mouseClick(providersList.itemAtIndex(0))
tryCompare(notificationSpy, "count", 1)
compare(notificationSpy.signalArguments[0][0],onRampProvidersModal.get(0).siteUrl)
compare(notificationSpy.signalArguments[0][1],onRampProvidersModal.get(0).hostname)
}
}
}

View File

@ -82,4 +82,10 @@ QtObject {
readonly property string ownerTokenInfo: "In order to Mint, Import and Airdrop community tokens, you first need to mint your Owner token which will give you permissions to access the token management features for your community." readonly property string ownerTokenInfo: "In order to Mint, Import and Airdrop community tokens, you first need to mint your Owner token which will give you permissions to access the token management features for your community."
readonly property string airdropInfo: "You can Airdrop tokens to deserving Community members or to give individuals token-based permissions." readonly property string airdropInfo: "You can Airdrop tokens to deserving Community members or to give individuals token-based permissions."
} }
readonly property QtObject onRampProviderImages: QtObject {
readonly property string latamex: Style.png("onRampProviders/latamex")
readonly property string moonPay: Style.png("onRampProviders/moonPay")
readonly property string ramp: Style.png("onRampProviders/ramp")
}
} }

View File

@ -0,0 +1,35 @@
import QtQuick 2.15
ListModel {
readonly property var data: [
{
name: "Ramp",
description: "Global crypto to fiat flow",
fees: "0.49% - 2.9%",
logoUrl: ModelsData.onRampProviderImages.ramp,
siteUrl: "https://ramp.network/buy?hostApiKey=zrtf9u2uqebeyzcs37fu5857tktr3eg9w5tffove&swapAsset=DAI,ETH,USDC,USDT",
hostname: "ramp.network",
recurrentSiteURL: ""
},
{
name: "MoonPay",
description: "The new standard for fiat to crypto",
fees: "1% - 4.5%",
logoUrl: ModelsData.onRampProviderImages.moonPay,
siteUrl: "https://buy.moonpay.com/?apiKey=pk_live_YQC6CQPA5qqDu0unEwHJyAYQyeIqFGR",
hostname: "moonpay.com",
recurrentSiteURL: "https://buy.moonpay.com/?apiKey=pk_live_YQC6CQPA5qqDu0unEwHJyAYQyeIqFGR",
},
{
name: "Latamex",
description: "Easily buy crypto in Argentina, Mexico, and Brazil",
fees: "1% - 1.7%",
logoUrl: ModelsData.onRampProviderImages.latamex,
siteUrl: "https://latamex.com/",
hostname: "latamex.com",
recurrentSiteURL: "",
}
]
Component.onCompleted: append(data)
}

View File

@ -22,6 +22,7 @@ WalletTransactionsModel 1.0 WalletTransactionsModel.qml
GroupedAccountsAssetsModel 1.0 GroupedAccountsAssetsModel.qml GroupedAccountsAssetsModel 1.0 GroupedAccountsAssetsModel.qml
TokensBySymbolModel 1.0 TokensBySymbolModel.qml TokensBySymbolModel 1.0 TokensBySymbolModel.qml
CommunitiesModel 1.0 CommunitiesModel.qml CommunitiesModel 1.0 CommunitiesModel.qml
OnRampProvidersModel 1.0 OnRampProvidersModel.qml
singleton ModelsData 1.0 ModelsData.qml singleton ModelsData 1.0 ModelsData.qml
singleton NetworksModel 1.0 NetworksModel.qml singleton NetworksModel 1.0 NetworksModel.qml

View File

@ -81,9 +81,7 @@ Rectangle {
visible: !root.isCommunityOwnershipTransfer && !root.walletStore.showAllAccounts visible: !root.isCommunityOwnershipTransfer && !root.walletStore.showAllAccounts
icon.name: "token" icon.name: "token"
text: qsTr("Buy") text: qsTr("Buy")
onClicked: function () { onClicked: Global.openBuyCryptoModalRequested()
Global.openPopup(buySellModal);
}
} }
StatusFlatButton { StatusFlatButton {
@ -97,11 +95,6 @@ Rectangle {
onClicked: root.launchSwapModal() onClicked: root.launchSwapModal()
} }
} }
Component {
id: buySellModal
CryptoServicesModal {}
}
} }

View File

@ -0,0 +1,98 @@
import QtQuick 2.14
import QtQuick.Layouts 1.0
import QtQml.Models 2.14
import SortFilterProxyModel 0.2
import StatusQ.Popups.Dialog 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ 0.1
import utils 1.0
StatusDialog {
id: root
required property var onRampProvidersModel
padding: Style.current.xlPadding
implicitWidth: 560
implicitHeight: 436
title: qsTr("Buy assets")
ColumnLayout {
anchors.fill: parent
spacing: 20
StatusSwitchTabBar {
id: tabBar
objectName: "tabBar"
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
StatusSwitchTabButton {
text: qsTr("One time")
}
StatusSwitchTabButton {
text: qsTr("Recurrent")
}
}
StatusListView {
id: providersList
objectName: "providersList"
Layout.fillWidth: true
Layout.fillHeight: true
model: SortFilterProxyModel {
sourceModel: !!root.onRampProvidersModel ? root.onRampProvidersModel : null
// TODO: this temporary and changed under https://github.com/status-im/status-desktop/issues/14820
// when recurrentSiteURL is available
filters: ValueFilter {
enabled: tabBar.currentIndex
roleName: "name"
value: ""
}
}
delegate: StatusListItem {
width: ListView.view.width
title: name
subTitle: description
asset.name: logoUrl
asset.isImage: true
statusListItemSubTitle.maximumLineCount: 1
statusListItemComponentsSlot.spacing: 8
components: [
StatusBaseText {
objectName: "feesText"
text: fees
color: Theme.palette.baseColor1
lineHeight: 24
lineHeightMode: Text.FixedHeight
verticalAlignment: Text.AlignVCenter
},
StatusIcon {
objectName: "externalLinkIcon"
icon: "tiny/external"
color: sensor.containsMouse ? Theme.palette.directColor1: Theme.palette.baseColor1
}
]
onClicked: {
let url = tabBar.currentIndex ? recurrentSiteURL : siteUrl
Global.openLinkWithConfirmation(url, hostname)
root.close()
}
}
}
}
footer: StatusDialogFooter {
objectName: "footer"
rightButtons: ObjectModel {
StatusButton {
text: qsTr("Done")
onClicked: root.close()
}
}
}
}

View File

@ -1,76 +0,0 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import utils 1.0
import shared.panels 1.0
import StatusQ.Popups 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1
import "../controls"
import "../stores"
StatusModal {
id: cryptoServicesPopupRoot
height: 400
headerSettings.title: qsTr("Buy crypto")
anchors.centerIn: parent
Loader {
id: loader
anchors.fill: parent
sourceComponent: servicesComponent
Component {
id: servicesComponent
Item {
anchors.fill: parent
anchors.topMargin: Style.current.padding
anchors.bottomMargin: Style.current.padding
anchors.leftMargin: Style.current.padding
anchors.rightMargin: Style.current.padding
StyledText {
id: note
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
color: Style.current.secondaryText
text: qsTr("Choose a service you'd like to use to buy crypto")
}
StatusListView {
anchors.top: note.bottom
anchors.bottom: parent.bottom
anchors.topMargin: Style.current.padding
anchors.horizontalCenter: parent.horizontalCenter
width: 394
model: RootStore.cryptoRampServicesModel
focus: true
spacing: Style.current.padding
delegate: StatusListItem {
width: parent.width
title: name
subTitle: description
asset.name: logoUrl
asset.isImage: true
label: fees
statusListItemSubTitle.maximumLineCount: 1
components: [
StatusIcon {
icon: "next"
color: Theme.palette.baseColor1
}
]
onClicked: {
Global.openLink(siteUrl);
cryptoServicesPopupRoot.close();
}
}
}
}
}
}
}

View File

@ -6,3 +6,4 @@ ReceiveModal 1.0 ReceiveModal.qml
AddEditSavedAddressPopup 1.0 AddEditSavedAddressPopup.qml AddEditSavedAddressPopup 1.0 AddEditSavedAddressPopup.qml
RemoveSavedAddressPopup 1.0 RemoveSavedAddressPopup.qml RemoveSavedAddressPopup 1.0 RemoveSavedAddressPopup.qml
SavedAddressActivityPopup 1.0 SavedAddressActivityPopup.qml SavedAddressActivityPopup 1.0 SavedAddressActivityPopup.qml
BuyCryptoModal 1.0 BuyCryptoModal.qml

View File

@ -16,6 +16,7 @@ import AppLayouts.Profile.popups 1.0
import AppLayouts.Communities.popups 1.0 import AppLayouts.Communities.popups 1.0
import AppLayouts.Communities.helpers 1.0 import AppLayouts.Communities.helpers 1.0
import AppLayouts.Wallet.popups.swap 1.0 import AppLayouts.Wallet.popups.swap 1.0
import AppLayouts.Wallet.popups 1.0
import AppLayouts.Wallet.stores 1.0 as WalletStore import AppLayouts.Wallet.stores 1.0 as WalletStore
import AppLayouts.Chat.stores 1.0 as ChatStore import AppLayouts.Chat.stores 1.0 as ChatStore
@ -91,6 +92,7 @@ QtObject {
Global.openConfirmHideCollectiblePopup.connect(openConfirmHideCollectiblePopup) Global.openConfirmHideCollectiblePopup.connect(openConfirmHideCollectiblePopup)
Global.openCommunityMemberMessagesPopupRequested.connect(openCommunityMemberMessagesPopup) Global.openCommunityMemberMessagesPopupRequested.connect(openCommunityMemberMessagesPopup)
Global.openSwapModalRequested.connect(openSwapModal) Global.openSwapModalRequested.connect(openSwapModal)
Global.openBuyCryptoModalRequested.connect(openBuyCryptoModal)
} }
property var currentPopup property var currentPopup
@ -390,6 +392,10 @@ QtObject {
openPopup(swapModal, {swapInputParamsForm: parameters}) openPopup(swapModal, {swapInputParamsForm: parameters})
} }
function openBuyCryptoModal() {
openPopup(buyCryptoModal)
}
readonly property list<Component> _components: [ readonly property list<Component> _components: [
Component { Component {
id: removeContactConfirmationDialog id: removeContactConfirmationDialog
@ -1248,6 +1254,13 @@ QtObject {
} }
onClosed: destroy() onClosed: destroy()
} }
},
Component {
id: buyCryptoModal
BuyCryptoModal {
onRampProvidersModel: WalletStore.RootStore.cryptoRampServicesModel
onClosed: destroy()
}
} }
] ]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -107,6 +107,9 @@ QtObject {
// Swap // Swap
signal openSwapModalRequested(var formDataParams) signal openSwapModalRequested(var formDataParams)
// BuyCrypto
signal openBuyCryptoModalRequested()
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
// WalletConnect POC - to remove // WalletConnect POC - to remove
signal popupWalletConnect() signal popupWalletConnect()