chore(savedaddresses): saved addresses improvements

Closes #13140
This commit is contained in:
Sale Djenic 2024-01-09 14:50:01 +01:00 committed by saledjenic
parent 1e972950e6
commit ba5e259296
17 changed files with 237 additions and 106 deletions

View File

@ -27,11 +27,14 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_SAVED_ADDRESS_UPDATED) do(e:Args): self.events.on(SIGNAL_SAVED_ADDRESS_UPDATED) do(e:Args):
let args = SavedAddressArgs(e) let args = SavedAddressArgs(e)
self.delegate.savedAddressUpdated(args.name, args.address, args.ens, args.errorMsg) self.delegate.savedAddressUpdated(args.name, args.address, args.errorMsg)
self.events.on(SIGNAL_SAVED_ADDRESS_DELETED) do(e:Args): self.events.on(SIGNAL_SAVED_ADDRESS_DELETED) do(e:Args):
let args = SavedAddressArgs(e) let args = SavedAddressArgs(e)
self.delegate.savedAddressDeleted(args.address, args.ens, args.errorMsg) self.delegate.savedAddressDeleted(args.address, args.errorMsg)
proc areTestNetworksEnabled*(self: Controller): bool =
return self.savedAddressService.areTestNetworksEnabled()
proc getSavedAddresses*(self: Controller): seq[saved_address_service.SavedAddressDto] = proc getSavedAddresses*(self: Controller): seq[saved_address_service.SavedAddressDto] =
return self.savedAddressService.getSavedAddresses() return self.savedAddressService.getSavedAddresses()
@ -40,5 +43,5 @@ proc createOrUpdateSavedAddress*(self: Controller, name: string, address: string
chainShortNames: string) = chainShortNames: string) =
self.savedAddressService.createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames) self.savedAddressService.createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames)
proc deleteSavedAddress*(self: Controller, address: string, ens: string) = proc deleteSavedAddress*(self: Controller, address: string) =
self.savedAddressService.deleteSavedAddress(address, ens) self.savedAddressService.deleteSavedAddress(address)

View File

@ -21,13 +21,13 @@ method createOrUpdateSavedAddress*(self: AccessInterface, name: string, address:
chainShortNames: string) {.base.} = chainShortNames: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method deleteSavedAddress*(self: AccessInterface, address: string, ens: string) {.base.} = method deleteSavedAddress*(self: AccessInterface, address: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method savedAddressUpdated*(self: AccessInterface, name: string, address: string, ens: string, errorMsg: string) {.base.} = method savedAddressUpdated*(self: AccessInterface, name: string, address: string, errorMsg: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method savedAddressDeleted*(self: AccessInterface, address: string, ens: string, errorMsg: string) {.base.} = method savedAddressDeleted*(self: AccessInterface, address: string, errorMsg: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method savedAddressNameExists*(self: AccessInterface, name: string): bool {.base.} = method savedAddressNameExists*(self: AccessInterface, name: string): bool {.base.} =

View File

@ -102,25 +102,17 @@ QtObject:
for item in items: for item in items:
self.itemChanged(item.getAddress()) self.itemChanged(item.getAddress())
proc getItemByAddress*(self: Model, address: string): Item = proc getItemByAddress*(self: Model, address: string, isTest: bool): Item =
if address.len == 0 or address == ZERO_ADDRESS: if address.len == 0 or address == ZERO_ADDRESS:
return return
for item in self.items: for item in self.items:
if cmpIgnoreCase(item.getAddress(), address) == 0: if cmpIgnoreCase(item.getAddress(), address) == 0 and
(item.getIsTest() == isTest):
return item return item
proc getItemByEnsOrAddress*(self: Model, addrOrEns: string): Item = proc nameExists*(self: Model, name: string, isTest: bool): bool =
if addrOrEns.len == 0:
return
for item in self.items: for item in self.items:
if item.getEns().len > 0: if item.getName() == name and
if item.getEns() == addrOrEns: (item.getIsTest() == isTest):
return item
if addrOrEns != ZERO_ADDRESS and cmpIgnoreCase(item.getAddress(), addrOrEns) == 0:
return item
proc nameExists*(self: Model, name: string): bool =
for item in self.items:
if item.getName() == name:
return true return true
return false return false

View File

@ -61,28 +61,24 @@ method createOrUpdateSavedAddress*(self: Module, name: string, address: string,
chainShortNames: string) = chainShortNames: string) =
self.controller.createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames) self.controller.createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames)
method deleteSavedAddress*(self: Module, address: string, ens: string) = method deleteSavedAddress*(self: Module, address: string) =
self.controller.deleteSavedAddress(address, ens) self.controller.deleteSavedAddress(address)
method savedAddressUpdated*(self: Module, name: string, address: string, ens: string, errorMsg: string) = method savedAddressUpdated*(self: Module, name: string, address: string, errorMsg: string) =
var item = self.view.getModel().getItemByEnsOrAddress(address) let item = self.view.getModel().getItemByAddress(address, self.controller.areTestNetworksEnabled())
if item.isEmpty():
item = self.view.getModel().getItemByEnsOrAddress(ens)
self.loadSavedAddresses() self.loadSavedAddresses()
self.view.savedAddressAddedOrUpdated(item.isEmpty(), name, address, ens, errorMsg) self.view.savedAddressAddedOrUpdated(item.isEmpty(), name, address, errorMsg)
method savedAddressDeleted*(self: Module, address: string, ens: string, errorMsg: string) = method savedAddressDeleted*(self: Module, address: string, errorMsg: string) =
var item = self.view.getModel().getItemByEnsOrAddress(address) let item = self.view.getModel().getItemByAddress(address, self.controller.areTestNetworksEnabled())
if item.isEmpty():
item = self.view.getModel().getItemByEnsOrAddress(ens)
self.loadSavedAddresses() self.loadSavedAddresses()
self.view.savedAddressDeleted(item.getName(), address, ens, errorMsg) self.view.savedAddressDeleted(item.getName(), address, errorMsg)
method savedAddressNameExists*(self: Module, name: string): bool = method savedAddressNameExists*(self: Module, name: string): bool =
return self.view.getModel().nameExists(name) return self.view.getModel().nameExists(name, self.controller.areTestNetworksEnabled())
method getSavedAddressAsJson*(self: Module, address: string): string = method getSavedAddressAsJson*(self: Module, address: string): string =
let item = self.view.getModel().getItemByAddress(address) let item = self.view.getModel().getItemByAddress(address, self.controller.areTestNetworksEnabled())
let jsonObj = %* { let jsonObj = %* {
"name": item.getName(), "name": item.getName(),
"address": item.getAddress(), "address": item.getAddress(),

View File

@ -40,16 +40,16 @@ QtObject:
proc setItems*(self: View, items: seq[Item]) = proc setItems*(self: View, items: seq[Item]) =
self.model.setItems(items) self.model.setItems(items)
proc savedAddressAddedOrUpdated*(self: View, added: bool, name: string, address: string, ens: string, errorMsg: string) {.signal.} proc savedAddressAddedOrUpdated*(self: View, added: bool, name: string, address: string, errorMsg: string) {.signal.}
proc createOrUpdateSavedAddress*(self: View, name: string, address: string, ens: string, colorId: string, proc createOrUpdateSavedAddress*(self: View, name: string, address: string, ens: string, colorId: string,
chainShortNames: string) {.slot.} = chainShortNames: string) {.slot.} =
self.delegate.createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames) self.delegate.createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames)
proc savedAddressDeleted*(self: View, name: string, address: string, ens: string, errorMsg: string) {.signal.} proc savedAddressDeleted*(self: View, name: string, address: string, errorMsg: string) {.signal.}
proc deleteSavedAddress*(self: View, address: string, ens: string) {.slot.} = proc deleteSavedAddress*(self: View, address: string) {.slot.} =
self.delegate.deleteSavedAddress(address, ens) self.delegate.deleteSavedAddress(address)
proc savedAddressNameExists*(self: View, name: string): bool {.slot.} = proc savedAddressNameExists*(self: View, name: string): bool {.slot.} =
return self.delegate.savedAddressNameExists(name) return self.delegate.savedAddressNameExists(name)

View File

@ -4,7 +4,10 @@ include app/core/tasks/common
import backend/backend import backend/backend
type type
SavedAddressTaskArg = ref object of QObjectTaskArg SavedAddressesTaskArg = ref object of QObjectTaskArg
chainId*: int
SavedAddressTaskArg = ref object of SavedAddressesTaskArg
name: string name: string
address: string address: string
colorId: string colorId: string
@ -12,6 +15,54 @@ type
ens: string ens: string
isTestAddress: bool isTestAddress: bool
UpdateCriteria {.pure.} = enum
AlwaysUpdate
OnlyIfDifferent
proc isValidChainId(chainId: int): bool =
return chainId == Mainnet or chainId == Goerli or chainId == Sepolia
proc checkForEnsNameAndUpdate(chainId: int, savedAddress: var SavedAddressDto, updateCriteria: UpdateCriteria): RpcResponse[JsonNode] {.raises: [RpcException].} =
if savedAddress.isTest and chainId == Mainnet or
not savedAddress.isTest and chainId != Mainnet:
return
try:
try:
let ensResponse = backend.getName(chainId, savedAddress.address)
if updateCriteria == UpdateCriteria.OnlyIfDifferent and savedAddress.ens == ensResponse.result.getStr():
return
savedAddress.ens = ensResponse.result.getStr()
except:
savedAddress.ens = ""
return backend.upsertSavedAddress(savedAddress)
except Exception as e:
raise newException(RpcException, e.msg)
const fetchSavedAddressesAndResolveEnsNamesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[SavedAddressesTaskArg](argEncoded)
var response = %* {
"response": [],
"error": "",
}
try:
if not isValidChainId(arg.chainId):
raise newException(CatchableError, "invalid chainId: " & $arg.chainId)
let rpcResponse = backend.getSavedAddresses()
if not rpcResponse.error.isNil:
raise newException(CatchableError, rpcResponse.error.message)
for saJson in rpcResponse.result.getElems():
if saJson.kind != JObject or not saJson.hasKey("address"):
continue
var savedAddress = saJson.toSavedAddressDto()
try:
discard checkForEnsNameAndUpdate(arg.chainId, savedAddress, UpdateCriteria.OnlyIfDifferent)
except:
discard
response["response"].add(savedAddress.toJsonNode())
except Exception as e:
response["error"] = %* e.msg
arg.finish(response)
const upsertSavedAddressTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = const upsertSavedAddressTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[SavedAddressTaskArg](argEncoded) let arg = decode[SavedAddressTaskArg](argEncoded)
var response = %* { var response = %* {
@ -22,14 +73,16 @@ const upsertSavedAddressTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.
"error": "", "error": "",
} }
try: try:
let rpcResponse = backend.upsertSavedAddress(SavedAddressDto( if not isValidChainId(arg.chainId):
raise newException(CatchableError, "invalid chainId: " & $arg.chainId)
var savedAddress = SavedAddressDto(
name: arg.name, name: arg.name,
address: arg.address, address: arg.address,
colorId: arg.colorId, colorId: arg.colorId,
chainShortNames: arg.chainShortNames, chainShortNames: arg.chainShortNames,
ens: arg.ens, ens: arg.ens,
isTest: arg.isTestAddress) isTest: arg.isTestAddress)
) let rpcResponse = checkForEnsNameAndUpdate(arg.chainId, savedAddress, UpdateCriteria.AlwaysUpdate)
if not rpcResponse.error.isNil: if not rpcResponse.error.isNil:
raise newException(CatchableError, rpcResponse.error.message) raise newException(CatchableError, rpcResponse.error.message)
response["response"] = %* "ok" response["response"] = %* "ok"
@ -42,11 +95,10 @@ const deleteSavedAddressTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.
var response = %* { var response = %* {
"response": "", "response": "",
"address": %* arg.address, "address": %* arg.address,
"ens": %* arg.ens,
"error": "", "error": "",
} }
try: try:
let rpcResponse = backend.deleteSavedAddress(arg.address, arg.ens, arg.isTestAddress) let rpcResponse = backend.deleteSavedAddress(arg.address, arg.isTestAddress)
if not rpcResponse.error.isNil: if not rpcResponse.error.isNil:
raise newException(CatchableError, rpcResponse.error.message) raise newException(CatchableError, rpcResponse.error.message)
response["response"] = %* "ok" response["response"] = %* "ok"

View File

@ -22,3 +22,14 @@ proc toSavedAddressDto*(jsonObj: JsonNode): SavedAddressDto =
discard jsonObj.getProp("chainShortNames", result.chainShortNames) discard jsonObj.getProp("chainShortNames", result.chainShortNames)
discard jsonObj.getProp("isTest", result.isTest) discard jsonObj.getProp("isTest", result.isTest)
discard jsonObj.getProp("createdAt", result.createdAt) discard jsonObj.getProp("createdAt", result.createdAt)
proc toJsonNode*(self: SavedAddressDto): JsonNode =
result = %* {
"name": self.name,
"address": self.address,
"ens": self.ens,
"colorId": self.colorId,
"chainShortNames": self.chainShortNames,
"isTest": self.isTest,
"createdAt": self.createdAt,
}

View File

@ -26,7 +26,6 @@ type
SavedAddressArgs* = ref object of Args SavedAddressArgs* = ref object of Args
name*: string name*: string
address*: string address*: string
ens*: string
errorMsg*: string errorMsg*: string
QtObject: QtObject:
@ -49,57 +48,74 @@ QtObject:
result.networkService = networkService result.networkService = networkService
result.settingsService = settingsService result.settingsService = settingsService
proc fetchAddresses(self: Service) = ## Forward declaration
try: proc fetchSavedAddressesAndResolveEnsNames(self: Service)
let response = backend.getSavedAddresses() proc updateAddresses(self: Service, signal: string, arg: Args)
self.savedAddresses = map(
response.result.getElems(),
proc(x: JsonNode): SavedAddressDto = toSavedAddressDto(x)
)
let chainId = self.networkService.getAppNetwork().chainId
for savedAddress in self.savedAddresses:
if savedAddress.ens != "":
try:
let nameResponse = backend.getName(chainId, savedAddress.address)
savedAddress.ens = nameResponse.result.getStr
except:
continue
except Exception as e:
error "error: ", procName="fetchAddress", errName = e.name, errDesription = e.msg
proc updateAddresses(self: Service) =
self.fetchAddresses()
self.events.emit(SIGNAL_SAVED_ADDRESSES_UPDATED, Args())
proc init*(self: Service) = proc init*(self: Service) =
# Subscribe to sync events and check for changes
self.events.on(SignalType.Message.event) do(e:Args): self.events.on(SignalType.Message.event) do(e:Args):
var data = MessageSignal(e) var data = MessageSignal(e)
if(len(data.savedAddresses) > 0): if(len(data.savedAddresses) > 0):
self.updateAddresses() self.updateAddresses(SIGNAL_SAVED_ADDRESSES_UPDATED, Args())
self.fetchAddresses() self.fetchSavedAddressesAndResolveEnsNames()
proc areTestNetworksEnabled*(self: Service): bool =
return self.settingsService.areTestNetworksEnabled()
proc getAddresses(self: Service): seq[SavedAddressDto] =
try:
let response = backend.getSavedAddresses()
return map(response.result.getElems(), proc(x: JsonNode): SavedAddressDto = toSavedAddressDto(x))
except Exception as e:
error "error: ", procName="fetchAddress", errName = e.name, errDesription = e.msg
proc getSavedAddresses*(self: Service): seq[SavedAddressDto] = proc getSavedAddresses*(self: Service): seq[SavedAddressDto] =
return self.savedAddresses return self.savedAddresses
proc updateAddresses(self: Service, signal: string, arg: Args) =
self.savedAddresses = self.getAddresses()
self.events.emit(signal, arg)
proc fetchSavedAddressesAndResolveEnsNames(self: Service) =
let arg = SavedAddressTaskArg(
chainId: self.networkService.getAppNetwork().chainId,
tptr: cast[ByteAddress](fetchSavedAddressesAndResolveEnsNamesTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onSavedAddressesFetched",
)
self.threadpool.start(arg)
proc onSavedAddressesFetched(self: Service, rpcResponse: string) {.slot.} =
try:
let rpcResponseObj = rpcResponse.parseJson
if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
raise newException(CatchableError, rpcResponseObj{"error"}.getStr)
if rpcResponseObj{"response"}.kind != JArray:
raise newException(CatchableError, "invalid response")
self.savedAddresses = map(rpcResponseObj{"response"}.getElems(), proc(x: JsonNode): SavedAddressDto = toSavedAddressDto(x))
except Exception as e:
error "onSavedAddressesFetched", msg = e.msg
self.events.emit(SIGNAL_SAVED_ADDRESSES_UPDATED, Args())
proc createOrUpdateSavedAddress*(self: Service, name: string, address: string, ens: string, colorId: string, proc createOrUpdateSavedAddress*(self: Service, name: string, address: string, ens: string, colorId: string,
chainShortNames: string) = chainShortNames: string) =
let arg = SavedAddressTaskArg( let arg = SavedAddressTaskArg(
chainId: self.networkService.getAppNetwork().chainId,
name: name, name: name,
address: address, address: address,
ens: ens, ens: ens,
colorId: colorId, colorId: colorId,
chainShortNames: chainShortNames, chainShortNames: chainShortNames,
isTestAddress: self.settingsService.areTestNetworksEnabled(), isTestAddress: self.areTestNetworksEnabled(),
tptr: cast[ByteAddress](upsertSavedAddressTask), tptr: cast[ByteAddress](upsertSavedAddressTask),
vptr: cast[ByteAddress](self.vptr), vptr: cast[ByteAddress](self.vptr),
slot: "onSavedAddressCreatedOrUpdated", slot: "onSavedAddressCreatedOrUpdated",
) )
self.threadpool.start(arg) self.threadpool.start(arg)
proc onSavedAddressCreatedOrUpdated*(self: Service, rpcResponse: string) {.slot.} = proc onSavedAddressCreatedOrUpdated(self: Service, rpcResponse: string) {.slot.} =
var arg = SavedAddressArgs() var arg = SavedAddressArgs()
try: try:
let rpcResponseObj = rpcResponse.parseJson let rpcResponseObj = rpcResponse.parseJson
@ -110,25 +126,22 @@ QtObject:
arg.name = rpcResponseObj{"name"}.getStr arg.name = rpcResponseObj{"name"}.getStr
arg.address = rpcResponseObj{"address"}.getStr arg.address = rpcResponseObj{"address"}.getStr
arg.ens = rpcResponseObj{"ens"}.getStr
except Exception as e: except Exception as e:
error "onSavedAddressCreatedOrUpdated", msg = e.msg error "onSavedAddressCreatedOrUpdated", msg = e.msg
arg.errorMsg = e.msg arg.errorMsg = e.msg
self.fetchAddresses() self.updateAddresses(SIGNAL_SAVED_ADDRESS_UPDATED, arg)
self.events.emit(SIGNAL_SAVED_ADDRESS_UPDATED, arg)
proc deleteSavedAddress*(self: Service, address: string, ens: string) = proc deleteSavedAddress*(self: Service, address: string) =
let arg = SavedAddressTaskArg( let arg = SavedAddressTaskArg(
address: address, address: address,
ens: ens, isTestAddress: self.areTestNetworksEnabled(),
isTestAddress: self.settingsService.areTestNetworksEnabled(),
tptr: cast[ByteAddress](deleteSavedAddressTask), tptr: cast[ByteAddress](deleteSavedAddressTask),
vptr: cast[ByteAddress](self.vptr), vptr: cast[ByteAddress](self.vptr),
slot: "onDeleteSavedAddress", slot: "onDeleteSavedAddress",
) )
self.threadpool.start(arg) self.threadpool.start(arg)
proc onDeleteSavedAddress*(self: Service, rpcResponse: string) {.slot.} = proc onDeleteSavedAddress(self: Service, rpcResponse: string) {.slot.} =
var arg = SavedAddressArgs() var arg = SavedAddressArgs()
try: try:
let rpcResponseObj = rpcResponse.parseJson let rpcResponseObj = rpcResponse.parseJson
@ -138,9 +151,7 @@ QtObject:
raise newException(CatchableError, "invalid response") raise newException(CatchableError, "invalid response")
arg.address = rpcResponseObj{"address"}.getStr arg.address = rpcResponseObj{"address"}.getStr
arg.ens = rpcResponseObj{"ens"}.getStr
except Exception as e: except Exception as e:
error "onDeleteSavedAddress", msg = e.msg error "onDeleteSavedAddress", msg = e.msg
arg.errorMsg = e.msg arg.errorMsg = e.msg
self.fetchAddresses() self.updateAddresses(SIGNAL_SAVED_ADDRESS_DELETED, arg)
self.events.emit(SIGNAL_SAVED_ADDRESS_DELETED, arg)

View File

@ -91,10 +91,9 @@ rpc(upsertSavedAddress, "wakuext"):
rpc(deleteSavedAddress, "wakuext"): rpc(deleteSavedAddress, "wakuext"):
address: string address: string
ens: string
isTest: bool isTest: bool
rpc(getSavedAddresses, "wallet"): rpc(getSavedAddresses, "wakuext"):
discard discard
rpc(checkConnected, "wallet"): rpc(checkConnected, "wallet"):

View File

@ -108,3 +108,7 @@ class BaseElement:
def wait_until_hidden(self, timeout_msec: int = configs.squish.UI_LOAD_TIMEOUT_MSEC): def wait_until_hidden(self, timeout_msec: int = configs.squish.UI_LOAD_TIMEOUT_MSEC):
assert squish.waitFor(lambda: not self.is_visible, timeout_msec), f'Object {self} is not hidden' assert squish.waitFor(lambda: not self.is_visible, timeout_msec), f'Object {self} is not hidden'
def wait_until_enabled(self, timeout_msec: int = configs.squish.UI_LOAD_TIMEOUT_MSEC):
assert squish.waitFor(lambda: self.is_enabled, timeout_msec), f'Object {self} is not enabled'
return self

View File

@ -62,7 +62,7 @@ class AddSavedAddressPopup(SavedAddressPopup):
self.verify_ethereum_mainnet_network_tag_present() self.verify_ethereum_mainnet_network_tag_present()
self.verify_otimism_mainnet_network_tag_present() self.verify_otimism_mainnet_network_tag_present()
self.verify_arbitrum_mainnet_network_tag_present(), self.verify_arbitrum_mainnet_network_tag_present(),
self._save_add_address_button.click() self._save_add_address_button.wait_until_enabled().click()
self.wait_until_hidden() self.wait_until_hidden()

View File

@ -87,7 +87,7 @@ StatusListItem {
radius: 8 radius: 8
icon.name: "more" icon.name: "more"
onClicked: { onClicked: {
menu.openMenu(this, x - menu.width - statusListItemComponentsSlot.spacing, y + height + Style.current.halfPadding, menu.openMenu(this, x + width - menu.width - statusListItemComponentsSlot.spacing, y + height + Style.current.halfPadding,
{ {
name: root.name, name: root.name,
address: root.address, address: root.address,

View File

@ -98,6 +98,13 @@ StatusDialog {
readonly property var chainPrefixRegexPattern: /[^:]+\:?|:/g readonly property var chainPrefixRegexPattern: /[^:]+\:?|:/g
readonly property bool addressInputIsENS: !!d.ens readonly property bool addressInputIsENS: !!d.ens
property bool addressAlreadyAdded: false
function checkIfAddressIsAlreadyAddded(address) {
let details = RootStore.getSavedAddress(address)
d.addressAlreadyAdded = !!details.address
return !d.addressAlreadyAdded
}
/// Ensures that the \c root.address and \c root.chainShortNames are not reset when the initial text is set /// Ensures that the \c root.address and \c root.chainShortNames are not reset when the initial text is set
property bool initialized: false property bool initialized: false
@ -121,6 +128,13 @@ StatusDialog {
RootStore.createOrUpdateSavedAddress(d.name, d.address, d.ens, d.colorId, d.chainShortNames) RootStore.createOrUpdateSavedAddress(d.name, d.address, d.ens, d.colorId, d.chainShortNames)
root.close() root.close()
} }
property bool resolvingEnsName: false
readonly property string uuid: Utils.uuid()
readonly property var validateEnsAsync: Backpressure.debounce(root, 500, function (value) {
var name = value.startsWith("@") ? value.substring(1) : value
mainModule.resolveENS(name, d.uuid)
});
} }
Column { Column {
@ -182,13 +196,61 @@ StatusDialog {
validators: [ validators: [
StatusMinLengthValidator { StatusMinLengthValidator {
minLength: 1 minLength: 1
errorMessage: qsTr("Address must not be blank") errorMessage: qsTr("Please enter an ethereum address")
}, },
StatusValidator { StatusValidator {
errorMessage: addressInput.plainText ? qsTr("Please enter a valid address or ENS name.") : "" errorMessage: d.addressAlreadyAdded? qsTr("This address is already saved") : qsTr("Ethereum address invalid")
validate: function (t) { validate: function (value) {
return t !== Constants.zeroAddress && (Utils.isValidAddressWithChainPrefix(t) || Utils.isValidEns(t)) if (value !== Constants.zeroAddress) {
? true : { actual: t } if (Utils.isValidEns(value)) {
return true
}
if (Utils.isValidAddressWithChainPrefix(value)) {
if (d.editMode) {
return true
}
const prefixAndAddress = Utils.splitToChainPrefixAndAddress(value)
return d.checkIfAddressIsAlreadyAddded(prefixAndAddress.address)
}
}
return false
}
}
]
asyncValidators: [
StatusAsyncValidator {
id: resolvingEnsName
name: "resolving-ens-name"
errorMessage: d.addressAlreadyAdded? qsTr("This address is already saved") : qsTr("Ethereum address invalid")
asyncOperation: (value) => {
if (!Utils.isValidEns(value)) {
resolvingEnsName.asyncComplete("not-ens")
return
}
d.resolvingEnsName = true
d.validateEnsAsync(value)
}
validate: (value) => {
if (d.editMode || value === "not-ens") {
return true
}
if (!!value) {
return d.checkIfAddressIsAlreadyAddded(value)
}
return false
}
Connections {
target: mainModule
function onResolvedENS(resolvedPubKey: string, resolvedAddress: string, uuid: string) {
if (uuid !== d.uuid) {
return
}
d.resolvingEnsName = false
d.address = resolvedAddress
resolvingEnsName.asyncComplete(resolvedAddress)
}
} }
} }
] ]
@ -207,6 +269,7 @@ StatusDialog {
if (skipTextUpdate || !d.initialized) if (skipTextUpdate || !d.initialized)
return return
d.addressAlreadyAdded = false
plainText = input.edit.getText(0, text.length) plainText = input.edit.getText(0, text.length)
if (input.edit.previousText != plainText) { if (input.edit.previousText != plainText) {
@ -228,11 +291,13 @@ StatusDialog {
// Update root values // Update root values
if (Utils.isLikelyEnsName(plainText)) { if (Utils.isLikelyEnsName(plainText)) {
d.resolvingEnsName = true
d.ens = plainText d.ens = plainText
d.address = Constants.zeroAddress d.address = Constants.zeroAddress
d.chainShortNames = "" d.chainShortNames = ""
} }
else { else {
d.resolvingEnsName = false
d.ens = "" d.ens = ""
d.address = prefixAndAddress.address d.address = prefixAndAddress.address
d.chainShortNames = prefixAndAddress.prefix d.chainShortNames = prefixAndAddress.prefix
@ -409,7 +474,8 @@ StatusDialog {
rightButtons: ObjectModel { rightButtons: ObjectModel {
StatusButton { StatusButton {
text: d.editMode? qsTr("Save") : qsTr("Add address") text: d.editMode? qsTr("Save") : qsTr("Add address")
enabled: d.valid && d.dirty enabled: d.valid && d.dirty && !d.resolvingEnsName
loading: d.resolvingEnsName
onClicked: { onClicked: {
d.submit() d.submit()
} }

View File

@ -24,7 +24,7 @@ StatusDialog {
property string colorId property string colorId
property string chainShortNames property string chainShortNames
signal removeSavedAddress(string address, string ens) signal removeSavedAddress(string address)
width: 521 width: 521
focus: visible focus: visible
@ -36,7 +36,7 @@ StatusDialog {
readonly property real lineHeight: 1.2 readonly property real lineHeight: 1.2
function confirm() { function confirm() {
root.removeSavedAddress(root.address, root.ens) root.removeSavedAddress(root.address)
} }
} }

View File

@ -18,7 +18,6 @@ QtObject {
readonly property bool showAllAccounts: !root.showSavedAddresses && !root.selectedAddress readonly property bool showAllAccounts: !root.showSavedAddresses && !root.selectedAddress
property var lastCreatedSavedAddress property var lastCreatedSavedAddress
property var lastDeletedSavedAddress
property bool addingSavedAddress: false property bool addingSavedAddress: false
property bool deletingSavedAddress: false property bool deletingSavedAddress: false
@ -354,9 +353,9 @@ QtObject {
walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames) walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames)
} }
function deleteSavedAddress(address, ens) { function deleteSavedAddress(address) {
root.deletingSavedAddress = true root.deletingSavedAddress = true
walletSectionSavedAddresses.deleteSavedAddress(address, ens) walletSectionSavedAddresses.deleteSavedAddress(address)
} }
function savedAddressNameExists(name) { function savedAddressNameExists(name) {

View File

@ -99,8 +99,7 @@ ColumnLayout {
State { State {
name: "highlighted" name: "highlighted"
when: RootStore.lastCreatedSavedAddress ? (!RootStore.lastCreatedSavedAddress.error && when: RootStore.lastCreatedSavedAddress ? (!RootStore.lastCreatedSavedAddress.error &&
RootStore.lastCreatedSavedAddress.address.toLowerCase() === address.toLowerCase() && RootStore.lastCreatedSavedAddress.address.toLowerCase() === address.toLowerCase()) : false
RootStore.lastCreatedSavedAddress.ens === ens) : false
PropertyChanges { target: savedAddressDelegate; color: Theme.palette.baseColor2 } PropertyChanges { target: savedAddressDelegate; color: Theme.palette.baseColor2 }
StateChangeScript { StateChangeScript {
script: Qt.callLater(d.reset) script: Qt.callLater(d.reset)

View File

@ -1715,9 +1715,9 @@ Item {
Connections { Connections {
target: WalletStore.RootStore.walletSectionSavedAddressesInst target: WalletStore.RootStore.walletSectionSavedAddressesInst
function onSavedAddressAddedOrUpdated(added: bool, name: string, address: string, ens: string, errorMsg: string) { function onSavedAddressAddedOrUpdated(added: bool, name: string, address: string, errorMsg: string) {
WalletStore.RootStore.addingSavedAddress = false WalletStore.RootStore.addingSavedAddress = false
WalletStore.RootStore.lastCreatedSavedAddress = { address: address, ens: ens, error: errorMsg } WalletStore.RootStore.lastCreatedSavedAddress = { address: address, error: errorMsg }
if (!!errorMsg) { if (!!errorMsg) {
let mode = qsTr("adding") let mode = qsTr("adding")
@ -1783,7 +1783,7 @@ Item {
} }
onRemoveSavedAddress: { onRemoveSavedAddress: {
WalletStore.RootStore.deleteSavedAddress(address, ens) WalletStore.RootStore.deleteSavedAddress(address)
close() close()
} }
} }
@ -1791,9 +1791,8 @@ Item {
Connections { Connections {
target: WalletStore.RootStore.walletSectionSavedAddressesInst target: WalletStore.RootStore.walletSectionSavedAddressesInst
function onSavedAddressDeleted(name: string, address: string, ens: string, errorMsg: string) { function onSavedAddressDeleted(name: string, address: string, errorMsg: string) {
WalletStore.RootStore.deletingSavedAddress = false WalletStore.RootStore.deletingSavedAddress = false
WalletStore.RootStore.lastDeletedSavedAddress = { address: address, ens: ens, error: errorMsg }
if (!!errorMsg) { if (!!errorMsg) {