feat: 1-on-1 chat command ENS flow

1-on-1 chat command to send and request a transaction to/from a contact with ENS enabled.
This commit is contained in:
emizzle 2020-10-28 18:44:09 +11:00 committed by Iuri Matias
parent 326c6bb6c3
commit bc1525f513
17 changed files with 321 additions and 258 deletions

View File

@ -401,6 +401,7 @@ QtObject:
# Updating usernames for all the messages list
for k in self.messageList.keys:
self.messageList[k].updateUsernames(contacts)
self.activeChannel.contactsUpdated()
proc updateChannelForContacts*(self: ChatsView, contacts: seq[Profile]) =
for contact in contacts:

View File

@ -35,6 +35,8 @@ QtObject:
QtProperty[string] id:
read = id
proc contactsUpdated*(self: ChatItemView) {.signal}
proc userNameOrAlias(self: ChatItemView, pubKey: string): string {.slot.} =
if self.status.chat.contacts.hasKey(pubKey):
return ens.userNameOrAlias(self.status.chat.contacts[pubKey])
@ -48,6 +50,18 @@ QtObject:
QtProperty[string] name:
read = name
notify = contactsUpdated
proc ensVerified*(self: ChatItemView): bool {.slot.} =
if self.chatItem != nil and
self.chatItem.chatType.isOneToOne and
self.status.chat.contacts.hasKey(self.chatItem.id):
return self.status.chat.contacts[self.chatItem.id].ensVerified
result = false
QtProperty[bool] ensVerified:
read = ensVerified
notify = contactsUpdated
proc color*(self: ChatItemView): string {.slot.} = result = ?.self.chatItem.color

View File

@ -9,6 +9,7 @@ QtObject:
address*: string
pubKey*: string
appearance*: int
ensVerified*: bool
proc setup(self: ProfileInfoView) =
self.QObject.setup
@ -23,6 +24,7 @@ QtObject:
result.username = ""
result.identicon = ""
result.appearance = 0
result.ensVerified = false
result.setup
proc profileChanged*(self: ProfileInfoView) {.signal.}
@ -33,6 +35,7 @@ QtObject:
self.appearance = profile.appearance
self.pubKey = profile.id
self.address = profile.address
self.ensVerified = profile.ensVerified
self.profileChanged()
proc username*(self: ProfileInfoView): string {.slot.} = result = self.username
@ -67,3 +70,9 @@ QtObject:
QtProperty[string] address:
read = address
notify = profileChanged
proc ensVerified*(self: ProfileInfoView): bool {.slot.} = self.ensVerified
QtProperty[bool] ensVerified:
read = ensVerified
notify = profileChanged

View File

@ -562,13 +562,17 @@ QtObject:
self.accounts.getAccount(index).transactions)
self.loadingTrxHistoryChanged(false)
proc resolveENS*(self: WalletView, ens: string) {.slot.} =
proc resolveENS*(self: WalletView, ens: string, uuid: string) {.slot.} =
spawnAndSend(self, "ensResolved") do:
status_ens.owner(ens)
$ %* { "address": status_ens.address(ens), "uuid": uuid }
proc ensWasResolved*(self: WalletView, resolvedPubKey: string) {.signal.}
proc ensWasResolved*(self: WalletView, resolvedAddress: string, uuid: string) {.signal.}
proc ensResolved(self: WalletView, pubKey: string) {.slot.} =
self.ensWasResolved(pubKey)
proc ensResolved(self: WalletView, addressUuidJson: string) {.slot.} =
let
parsed = addressUuidJson.parseJson
address = parsed["address"].to(string)
uuid = parsed["uuid"].to(string)
self.ensWasResolved(address, uuid)
proc transactionCompleted*(self: WalletView, success: bool, txHash: string, revertReason: string = "") {.signal.}

View File

@ -8,6 +8,7 @@ import "./components"
import "./ChatColumn"
import "./ChatColumn/ChatComponents"
import "./data"
import "../Wallet"
StackLayout {
id: chatColumnLayout
@ -219,36 +220,18 @@ StackLayout {
stickerPackList: chatsModel.stickerPacks
chatType: chatsModel.activeChannel.chatType
onSendTransactionCommandButtonClicked: {
chatCommandModal.sendChatCommand = chatColumnLayout.requestAddressForTransaction
chatCommandModal.isRequested = false
//% "Send"
chatCommandModal.commandTitle = qsTrId("command-button-send")
chatCommandModal.title = chatCommandModal.commandTitle
//% "Request Address"
chatCommandModal.finalButtonLabel = qsTrId("request-address")
chatCommandModal.selectedRecipient = {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
txModalLoader.sourceComponent = undefined
if (chatsModel.activeChannel.ensVerified) {
txModalLoader.sourceComponent = cmpSendTransactionWithEns
} else {
txModalLoader.sourceComponent = cmpSendTransactionNoEns
}
chatCommandModal.open()
txModalLoader.item.open()
}
onReceiveTransactionCommandButtonClicked: {
chatCommandModal.sendChatCommand = chatColumnLayout.requestTransaction
chatCommandModal.isRequested = true
//% "Request"
chatCommandModal.commandTitle = qsTrId("wallet-request")
chatCommandModal.title = chatCommandModal.commandTitle
//% "Request"
chatCommandModal.finalButtonLabel = qsTrId("wallet-request")
chatCommandModal.selectedRecipient = {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
chatCommandModal.open()
txModalLoader.sourceComponent = undefined
txModalLoader.sourceComponent = cmpReceiveTransaction
txModalLoader.item.open()
}
onStickerSelected: {
chatsModel.sendSticker(hashId, packId)
@ -259,8 +242,111 @@ StackLayout {
EmptyChat {}
ChatCommandModal {
id: chatCommandModal
Loader {
id: txModalLoader
}
Component {
id: cmpSendTransactionNoEns
ChatCommandModal {
id: sendTransactionNoEns
sendChatCommand: chatColumnLayout.requestAddressForTransaction
isRequested: false
//% "Send"
commandTitle: qsTrId("command-button-send")
title: commandTitle
//% "Request Address"
finalButtonLabel: qsTrId("request-address")
selectRecipient.selectedRecipient: {
return {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
}
selectRecipient.selectedType: RecipientSelector.Type.Contact
selectRecipient.readOnly: true
onReset: {
selectRecipient.selectedRecipient = Qt.binding(function() {
return {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
})
selectRecipient.selectedType = RecipientSelector.Type.Contact
selectRecipient.readOnly = true
}
}
}
Component {
id: cmpReceiveTransaction
ChatCommandModal {
id: receiveTransaction
sendChatCommand: chatColumnLayout.requestTransaction
isRequested: true
//% "Request"
commandTitle: qsTrId("wallet-request")
title: commandTitle
//% "Request"
finalButtonLabel: qsTrId("wallet-request")
selectRecipient.selectedRecipient: {
return {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
}
selectRecipient.selectedType: RecipientSelector.Type.Contact
selectRecipient.readOnly: true
onReset: {
selectRecipient.selectedRecipient = Qt.binding(function() {
return {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
})
selectRecipient.selectedType = RecipientSelector.Type.Contact
selectRecipient.readOnly = true
}
}
}
Component {
id: cmpSendTransactionWithEns
SendModal {
id: sendTransactionWithEns
onOpened: {
walletModel.getGasPricePredictions()
}
selectRecipient.readOnly: true
selectRecipient.selectedRecipient: {
return {
address: "",
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Address,
ensVerified: true
}
}
selectRecipient.selectedType: RecipientSelector.Type.Address
onReset: {
selectRecipient.readOnly = true
selectRecipient.selectedRecipient = Qt.binding(function() {
return {
address: "",
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Address,
ensVerified: true
}
})
selectRecipient.selectedType = RecipientSelector.Type.Address
}
}
}
}

View File

@ -11,15 +11,17 @@ ModalPopup {
property string finalButtonLabel: "Request address"
property var sendChatCommand: function () {}
property bool isRequested: false
signal reset
id: root
title: root.commandTitle
height: 504
property alias selectedRecipient: selectRecipient.selectedRecipient
property alias selectRecipient: selectRecipient
onClosed: {
stack.reset()
root.reset()
}
TransactionStackView {
@ -78,7 +80,6 @@ ModalPopup {
label: root.isRequested ?
qsTr("From") :
qsTr("To")
readOnly: true
anchors.top: separator.bottom
anchors.topMargin: 10
width: stack.width
@ -102,7 +103,7 @@ ModalPopup {
defaultCurrency: walletModel.defaultCurrency
getFiatValue: walletModel.getFiatValue
getCryptoValue: walletModel.getCryptoValue
isRequested: root.isRequested
validateBalance: !root.isRequested
width: stack.width
reset: function() {
selectedAccount = Qt.binding(function() { return selectFromAccount.selectedAccount })

View File

@ -1,110 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import "../../../../../imports"
import "../../../../../shared"
Popup {
id: root
width: buttonRow.width
height: buttonRow.height
padding: 0
margins: 0
background: Rectangle {
color: Style.current.background
radius: Style.current.radius
border.width: 0
layer.enabled: true
layer.effect: DropShadow {
verticalOffset: 3
radius: 8
samples: 15
fast: true
cached: true
color: "#22000000"
}
}
function requestAddressForTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
amount = utilsModel.eth2Wei(amount.toString(), tokenDecimals)
chatsModel.requestAddressForTransaction(chatsModel.activeChannel.id,
address,
amount,
tokenAddress)
chatCommandModal.close()
}
function requestTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
amount = utilsModel.eth2Wei(amount.toString(), tokenDecimals)
chatsModel.requestTransaction(chatsModel.activeChannel.id,
address,
amount,
tokenAddress)
chatCommandModal.close()
}
Row {
id: buttonRow
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
padding: Style.current.halfPadding
spacing: Style.current.halfPadding
ChatCommandButton {
iconColor: Style.current.purple
iconSource: "../../../../img/send.svg"
//% "Send transaction"
text: qsTrId("send-transaction")
onClicked: function () {
chatCommandModal.sendChatCommand = root.requestAddressForTransaction
chatCommandModal.isRequested = false
//% "Send"
chatCommandModal.commandTitle = qsTrId("command-button-send")
chatCommandModal.title = chatCommandModal.commandTitle
//% "Request Address"
chatCommandModal.finalButtonLabel = qsTrId("request-address")
chatCommandModal.selectedRecipient = {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
chatCommandModal.open()
root.close()
}
}
ChatCommandButton {
iconColor: Style.current.orange
iconSource: "../../../../img/send.svg"
rotatedImage: true
//% "Request transaction"
text: qsTrId("request-transaction")
onClicked: function () {
chatCommandModal.sendChatCommand = root.requestTransaction
chatCommandModal.isRequested = true
//% "Request"
chatCommandModal.commandTitle = qsTrId("wallet-request")
chatCommandModal.title = chatCommandModal.commandTitle
//% "Request"
chatCommandModal.finalButtonLabel = qsTrId("wallet-request")
chatCommandModal.selectedRecipient = {
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
chatCommandModal.open()
root.close()
}
}
ChatCommandModal {
id: chatCommandModal
}
}
}

View File

@ -45,7 +45,7 @@ Item {
SignTransactionModal {
id: signTransactionModal
onOpened: {
walletModel.getGasPricePredictions()
walletModel.getGasPricePredictions()
}
selectedRecipient: {
return {

View File

@ -8,8 +8,11 @@ import "../../../shared/status"
import "./components"
ModalPopup {
property alias selectFromAccount: selectFromAccount
id: root
property alias selectFromAccount: selectFromAccount
property alias selectRecipient: selectRecipient
property alias stack: stack
signal reset
//% "Send"
title: qsTrId("command-button-send")
@ -25,6 +28,7 @@ ModalPopup {
onClosed: {
stack.reset()
root.reset()
}
function sendTransaction() {
@ -86,7 +90,7 @@ ModalPopup {
reset: function() {
accounts = Qt.binding(function() { return walletModel.accounts })
contacts = Qt.binding(function() { return profileModel.addedContacts })
selectedRecipient = {}
selectedRecipient = undefined
}
onSelectedRecipientChanged: if (isValid) { gasSelector.estimateGas() }
}

View File

@ -24,6 +24,7 @@ Item {
property int dropdownWidth: width
property alias dropdownAlignment: select.menuAlignment
property bool isValid: true
property bool readOnly: false
property var reset: function() {}
function resetInternal() {
@ -33,6 +34,7 @@ Item {
minRequiredAssetBalance = 0
assetFound = undefined
isValid = true
readOnly = false
}
function validate() {

View File

@ -11,47 +11,63 @@ Item {
//% "ENS Username not found"
property string ensAsyncValidationError: qsTrId("ens-username-not-found")
property alias label: inpAddress.label
property alias text: inpAddress.text
property string selectedAddress
property var isValid: false
property bool isPending: false
readonly property string uuid: Utils.uuid()
property alias readOnly: inpAddress.readOnly
property bool isResolvedAddress: false
height: inpAddress.height
onSelectedAddressChanged: validate()
onTextChanged: resolveEns()
function resetInternal() {
selectedAddress = ""
inpAddress.resetInternal()
metrics.text = ""
isValid = false
isPending = false
isResolvedAddress = false
}
function validate(inputValue) {
if (!inputValue) inputValue = selectedAddress
let isValid =
(inputValue && inputValue.startsWith("0x") && Utils.isValidAddress(inputValue) || Utils.isValidEns(inputValue))
inpAddress.validationError = isValid ? "" : validationError
function resolveEns() {
if (Utils.isValidEns(text)) {
root.validateAsync(text)
}
}
function validate() {
let isValidEns = Utils.isValidEns(text)
let isValidAddress = Utils.isValidAddress(selectedAddress)
let isValid = (isValidEns && !isResolvedAddress) || isPending || isValidAddress
inpAddress.validationError = ""
if (!isValid){
inpAddress.validationError = isResolvedAddress ? ensAsyncValidationError : validationError
}
root.isValid = isValid
return isValid
}
property var validateAsync: Backpressure.debounce(inpAddress, 300, function (inputValue) {
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)
walletModel.resolveENS(name, uuid)
});
Connections {
target: walletModel
onEnsWasResolved: {
root.isPending = false
if (resolvedPubKey === ""){
inpAddress.validationError = root.ensAsyncValidationError
root.isValid = false
} else {
root.isValid = true
root.selectedAddress = resolvedPubKey
inpAddress.validationError = ""
if (uuid !== root.uuid) {
return
}
root.isPending = false
root.isResolvedAddress = true
root.selectedAddress = resolvedAddress
}
}
@ -64,29 +80,21 @@ Item {
validationErrorTopMargin: 8
textField.onFocusChanged: {
let isValid = true
if (text !== "") {
isValid = root.validate(metrics.text)
}
if (!isValid) {
return
}
if (textField.focus) {
text = metrics.text
} else if (Utils.isValidAddress(metrics.text)) {
text = metrics.elidedText
if (text !== "" && Utils.isValidAddress(metrics.text)) {
if (textField.focus) {
text = metrics.text
} else {
text = metrics.elidedText
}
}
}
textField.rightPadding: 73
onTextEdited: {
metrics.text = text
const isValid = root.validate(inputValue)
if (isValid) {
if (Utils.isValidAddress(inputValue)) {
root.selectedAddress = inputValue
} else {
Qt.callLater(root.validateAsync, inputValue)
}
}
resolveEns()
root.isResolvedAddress = false
root.selectedAddress = text
}
TextMetrics {
id: metrics
@ -98,6 +106,7 @@ Item {
anchors.rightMargin: 8
anchors.top: parent.top
anchors.topMargin: 14
visible: !root.readOnly
//% "Paste"
label: qsTrId("paste")
onClicked: {

View File

@ -7,14 +7,14 @@ import "../imports"
Item {
id: root
property var sources: []
property string selectedSource: sources[0] || "Address"
property var selectedSource: sources.length ? sources[0] : null
property int dropdownWidth: 220
property var reset: function() {}
height: select.height
function resetInternal() {
sources = []
selectedSource = sources[0] || "Address"
selectedSource = sources.length ? sources[0] : null
}
Select {
@ -26,7 +26,7 @@ Item {
anchors.fill: parent
StyledText {
id: selectedTextField
text: root.selectedSource
text: !!root.selectedSource ? root.selectedSource.text : qsTr("Invalid source")
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.verticalCenter: parent.verticalCenter
@ -49,7 +49,7 @@ Item {
StyledText {
id: itemText
text: root.sources[index]
text: root.sources[index].text
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.verticalCenter: parent.verticalCenter

View File

@ -21,7 +21,7 @@ Item {
property var getFiatValue: function () {}
property var getCryptoValue: function () {}
property bool isDirty: false
property bool isRequested: false
property bool validateBalance: true
property bool isValid: false
property var reset: function() {}
@ -57,7 +57,7 @@ Item {
} else if (input === 0.00 && hasTyped) {
error = greaterThan0ErrorMessage
isValid = false
} else if (!isRequested && input > balance && !noInput) {
} else if (validateBalance && input > balance && !noInput) {
error = balanceErrorMessage
isValid = false
}
@ -86,7 +86,7 @@ Item {
}
Item {
visible: !root.isRequested
visible: root.validateBalance
anchors.right: parent.right
anchors.left: parent.left
anchors.top: parent.top

View File

@ -16,31 +16,56 @@ Item {
property alias validationErrorAlignment: select.validationErrorAlignment
property bool isValid: false
property var reset: function() {}
property bool readOnly: false
function resetInternal() {
contacts = undefined
selectedContact = undefined
select.validationError = ""
isValid = false
readOnly = false
}
onContactsChanged: {
if (root.readOnly) {
return
}
//% "Select a contact"
root.selectedContact = { name: qsTrId("select-a-contact") }
}
onSelectedContactChanged: validate()
function validate() {
const isValid = !!(selectedContact && selectedContact.address)
const isValid = !!selectedContact && Utils.isValidAddress(selectedContact.address)
select.validationError = !isValid ? validationError : ""
root.isValid = isValid
return isValid
}
Input {
id: inpReadOnly
visible: root.readOnly
width: parent.width
text: (root.selectedContact && root.selectedContact.name) ? root.selectedContact.name : qsTr("No contact selected")
textField.leftPadding: 14
textField.topPadding: 18
textField.bottomPadding: 18
textField.verticalAlignment: TextField.AlignVCenter
textField.font.pixelSize: 15
textField.color: Style.current.secondaryText
readOnly: true
validationErrorAlignment: TextEdit.AlignRight
validationErrorTopMargin: 8
customHeight: 56
}
Select {
id: select
label: ""
model: root.contacts
width: parent.width
visible: !root.readOnly
menuAlignment: Select.MenuAlignment.Left
selectedItemView: Item {
anchors.fill: parent

View File

@ -15,27 +15,35 @@ Item {
property alias additionalInfo: txtAddlInfo.text
property var selectedRecipient
property bool readOnly: false
height: (readOnly ? inpReadOnly.height : inpAddress.height) + txtLabel.height
height: inpAddress.height + txtLabel.height
//% "Invalid ethereum address"
readonly property string addressValidationError: qsTrId("invalid-ethereum-address")
property bool isValid: false || readOnly
property bool isValid: false
property bool isPending: false
property var reset: function() {}
readonly property var sources: [
//% "Address"
qsTrId("address"),
{ text: qsTrId("address"), value: RecipientSelector.Type.Address, visible: true },
//% "My account"
qsTrId("my-account")
{ text: qsTrId("my-account"), value: RecipientSelector.Type.Account, visible: true },
{ text: qsTr("Contact"), value: RecipientSelector.Type.Contact, visible: false }
]
property var selectedType: RecipientSelector.Type.Address
function resetInternal() {
inpAddress.resetInternal()
selContact.resetInternal()
selAccount.resetInternal()
selAddressSource.resetInternal()
isValid = false
isPending = false
selectedType = RecipientSelector.Type.Address
selectedRecipient = undefined
accounts = undefined
contacts = undefined
selContact.reset()
selAccount.reset()
selAddressSource.reset()
isValid = Qt.binding(function() { return false || readOnly })
}
enum Type {
@ -46,20 +54,52 @@ Item {
function validate() {
let isValid = true
if (readOnly) {
isValid = Utils.isValidAddress(selectedRecipient.address)
if (!isValid) {
inpReadOnly.validationError = addressValidationError
}
} else if (selAddressSource.selectedSource === "Address") {
isValid = inpAddress.validate()
} else if (selAddressSource.selectedSource === "Contact") {
isValid = selContact.validate()
switch (root.selectedType) {
case RecipientSelector.Type.Address:
isValid = inpAddress.isValid
break
case RecipientSelector.Type.Contact:
isValid = selContact.isValid
break
case RecipientSelector.Type.Account:
isValid = selAccount.isValid
break
}
root.isValid = isValid
return isValid
}
function getSourceByType(type) {
return root.sources.find(source => source.value === type)
}
onSelectedTypeChanged: {
if (selectedType !== undefined) {
selAddressSource.selectedSource = getSourceByType(selectedType)
}
if (!selectedRecipient) {
return
}
switch (root.selectedType) {
case RecipientSelector.Type.Address:
inpAddress.text = selectedRecipient.name || ""
inpAddress.selectedAddress = selectedRecipient.address
inpAddress.visible = true
selContact.visible = selAccount.visible = false
break
case RecipientSelector.Type.Contact:
selContact.selectedContact = selectedRecipient
selContact.visible = true
inpAddress.visible = selAccount.visible = false
break
case RecipientSelector.Type.Account:
selAccount.selectedAccount = selectedRecipient
selAccount.visible = true
inpAddress.visible = selContact.visible = false
break
}
}
Text {
id: txtLabel
visible: label !== ""
@ -91,48 +131,24 @@ Item {
anchors.right: parent.right
spacing: 8
Input {
id: inpReadOnly
visible: root.readOnly
Layout.preferredWidth: selAddressSource.visible ? root.inputWidth : parent.width
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
anchors.left: undefined
anchors.right: undefined
//% "No recipient selected"
text: (root.selectedRecipient && root.selectedRecipient.name) ? root.selectedRecipient.name : qsTrId("no-recipient-selected")
textField.leftPadding: 14
textField.topPadding: 18
textField.bottomPadding: 18
textField.verticalAlignment: TextField.AlignVCenter
textField.font.pixelSize: 15
textField.color: Style.current.secondaryText
readOnly: true
validationErrorAlignment: TextEdit.AlignRight
validationErrorTopMargin: 8
customHeight: 56
}
AddressInput {
id: inpAddress
width: root.inputWidth
label: ""
visible: !root.readOnly
readOnly: root.readOnly
visible: true
Layout.preferredWidth: selAddressSource.visible ? root.inputWidth : parent.width
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
validationError: root.addressValidationError
onSelectedAddressChanged: {
if (root.readOnly) {
if (!selAddressSource.selectedSource || (selAddressSource.selectedSource && selAddressSource.selectedSource.value !== RecipientSelector.Type.Address)) {
return
}
root.selectedRecipient = { address: selectedAddress, type: RecipientSelector.Type.Address }
}
onIsValidChanged: {
if (selAddressSource.selectedSource === "Address") {
root.isValid = isValid
}
root.selectedRecipient.address = selectedAddress
root.selectedRecipient.type = RecipientSelector.Type.Address
}
onIsValidChanged: root.validate()
}
ContactSelector {
@ -141,26 +157,22 @@ Item {
visible: false
width: root.inputWidth
dropdownWidth: parent.width
readOnly: root.readOnly
Layout.preferredWidth: selAddressSource.visible ? root.inputWidth : parent.width
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
reset: function() {
contacts = root.contacts
contacts = Qt.binding(function() { return root.contacts })
readOnly = Qt.binding(function() { return root.readOnly })
}
onSelectedContactChanged: {
if (root.readOnly) {
if (!selectedContact || !selAddressSource.selectedSource || !selectedContact.address || (selAddressSource.selectedSource && selAddressSource.selectedSource.value !== RecipientSelector.Type.Contact)) {
return
}
if(selectedContact && selectedContact.address) {
const { address, name, alias, isContact, identicon, ensVerified } = selectedContact
root.selectedRecipient = { address, name, alias, isContact, identicon, ensVerified, type: RecipientSelector.Type.Contact }
}
}
onIsValidChanged: {
if (selAddressSource.selectedSource === "Contact") {
root.isValid = isValid
}
const { address, name, alias, isContact, identicon, ensVerified } = selectedContact
root.selectedRecipient = { address, name, alias, isContact, identicon, ensVerified, type: RecipientSelector.Type.Contact }
}
onIsValidChanged: root.validate()
}
AccountSelector {
@ -174,40 +186,44 @@ Item {
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
reset: function() {
accounts = root.accounts
accounts = Qt.binding(function() { return root.accounts })
}
onSelectedAccountChanged: {
if (root.readOnly || !selectedAccount) {
if (!selectedAccount || !selAddressSource.selectedSource || (selAddressSource.selectedSource && selAddressSource.selectedSource.value !== RecipientSelector.Type.Account)) {
return
}
const { address, name, iconColor, assets, fiatBalance } = selectedAccount
root.selectedRecipient = { address, name, iconColor, assets, fiatBalance, type: RecipientSelector.Type.Account }
}
onIsValidChanged: root.validate()
}
AddressSourceSelector {
id: selAddressSource
visible: !root.readOnly
sources: root.sources
sources: root.sources.filter(source => source.visible)
width: sourceSelectWidth
Layout.preferredWidth: root.sourceSelectWidth
Layout.alignment: Qt.AlignTop
reset: function() {
sources = root.sources
sources = Qt.binding(function() { return root.sources.filter(source => source.visible) })
selectedSource = root.getSourceByType(root.selectedType)
}
onSelectedSourceChanged: {
if (root.readOnly) {
if (root.readOnly || !selectedSource) {
return
}
let address, name
switch (selectedSource) {
case "Address":
switch (selectedSource.value) {
case RecipientSelector.Type.Address:
inpAddress.visible = true
selContact.visible = selAccount.visible = false
root.height = Qt.binding(function() { return inpAddress.height + txtLabel.height })
root.selectedRecipient = { address: inpAddress.selectedAddress, type: RecipientSelector.Type.Address }
if (root.selectedType !== RecipientSelector.Type.Address) root.selectedType = RecipientSelector.Type.Address
root.isValid = inpAddress.isValid
break;
case "Contact":
case RecipientSelector.Type.Contact:
selContact.visible = true
inpAddress.visible = selAccount.visible = false
root.height = Qt.binding(function() { return selContact.height + txtLabel.height })
@ -215,9 +231,10 @@ Item {
address = selContact.selectedContact.address
name = selContact.selectedContact.name
root.selectedRecipient = { address, name, alias, isContact, identicon, ensVerified, type: RecipientSelector.Type.Contact }
if (root.selectedType !== RecipientSelector.Type.Contact) root.selectedType = RecipientSelector.Type.Contact
root.isValid = selContact.isValid
break;
case "My account":
case RecipientSelector.Type.Account:
selAccount.visible = true
inpAddress.visible = selContact.visible = false
root.height = Qt.binding(function() { return selAccount.height + txtLabel.height })
@ -225,6 +242,7 @@ Item {
address = selAccount.selectedAccount.address
name = selAccount.selectedAccount.name
root.selectedRecipient = { address, name, iconColor, assets, fiatBalance, type: RecipientSelector.Type.Account }
if (root.selectedType !== RecipientSelector.Type.Account) root.selectedType = RecipientSelector.Type.Account
root.isValid = selAccount.isValid
break;
}

View File

@ -4,8 +4,8 @@ import "../imports"
TextField {
font.family: Style.current.fontRegular.name
color: Style.current.textColor
selectByMouse: true
color: readOnly ? Style.current.secondaryText : Style.current.textColor
selectByMouse: !readOnly
selectedTextColor: Style.current.textColor
selectionColor: Style.current.secondaryHover
}

View File

@ -132,7 +132,7 @@ Item {
when: !!root.toAccount && root.toAccount.type === RecipientSelector.Type.Address
PropertyChanges {
target: txtToPrimary
text: root.toAccount ? root.toAccount.address : ""
text: !!root.toAccount ? root.toAccount.address : ""
elide: Text.ElideMiddle
anchors.leftMargin: 190
anchors.right: parent.right