feat(@desktop/wallet): Add a temporary recipient selector so that we can quickly unblock other peoples work

This commit is contained in:
Khushboo Mehta 2024-12-07 00:38:38 +01:00
parent 64c3e2efb1
commit 6ecb636ec2
8 changed files with 487 additions and 14 deletions

View File

@ -6,6 +6,7 @@ import SortFilterProxyModel 0.2
import StatusQ 0.1 import StatusQ 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Backpressure 0.1
import Models 1.0 import Models 1.0
import Storybook 1.0 import Storybook 1.0
@ -43,6 +44,22 @@ SplitView {
stripTrailingZeroes: false stripTrailingZeroes: false
}) })
} }
readonly property var savedAddressesModel: ListModel {
Component.onCompleted: {
for (let i = 0; i < 10; i++)
append({
name: "some saved addr name " + i,
ens: [],
address: "0x2B748A02e06B159C7C3E98F5064577B96E55A7b4",
})
append({
name: "some saved ENS name ",
ens: ["me@status.eth"],
address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc4",
})
}
}
} }
PopupBackground { PopupBackground {
@ -77,6 +94,9 @@ SplitView {
collectiblesModel: collectiblesSelectionAdaptor.model collectiblesModel: collectiblesSelectionAdaptor.model
networksModel: d.filteredNetworksModel networksModel: d.filteredNetworksModel
savedAddressesModel: d.savedAddressesModel
recentRecipientsModel: WalletTransactionsModel{}
currentCurrency: "USD" currentCurrency: "USD"
fnFormatCurrencyAmount: function(amount, symbol, options = null, locale = null) { fnFormatCurrencyAmount: function(amount, symbol, options = null, locale = null) {
if (isNaN(amount)) { if (isNaN(amount)) {
@ -86,6 +106,15 @@ SplitView {
return LocaleUtils.currencyAmountToLocaleString(currencyAmount, options, locale) return LocaleUtils.currencyAmountToLocaleString(currencyAmount, options, locale)
} }
fnResolveENS: Backpressure.debounce(root, 500, function (ensName, uuid) {
if (!!ensName && ensName.endsWith(".eth")) {
// return some valid address
simpleSend.ensNameResolved(ensName, "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc4", uuid)
} else {
simpleSend.ensNameResolved(ensName, "", uuid) // invalid
}
})
Binding on selectedAccountAddress { Binding on selectedAccountAddress {
value: accountsCombobox.currentValue ?? "" value: accountsCombobox.currentValue ?? ""
} }
@ -462,7 +491,7 @@ SplitView {
} }
Text { Text {
text: "Accounts Selection" text: "Select an accounts"
} }
ComboBox { ComboBox {
id: accountsCombobox id: accountsCombobox
@ -478,17 +507,13 @@ SplitView {
valueRole: "address" valueRole: "address"
currentIndex: 0 currentIndex: 0
} }
Text {
text: "account selected is: \n"
+ simpleSend.selectedAccountAddress
}
CheckBox { CheckBox {
id: testNetworksCheckbox id: testNetworksCheckbox
text: "are test networks enabled" text: "are test networks enabled"
} }
Text { Text {
text: "Networks Selection" text: "Select a network"
} }
ComboBox { ComboBox {
id: networksCombobox id: networksCombobox
@ -497,12 +522,9 @@ SplitView {
valueRole: "chainId" valueRole: "chainId"
currentIndex: 0 currentIndex: 0
} }
Text {
text: "network selected is: " + simpleSend.selectedChainId
}
Text { Text {
text: "Tokens selection" text: "Select a token"
} }
ComboBox { ComboBox {
id: tokensCombobox id: tokensCombobox
@ -524,9 +546,6 @@ SplitView {
textRole: "name" textRole: "name"
valueRole: "tokensKey" valueRole: "tokensKey"
} }
Text {
text: "token selected is: " + simpleSend.selectedTokenKey
}
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
@ -546,9 +565,39 @@ SplitView {
Text { Text {
text: "amount selected in base unit: " + simpleSend.selectedAmountInBaseUnit text: "amount selected in base unit: " + simpleSend.selectedAmountInBaseUnit
} }
Text {
text: "Select a recipient"
}
RowLayout {
Layout.fillWidth: true
TextField {
id: recipientInput
Layout.preferredWidth: 200
Layout.preferredHeight: 50
}
Button {
text: "update in modal"
onClicked: simpleSend.selectedRecipientAddress = recipientInput.text
}
}
Text {
text: "account selected is: \n"
+ simpleSend.selectedAccountAddress
}
Text {
text: "network selected is: " + simpleSend.selectedChainId
}
Text {
text: "token selected is: " + simpleSend.selectedTokenKey
}
Text { Text {
text: "amount entered is: " + simpleSend.selectedAmount text: "amount entered is: " + simpleSend.selectedAmount
} }
Text {
text: "selected recipient is: \n" + simpleSend.selectedRecipientAddress
}
RolesRenamingModel { RolesRenamingModel {
id: collectiblesKeyModel id: collectiblesKeyModel

View File

@ -0,0 +1,187 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import shared.controls 1.0 as SharedControls
// TODO: remove all files and dependecies with this location once old send modal is removed
import shared.popups.send.controls 1.0
import shared.popups.send 1.0
import utils 1.0
import AppLayouts.Wallet.views 1.0
Rectangle {
id: root
required property var savedAddressesModel
required property var myAccountsModel
required property var recentRecipientsModel
property alias selectedRecipientAddress: recipientInputLoader.selectedRecipientAddress
property alias selectedRecipientType: recipientInputLoader.selectedRecipientType
signal resolveENS(string ensName, string uuid)
function ensNameResolved(resolvedPubKey, resolvedAddress, uuid) {
recipientInputLoader.ensNameResolved(resolvedPubKey, resolvedAddress, uuid)
}
implicitHeight: childrenRect.height
color: Theme.palette.indirectColor1
radius: 8
ColumnLayout {
id: layout
width: parent.width
spacing: 0
RecipientView {
id: recipientInputLoader
Layout.fillWidth: true
savedAddressesModel: root.savedAddressesModel
myAccountsModel: root.myAccountsModel
onResolveENS: root.resolveENS(ensName, uuid)
}
StatusTabBar {
id: recipientTypeTabBar
objectName: "recipientTypeTabBar"
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
Layout.topMargin: 12
StatusTabButton {
width: implicitWidth
objectName: "recentAddressesTab"
text: qsTr("Recent")
}
StatusTabButton {
width: implicitWidth
objectName: "savedAddressesTab"
text: qsTr("Saved")
}
StatusTabButton {
width: implicitWidth
objectName: "myAccountsTab"
text: qsTr("My Accounts")
}
visible: !root.selectedRecipientAddress
}
Repeater {
id: repeater
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
model: {
switch(recipientTypeTabBar.currentIndex) {
case 0:
return recentsObjModel
case 1:
return savedObjModel
case 2:
return myAccountsObjModel
}
}
}
}
DelegateModel {
id: recentsObjModel
model: root.recentRecipientsModel
delegate: StatusListItem {
id: listItem
property var entry: model.activityEntry
property bool isIncoming: entry.txType === Constants.TransactionType.Receive
Layout.fillWidth: true
title: isIncoming ? StatusQUtils.Utils.elideText(entry.sender,6,4) : StatusQUtils.Utils.elideText(entry.recipient,6,4)
subTitle: LocaleUtils.getTimeDifference(new Date(parseInt(entry.timestamp) * 1000), new Date())
statusListItemTitle.elide: Text.ElideMiddle
statusListItemTitle.wrapMode: Text.NoWrap
radius: 0
color: sensor.containsMouse || highlighted ? Theme.palette.baseColor2 : "transparent"
statusListItemComponentsSlot.spacing: 5
components: [
StatusIcon {
id: transferIcon
height: 15
width: 15
color: listItem.isIncoming ? Theme.palette.successColor1 : Theme.palette.dangerColor1
icon: listItem.isIncoming ? "arrow-down" : "arrow-up"
rotation: 45
},
StatusTextWithLoadingState {
font.pixelSize: 15
customColor: Theme.palette.directColor1
text: LocaleUtils.currencyAmountToLocaleString(entry.amountCurrency)
}
]
onClicked: {
root.selectedRecipientType = Helpers.RecipientAddressObjectType.RecentsAddress
let isIncoming = entry.txType === Constants.TransactionType.Receive
let selectedAddress = isIncoming ? entry.sender : entry.recipient
root.selectedRecipientAddress = selectedAddress
}
visible: !root.selectedRecipientAddress
}
}
DelegateModel {
id: savedObjModel
model: root.savedAddressesModel
delegate: SavedAddressListItem {
Layout.fillWidth: true
modelData: model
onClicked: {
root.selectedRecipientType = Helpers.RecipientAddressObjectType.SavedAddress
root.selectedRecipientAddress = modelData.address
}
visible: !root.selectedRecipientAddress
}
}
DelegateModel {
id: myAccountsObjModel
model: root.myAccountsModel
delegate: SharedControls.WalletAccountListItem {
required property var model
Layout.fillWidth: true
name: model.name
address: model.address
emoji: model.emoji
walletColor: Utils.getColorForId(model.colorId)
currencyBalance: model.currencyBalance
walletType: model.walletType
migratedToKeycard: model.migratedToKeycard ?? false
accountBalance: model.accountBalance ?? null
onClicked: {
root.selectedRecipientType = Helpers.RecipientAddressObjectType.Account
root.selectedRecipientAddress = model.address
}
visible: !root.selectedRecipientAddress
}
}
}

View File

@ -14,3 +14,4 @@ TokenSelectorPanel 1.0 TokenSelectorPanel.qml
WalletHeader 1.0 WalletHeader.qml WalletHeader 1.0 WalletHeader.qml
SendModalHeader 1.0 SendModalHeader.qml SendModalHeader 1.0 SendModalHeader.qml
StickySendModalHeader 1.0 StickySendModalHeader.qml StickySendModalHeader 1.0 StickySendModalHeader.qml
RecipientSelectorPanel 1.0 RecipientSelectorPanel.qml

View File

@ -71,6 +71,8 @@ StatusDialog {
Only networks valid as per mainnet/testnet selection Only networks valid as per mainnet/testnet selection
**/ **/
required property var networksModel required property var networksModel
required property var savedAddressesModel
required property var recentRecipientsModel
/** Input property holds currently selected Fiat currency **/ /** Input property holds currently selected Fiat currency **/
required property string currentCurrency required property string currentCurrency
/** Input function to format currency amount to locale string **/ /** Input function to format currency amount to locale string **/
@ -92,6 +94,14 @@ StatusDialog {
e.g. 1000000000000000000 for 1 ETH **/ e.g. 1000000000000000000 for 1 ETH **/
readonly property string selectedAmountInBaseUnit: amountToSend.amount readonly property string selectedAmountInBaseUnit: amountToSend.amount
/** TODO: replace with new and improved recipient selector StatusDateRangePicker
TBD under https://github.com/status-im/status-desktop/issues/16916 **/
property alias selectedRecipientAddress: recipientsPanel.selectedRecipientAddress
required property var fnResolveENS
function ensNameResolved(resolvedPubKey, resolvedAddress, uuid) {
recipientsPanel.ensNameResolved(resolvedPubKey, resolvedAddress, uuid)
}
QtObject { QtObject {
id: d id: d
@ -172,7 +182,6 @@ StatusDialog {
padding: 0 padding: 0
leftPadding: Theme.xlPadding leftPadding: Theme.xlPadding
rightPadding: Theme.xlPadding rightPadding: Theme.xlPadding
bottomPadding: Theme.xlPadding
topMargin: margins + accountSelector.height + Theme.padding topMargin: margins + accountSelector.height + Theme.padding
background: StatusDialogBackground { background: StatusDialogBackground {
@ -345,6 +354,32 @@ StatusDialog {
disabledTextColor: type === StatusBaseButton.Type.Danger ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1 disabledTextColor: type === StatusBaseButton.Type.Danger ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1
} }
} }
/** TODO: replace with new and improved recipient selector TBD under
https://github.com/status-im/status-desktop/issues/16916 **/
ColumnLayout {
spacing: Theme.halfPadding
Layout.fillWidth: true
StatusBaseText {
elide: Text.ElideRight
text: qsTr("To")
font.pixelSize: 15
color: Theme.palette.directColor1
}
RecipientSelectorPanel {
id: recipientsPanel
Layout.fillWidth: true
Layout.fillHeight: true
Layout.bottomMargin: Theme.xlPadding
savedAddressesModel: root.savedAddressesModel
myAccountsModel: root.accountsModel
recentRecipientsModel: root.recentRecipientsModel
onResolveENS: root.fnResolveENS(ensName, uuid)
}
}
} }
} }
} }

View File

@ -0,0 +1,193 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import StatusQ 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Backpressure 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import AppLayouts.Wallet 1.0
import shared.controls 1.0 as SharedControls
import shared.stores.send 1.0
import shared.popups.send.panels 1.0
import shared.popups.send 1.0
import shared.popups.send.controls 1.0
import utils 1.0
Loader {
id: root
required property var savedAddressesModel
required property var myAccountsModel
property string selectedRecipientAddress
property int selectedRecipientType: Helpers.RecipientAddressObjectType.Address
property bool interactive: true
signal resolveENS(string ensName, string uuid)
function ensNameResolved(resolvedPubKey, resolvedAddress, uuid) {
if(uuid !== d.uuid) {
return
}
root.selectedRecipientAddress = resolvedAddress
}
QtObject {
id: d
property bool isValidAddress: true
property bool isBeingEvaluated: false
property string uuid
readonly property var validateInput: Backpressure.debounce(root, 500, function (address) {
d.isValidAddress = Utils.isValidAddress(address)
const isENSName = Utils.isValidEns(address)
if(d.isValidAddress) {
root.selectedRecipientAddress = address
d.isBeingEvaluated = false
}
else if(isENSName) {
d.uuid = Utils.uuid()
return root.resolveENS(address, uuid)
} else {
root.selectedRecipientAddress = ""
d.isBeingEvaluated = false
}
})
readonly property var accountsSelectedEntry: ModelEntry {
sourceModel: root.myAccountsModel
key: "address"
value: root.selectedRecipientAddress
}
readonly property var savedAddrSelectedEntry: ModelEntry {
sourceModel: root.savedAddressesModel
key: "address"
value: root.selectedRecipientAddress
}
function clearValues() {
root.selectedRecipientAddress = ""
root.selectedRecipientType = Helpers.RecipientAddressObjectType.Address
}
}
sourceComponent: root.selectedRecipientType === Helpers.RecipientAddressObjectType.SavedAddress ?
savedAddressRecipient:
root.selectedRecipientType === Helpers.RecipientAddressObjectType.Account ?
myAccountRecipient:
root.selectedRecipientType === Helpers.RecipientAddressObjectType.RecentsAddress ?
recentsRecipient : addressRecipient
Component {
id: savedAddressRecipient
SavedAddressListItem {
implicitWidth: parent.width
modelData: d.savedAddrSelectedEntry.item
radius: 8
clearVisible: true
color: Theme.palette.indirectColor1
sensor.enabled: false
subTitle: {
if(!!modelData) {
if (!!modelData && !!modelData.ens && modelData.ens.length > 0)
return Utils.richColorText(modelData.ens, Theme.palette.directColor1)
else
return StatusQUtils.Utils.elideText(modelData.address,6,4)
}
return ""
}
onCleared: d.clearValues()
}
}
Component {
id: myAccountRecipient
SharedControls.WalletAccountListItem {
id: accountItem
readonly property var modelData: d.accountsSelectedEntry.item
name: !!modelData ? modelData.name : ""
address: !!modelData ? modelData.address : ""
emoji: !!modelData ? modelData.emoji : ""
walletColor: !!modelData ? Utils.getColorForId(modelData.colorId): ""
currencyBalance: !!modelData ? modelData.currencyBalance : ""
walletType: !!modelData ? modelData.walletType : ""
migratedToKeycard: !!modelData ? modelData.migratedToKeycard ?? false : false
accountBalance: !!modelData ? modelData.accountBalance : null
width: parent.width
radius: 8
clearVisible: true
color: Theme.palette.indirectColor1
sensor.enabled: false
subTitle: {
if(!!modelData) {
return StatusQUtils.Utils.elideAndFormatWalletAddress(modelData.address)
}
return ""
}
onCleared: d.clearValues()
}
}
Component {
id: recentsRecipient
SendRecipientInput {
width: parent.width
height: visible ? implicitHeight: 0
interactive: root.interactive
input.edit.enabled: false
input.edit.textFormat: Text.AutoText
text: root.selectedRecipientAddress
onClearClicked: d.clearValues()
}
}
Component {
id: addressRecipient
SendRecipientInput {
function validateInput() {
const plainText = StatusQUtils.StringUtils.plainText(text)
d.isBeingEvaluated = true
d.validateInput(plainText)
}
width: parent.width
height: visible ? implicitHeight: 0
interactive: root.interactive
checkMarkVisible: !d.isBeingEvaluated && d.isValidAddress
loading: d.isBeingEvaluated
input.edit.textFormat: Text.AutoText
text: {
if(!!root.selectedRecipientAddress ) {
return root.selectedRecipientAddress
}
return text
}
onTextChanged: Qt.callLater(() => validateInput())
onClearClicked: {
text = ""
d.clearValues()
}
onValidateInputRequested: Qt.callLater(() => validateInput())
}
}
}

View File

@ -6,3 +6,4 @@ TokenSelectorAssetDelegate 1.0 TokenSelectorAssetDelegate.qml
TokenSelectorCollectibleDelegate 1.0 TokenSelectorCollectibleDelegate.qml TokenSelectorCollectibleDelegate 1.0 TokenSelectorCollectibleDelegate.qml
TokenSelectorSectionDelegate 1.0 TokenSelectorSectionDelegate.qml TokenSelectorSectionDelegate 1.0 TokenSelectorSectionDelegate.qml
AccountContextMenu 1.0 AccountContextMenu.qml AccountContextMenu 1.0 AccountContextMenu.qml
RecipientView 1.0 RecipientView.qml

View File

@ -671,6 +671,8 @@ Item {
fnFormatCurrencyAmount: function(amount, symbol, options = null, locale = null) { fnFormatCurrencyAmount: function(amount, symbol, options = null, locale = null) {
return appMain.currencyStore.formatCurrencyAmount(amount, symbol) return appMain.currencyStore.formatCurrencyAmount(amount, symbol)
} }
savedAddressesModel: WalletStores.RootStore.savedAddresses
recentRecipientsModel: appMain.transactionStore.tempActivityController1Model
Component.onCompleted: { Component.onCompleted: {
// It's requested from many nested places, so as a workaround we use // It's requested from many nested places, so as a workaround we use

View File

@ -88,6 +88,8 @@ QtObject {
required property var showCommunityAssetsInSend required property var showCommunityAssetsInSend
/** required function to format currency amount to locale string **/ /** required function to format currency amount to locale string **/
required property var fnFormatCurrencyAmount required property var fnFormatCurrencyAmount
required property var savedAddressesModel
required property var recentRecipientsModel
function openSend(params = {}) { function openSend(params = {}) {
// TODO remove once simple send is feature complete // TODO remove once simple send is feature complete
@ -221,6 +223,9 @@ QtObject {
collectiblesModel: collectiblesSelectionAdaptor.model collectiblesModel: collectiblesSelectionAdaptor.model
networksModel: root.filteredFlatNetworksModel networksModel: root.filteredFlatNetworksModel
savedAddressesModel: root.savedAddressesModel
recentRecipientsModel: root.recentRecipientsModel
currentCurrency: root.currentCurrency currentCurrency: root.currentCurrency
fnFormatCurrencyAmount: root.fnFormatCurrencyAmount fnFormatCurrencyAmount: root.fnFormatCurrencyAmount