feat: Re-enable contact selection in the wallet send dialog
feat: Re-enable contact selection in the wallet send dialog Selecting a ENS-verified contact will resolve it's ENS address so transactions can be sent directly to contacts from the wallet (if and only if they have ENS enabled for their account). feat: add EnsResolver component that shows a loader and allows automated ENS resolution from within QML
This commit is contained in:
parent
44b1d55ebb
commit
5ac4162f82
|
@ -14,15 +14,14 @@ Item {
|
|||
property alias text: inpAddress.text
|
||||
property string selectedAddress
|
||||
property var isValid: false
|
||||
property bool isPending: false
|
||||
readonly property string uuid: Utils.uuid()
|
||||
property alias isPending: ensResolver.isPending
|
||||
property alias readOnly: inpAddress.readOnly
|
||||
property bool isResolvedAddress: false
|
||||
|
||||
height: inpAddress.height
|
||||
|
||||
onSelectedAddressChanged: validate()
|
||||
onTextChanged: resolveEns()
|
||||
onTextChanged: ensResolver.resolveEns(text)
|
||||
|
||||
function resetInternal() {
|
||||
selectedAddress = ""
|
||||
|
@ -33,12 +32,6 @@ Item {
|
|||
isResolvedAddress = false
|
||||
}
|
||||
|
||||
function resolveEns() {
|
||||
if (Utils.isValidEns(text)) {
|
||||
root.validateAsync(text)
|
||||
}
|
||||
}
|
||||
|
||||
function validate() {
|
||||
let isValidEns = Utils.isValidEns(text)
|
||||
let isValidAddress = Utils.isValidAddress(selectedAddress)
|
||||
|
@ -51,26 +44,6 @@ Item {
|
|||
return isValid
|
||||
}
|
||||
|
||||
property var validateAsync: Backpressure.debounce(inpAddress, 600, function (inputValue) {
|
||||
root.isPending = true
|
||||
root.selectedAddress = ""
|
||||
var name = inputValue.startsWith("@") ? inputValue.substring(1) : inputValue
|
||||
walletModel.resolveENS(name, uuid)
|
||||
});
|
||||
|
||||
|
||||
Connections {
|
||||
target: walletModel
|
||||
onEnsWasResolved: {
|
||||
if (uuid !== root.uuid) {
|
||||
return
|
||||
}
|
||||
root.isPending = false
|
||||
root.isResolvedAddress = true
|
||||
root.selectedAddress = resolvedAddress
|
||||
}
|
||||
}
|
||||
|
||||
Input {
|
||||
id: inpAddress
|
||||
//% "eg. 0x1234 or ENS"
|
||||
|
@ -92,7 +65,7 @@ Item {
|
|||
onTextEdited: {
|
||||
metrics.text = text
|
||||
|
||||
resolveEns()
|
||||
ensResolver.resolveEns(text)
|
||||
root.isResolvedAddress = false
|
||||
root.selectedAddress = text
|
||||
}
|
||||
|
@ -117,19 +90,19 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
sourceComponent: loadingIndicator
|
||||
EnsResolver {
|
||||
id: ensResolver
|
||||
anchors.top: inpAddress.bottom
|
||||
anchors.right: inpAddress.right
|
||||
anchors.topMargin: Style.current.halfPadding
|
||||
active: root.isPending
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loadingIndicator
|
||||
LoadingImage {
|
||||
width: 12
|
||||
height: 12
|
||||
onResolved: {
|
||||
root.isResolvedAddress = true
|
||||
root.selectedAddress = resolvedAddress
|
||||
}
|
||||
onIsPendingChanged: {
|
||||
if (isPending) {
|
||||
root.selectedAddress = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,13 @@ Item {
|
|||
property string validationError: qsTrId("please-select-a-contact")
|
||||
property alias validationErrorAlignment: select.validationErrorAlignment
|
||||
property bool isValid: false
|
||||
property alias isPending: ensResolver.isPending
|
||||
property var reset: function() {}
|
||||
property bool readOnly: false
|
||||
property bool isResolvedAddress: false
|
||||
//% "Select a contact"
|
||||
property string selectAContact: qsTrId("select-a-contact")
|
||||
property string noEnsAddressMessage: qsTr("Contact does not have an ENS address. Please send a transaction in chat.")
|
||||
|
||||
function resetInternal() {
|
||||
contacts = undefined
|
||||
|
@ -24,21 +29,40 @@ Item {
|
|||
select.validationError = ""
|
||||
isValid = false
|
||||
readOnly = false
|
||||
isResolvedAddress = false
|
||||
}
|
||||
|
||||
onContactsChanged: {
|
||||
if (root.readOnly) {
|
||||
return
|
||||
}
|
||||
//% "Select a contact"
|
||||
root.selectedContact = { name: qsTrId("select-a-contact") }
|
||||
root.selectedContact = { name: selectAContact }
|
||||
}
|
||||
|
||||
onSelectedContactChanged: validate()
|
||||
onSelectedContactChanged: {
|
||||
if (selectedContact && selectedContact.ensVerified) {
|
||||
root.isResolvedAddress = false
|
||||
ensResolver.resolveEns(selectedContact.name)
|
||||
}
|
||||
validate()
|
||||
}
|
||||
|
||||
function validate() {
|
||||
const isValid = !!selectedContact && Utils.isValidAddress(selectedContact.address)
|
||||
select.validationError = !isValid ? validationError : ""
|
||||
if (!selectedContact) {
|
||||
return root.isValid
|
||||
}
|
||||
let isValidAddress = Utils.isValidAddress(selectedContact.address)
|
||||
let isDefaultValue = selectedContact.name === selectAContact
|
||||
let isValid = (selectedContact.ensVerified && isValidAddress) || isPending || isValidAddress
|
||||
select.validationError = ""
|
||||
if (!isValid && !isDefaultValue &&
|
||||
(
|
||||
!selectedContact.ensVerified ||
|
||||
(selectedContact.ensVerified && isResolvedAddress)
|
||||
)
|
||||
) {
|
||||
select.validationError = !selectedContact.ensVerified ? noEnsAddressMessage : validationError
|
||||
}
|
||||
root.isValid = isValid
|
||||
return isValid
|
||||
}
|
||||
|
@ -109,6 +133,23 @@ Item {
|
|||
menu.width: dropdownWidth
|
||||
}
|
||||
|
||||
EnsResolver {
|
||||
id: ensResolver
|
||||
anchors.top: select.bottom
|
||||
anchors.right: select.right
|
||||
anchors.topMargin: Style.current.halfPadding
|
||||
onResolved: {
|
||||
root.isResolvedAddress = true
|
||||
root.selectedContact.address = resolvedAddress
|
||||
validate()
|
||||
}
|
||||
onIsPendingChanged: {
|
||||
if (isPending) {
|
||||
root.selectedContact.address = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: menuItem
|
||||
MenuItem {
|
||||
|
@ -187,7 +228,6 @@ Item {
|
|||
onClicked: {
|
||||
root.selectedContact = { address, name, alias, isContact, identicon, ensVerified }
|
||||
select.menu.close()
|
||||
validate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../imports"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool isPending: false
|
||||
readonly property string uuid: Utils.uuid()
|
||||
readonly property var validateAsync: Backpressure.debounce(inpAddress, 600, function (inputValue) {
|
||||
root.isPending = true
|
||||
var name = inputValue.startsWith("@") ? inputValue.substring(1) : inputValue
|
||||
walletModel.resolveENS(name, uuid)
|
||||
});
|
||||
signal resolved(string resolvedAddress)
|
||||
|
||||
function resolveEns(name) {
|
||||
if (Utils.isValidEns(name)) {
|
||||
root.validateAsync(name)
|
||||
}
|
||||
}
|
||||
width: 12
|
||||
height: 12
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: loadingIndicator
|
||||
active: root.isPending
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loadingIndicator
|
||||
LoadingImage {
|
||||
width: root.width
|
||||
height: root.height
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: walletModel
|
||||
onEnsWasResolved: {
|
||||
if (uuid !== root.uuid) {
|
||||
return
|
||||
}
|
||||
root.resolved(resolvedAddress)
|
||||
root.isPending = false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,14 +19,26 @@ Item {
|
|||
//% "Invalid ethereum address"
|
||||
readonly property string addressValidationError: qsTrId("invalid-ethereum-address")
|
||||
property bool isValid: false
|
||||
property bool isPending: false
|
||||
property bool isPending: {
|
||||
if (!selAddressSource.selectedSource) {
|
||||
return false
|
||||
}
|
||||
switch (selAddressSource.selectedSource.value) {
|
||||
case RecipientSelector.Type.Address:
|
||||
return inpAddress.isPending
|
||||
case RecipientSelector.Type.Contact:
|
||||
return selContact.isPending
|
||||
case RecipientSelector.Type.Account:
|
||||
return selAccount.isPending
|
||||
}
|
||||
}
|
||||
property var reset: function() {}
|
||||
readonly property var sources: [
|
||||
//% "Address"
|
||||
{ text: qsTrId("address"), value: RecipientSelector.Type.Address, visible: true },
|
||||
//% "My account"
|
||||
{ text: qsTrId("my-account"), value: RecipientSelector.Type.Account, visible: true },
|
||||
{ text: qsTr("Contact"), value: RecipientSelector.Type.Contact, visible: false }
|
||||
{ text: qsTr("Contact"), value: RecipientSelector.Type.Contact, visible: true }
|
||||
]
|
||||
property var selectedType: RecipientSelector.Type.Address
|
||||
|
||||
|
@ -36,7 +48,19 @@ Item {
|
|||
selAccount.resetInternal()
|
||||
selAddressSource.resetInternal()
|
||||
isValid = false
|
||||
isPending = false
|
||||
isPending = Qt.binding(function() {
|
||||
if (!selAddressSource.selectedSource) {
|
||||
return false
|
||||
}
|
||||
switch (selAddressSource.selectedSource.value) {
|
||||
case RecipientSelector.Type.Address:
|
||||
return inpAddress.isPending
|
||||
case RecipientSelector.Type.Contact:
|
||||
return selContact.isPending
|
||||
case RecipientSelector.Type.Account:
|
||||
return selAccount.isPending
|
||||
}
|
||||
})
|
||||
selectedType = RecipientSelector.Type.Address
|
||||
selectedRecipient = undefined
|
||||
accounts = undefined
|
||||
|
|
Loading…
Reference in New Issue