chore(savedaddresses): preparing saved address for reuse in wallet settings section

Part 1 of #13088
This commit is contained in:
Sale Djenic 2023-12-26 11:19:41 +01:00 committed by saledjenic
parent 4854d9d100
commit 3ac13a7678
25 changed files with 735 additions and 499 deletions

View File

@ -215,7 +215,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
# result.mnemonicService = mnemonic_service.newService() # result.mnemonicService = mnemonic_service.newService()
result.privacyService = privacy_service.newService(statusFoundation.events, result.settingsService, result.privacyService = privacy_service.newService(statusFoundation.events, result.settingsService,
result.accountsService) result.accountsService)
result.savedAddressService = saved_address_service.newService(statusFoundation.events, result.networkService, result.settingsService) result.savedAddressService = saved_address_service.newService(statusFoundation.threadpool, statusFoundation.events,
result.networkService, result.settingsService)
result.devicesService = devices_service.newService(statusFoundation.events, statusFoundation.threadpool, result.devicesService = devices_service.newService(statusFoundation.events, statusFoundation.threadpool,
result.settingsService, result.accountsService, result.walletAccountService) result.settingsService, result.accountsService, result.walletAccountService)
result.mailserversService = mailservers_service.newService(statusFoundation.events, statusFoundation.threadpool, result.mailserversService = mailservers_service.newService(statusFoundation.events, statusFoundation.threadpool,

View File

@ -1,6 +1,6 @@
import io_interface import io_interface
import ../../../../core/eventemitter import app/core/eventemitter
import ../../../../../app_service/service/saved_address/service as saved_address_service import app_service/service/saved_address/service as saved_address_service
type type
Controller* = ref object of RootObj Controller* = ref object of RootObj
@ -22,14 +22,22 @@ proc delete*(self: Controller) =
discard discard
proc init*(self: Controller) = proc init*(self: Controller) =
self.events.on(SIGNAL_SAVED_ADDRESS_CHANGED) do(e:Args): self.events.on(SIGNAL_SAVED_ADDRESSES_UPDATED) do(e:Args):
self.delegate.loadSavedAddresses() self.delegate.loadSavedAddresses()
self.events.on(SIGNAL_SAVED_ADDRESS_UPDATED) do(e:Args):
let args = SavedAddressArgs(e)
self.delegate.savedAddressUpdated(args.address, args.ens, args.errorMsg)
self.events.on(SIGNAL_SAVED_ADDRESS_DELETED) do(e:Args):
let args = SavedAddressArgs(e)
self.delegate.savedAddressDeleted(args.address, args.ens, args.errorMsg)
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()
proc createOrUpdateSavedAddress*(self: Controller, name: string, address: string, favourite: bool, chainShortNames: string, ens: string): string = proc createOrUpdateSavedAddress*(self: Controller, name: string, address: string, favourite: bool, chainShortNames: string, ens: string) =
return self.savedAddressService.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens) self.savedAddressService.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens)
proc deleteSavedAddress*(self: Controller, address: string, ens: string): string = proc deleteSavedAddress*(self: Controller, address: string, ens: string) =
return self.savedAddressService.deleteSavedAddress(address, ens) self.savedAddressService.deleteSavedAddress(address, ens)

View File

@ -17,10 +17,16 @@ method viewDidLoad*(self: AccessInterface) {.base.} =
method loadSavedAddresses*(self: AccessInterface) {.base.} = method loadSavedAddresses*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method createOrUpdateSavedAddress*(self: AccessInterface, name: string, address: string, favourite: bool, chainShortNames: string, ens: string): string {.base.} = method createOrUpdateSavedAddress*(self: AccessInterface, name: string, address: string, favourite: bool, chainShortNames: string, ens: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method deleteSavedAddress*(self: AccessInterface, address: string, ens: string): string {.base.} = method deleteSavedAddress*(self: AccessInterface, address: string, ens: string) {.base.} =
raise newException(ValueError, "No implementation available")
method savedAddressUpdated*(self: AccessInterface, address: string, ens: string, errorMsg: string) {.base.} =
raise newException(ValueError, "No implementation available")
method savedAddressDeleted*(self: AccessInterface, address: string, ens: string, errorMsg: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
type type

View File

@ -1,9 +1,9 @@
import NimQml, sugar, sequtils import NimQml, sugar, sequtils
import ../io_interface as delegate_interface import ../io_interface as delegate_interface
import ../../../../global/global_singleton import app/global/global_singleton
import ../../../../core/eventemitter import app/core/eventemitter
import ../../../../../app_service/service/saved_address/service as saved_address_service import app_service/service/saved_address/service as saved_address_service
import ./io_interface, ./view, ./controller, ./item import ./io_interface, ./view, ./controller, ./item
@ -57,8 +57,16 @@ method viewDidLoad*(self: Module) =
self.moduleLoaded = true self.moduleLoaded = true
self.delegate.savedAddressesModuleDidLoad() self.delegate.savedAddressesModuleDidLoad()
method createOrUpdateSavedAddress*(self: Module, name: string, address: string, favourite: bool, chainShortNames: string, ens: string): string = method createOrUpdateSavedAddress*(self: Module, name: string, address: string, favourite: bool, chainShortNames: string, ens: string) =
return self.controller.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens) self.controller.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens)
method deleteSavedAddress*(self: Module, address: string, ens: string): string = method deleteSavedAddress*(self: Module, address: string, ens: string) =
return self.controller.deleteSavedAddress(address, ens) self.controller.deleteSavedAddress(address, ens)
method savedAddressUpdated*(self: Module, address: string, ens: string, errorMsg: string) =
self.loadSavedAddresses()
self.view.savedAddressUpdated(address, ens, errorMsg)
method savedAddressDeleted*(self: Module, address: string, ens: string, errorMsg: string) =
self.loadSavedAddresses()
self.view.savedAddressDeleted(address, ens, errorMsg)

View File

@ -37,11 +37,15 @@ QtObject:
proc setItems*(self: View, items: seq[Item]) = proc setItems*(self: View, items: seq[Item]) =
self.model.setItems(items) self.model.setItems(items)
proc createOrUpdateSavedAddress*(self: View, name: string, address: string, favourite: bool, chainShortNames: string, ens: string): string {.slot.} = proc savedAddressUpdated*(self: View, address: string, ens: string, errorMsg: string) {.signal.}
return self.delegate.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens)
proc deleteSavedAddress*(self: View, address: string, ens: string): string {.slot.} = proc createOrUpdateSavedAddress*(self: View, name: string, address: string, favourite: bool, chainShortNames: string, ens: string) {.slot.} =
return self.delegate.deleteSavedAddress(address, ens) self.delegate.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens)
proc savedAddressDeleted*(self: View, address: string, ens: string, errorMsg: string) {.signal.}
proc deleteSavedAddress*(self: View, address: string, ens: string) {.slot.} =
self.delegate.deleteSavedAddress(address, ens)
proc getNameByAddress*(self: View, address: string): string {.slot.} = proc getNameByAddress*(self: View, address: string): string {.slot.} =
return self.model.getNameByAddress(address) return self.model.getNameByAddress(address)

View File

@ -0,0 +1,54 @@
include app_service/common/json_utils
include app/core/tasks/common
import backend/backend
type
SavedAddressTaskArg = ref object of QObjectTaskArg
name: string
address: string
favourite: bool
chainShortNames: string
ens: string
isTestAddress: bool
const upsertSavedAddressTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[SavedAddressTaskArg](argEncoded)
var response = %* {
"response": "",
"address": %* arg.address,
"ens": %* arg.ens,
"error": "",
}
try:
let rpcResponse = backend.upsertSavedAddress(backend.SavedAddress(
name: arg.name,
address: arg.address,
favourite: arg.favourite,
chainShortNames: arg.chainShortNames,
ens: arg.ens,
isTest: arg.isTestAddress)
)
if not rpcResponse.error.isNil:
raise newException(CatchableError, rpcResponse.error.message)
response["response"] = %* "ok"
except Exception as e:
response["error"] = %* e.msg
arg.finish(response)
const deleteSavedAddressTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[SavedAddressTaskArg](argEncoded)
var response = %* {
"response": "",
"address": %* arg.address,
"ens": %* arg.ens,
"error": "",
}
try:
let rpcResponse = backend.deleteSavedAddress(arg.address, arg.ens, arg.isTestAddress)
if not rpcResponse.error.isNil:
raise newException(CatchableError, rpcResponse.error.message)
response["response"] = %* "ok"
except Exception as e:
response["error"] = %* e.msg
arg.finish(response)

View File

@ -1,93 +1,144 @@
import chronicles, sequtils, json import NimQml, chronicles, sequtils, json
import dto import dto
import ../../../app/core/eventemitter import backend/backend
import ../../../backend/backend import app/core/eventemitter
import ../../../app/core/[main] import app/core/signals/types
import ../network/service as network_service import app/core/[main]
import ../settings/service as settings_service import app/core/tasks/[qt, threadpool]
import app_service/service/network/service as network_service
import app_service/service/settings/service as settings_service
export dto export dto
include async_tasks
logScope: logScope:
topics = "saved-address-service" topics = "saved-address-service"
# Signals which may be emitted by this service: # Signals which may be emitted by this service:
const SIGNAL_SAVED_ADDRESS_CHANGED* = "savedAddressChanged" const SIGNAL_SAVED_ADDRESSES_UPDATED* = "savedAddressesUpdated"
const SIGNAL_SAVED_ADDRESS_UPDATED* = "savedAddressUpdated"
const SIGNAL_SAVED_ADDRESS_DELETED* = "savedAddressDeleted"
type type
Service* = ref object of RootObj SavedAddressArgs* = ref object of Args
address*: string
ens*: string
errorMsg*: string
QtObject:
type Service* = ref object of QObject
threadpool: ThreadPool
events: EventEmitter events: EventEmitter
savedAddresses: seq[SavedAddressDto] savedAddresses: seq[SavedAddressDto]
networkService: network_service.Service networkService: network_service.Service
settingsService: settings_service.Service settingsService: settings_service.Service
proc delete*(self: Service) = proc delete*(self: Service) =
discard self.QObject.delete
proc newService*(events: EventEmitter, networkService: network_service.Service, proc newService*(threadpool: ThreadPool, events: EventEmitter, networkService: network_service.Service,
settingsService: settings_service.Service): Service = settingsService: settings_service.Service): Service =
result = Service() new(result, delete)
result.events = events result.QObject.setup
result.networkService = networkService result.threadpool = threadpool
result.settingsService = settingsService result.events = events
result.networkService = networkService
result.settingsService = settingsService
proc fetchAddresses(self: Service) = proc fetchAddresses(self: Service) =
try: try:
let response = backend.getSavedAddresses() let response = backend.getSavedAddresses()
self.savedAddresses = map( self.savedAddresses = map(
response.result.getElems(), response.result.getElems(),
proc(x: JsonNode): SavedAddressDto = toSavedAddressDto(x) proc(x: JsonNode): SavedAddressDto = toSavedAddressDto(x)
)
let chainId = self.networkService.getNetworkForEns().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) =
# Subscribe to sync events and check for changes
self.events.on(SignalType.Message.event) do(e:Args):
var data = MessageSignal(e)
if(len(data.savedAddresses) > 0):
self.updateAddresses()
self.fetchAddresses()
proc getSavedAddresses*(self: Service): seq[SavedAddressDto] =
return self.savedAddresses
proc createOrUpdateSavedAddress*(self: Service, name: string, address: string, favourite: bool, chainShortNames: string,
ens: string) =
let arg = SavedAddressTaskArg(
name: name,
address: address,
favourite: favourite,
chainShortNames: chainShortNames,
ens: ens,
isTestAddress: self.settingsService.areTestNetworksEnabled(),
tptr: cast[ByteAddress](upsertSavedAddressTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onSavedAddressCreatedOrUpdated",
) )
let chainId = self.networkService.getNetworkForEns().chainId self.threadpool.start(arg)
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: proc onSavedAddressCreatedOrUpdated*(self: Service, rpcResponse: string) {.slot.} =
error "error: ", procName="fetchAddress", errName = e.name, errDesription = e.msg var arg = SavedAddressArgs()
try:
let rpcResponseObj = rpcResponse.parseJson
if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
raise newException(CatchableError, rpcResponseObj{"error"}.getStr)
if rpcResponseObj{"response"}.kind != JNull and rpcResponseObj{"response"}.getStr != "ok":
raise newException(CatchableError, "invalid response")
proc updateAddresses(self: Service) = arg.address = rpcResponseObj{"address"}.getStr
self.fetchAddresses() arg.ens = rpcResponseObj{"ens"}.getStr
self.events.emit(SIGNAL_SAVED_ADDRESS_CHANGED, Args()) except Exception as e:
error "onSavedAddressCreatedOrUpdated", msg = e.msg
arg.errorMsg = e.msg
self.fetchAddresses()
self.events.emit(SIGNAL_SAVED_ADDRESS_UPDATED, arg)
proc init*(self: Service) = proc deleteSavedAddress*(self: Service, address: string, ens: string) =
# Subscribe to sync events and check for changes let arg = SavedAddressTaskArg(
self.events.on(SignalType.Message.event) do(e:Args): address: address,
var data = MessageSignal(e) ens: ens,
if(len(data.savedAddresses) > 0): isTestAddress: self.settingsService.areTestNetworksEnabled(),
self.updateAddresses() tptr: cast[ByteAddress](deleteSavedAddressTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onDeleteSavedAddress",
)
self.threadpool.start(arg)
self.fetchAddresses() proc onDeleteSavedAddress*(self: Service, rpcResponse: string) {.slot.} =
var arg = SavedAddressArgs()
try:
let rpcResponseObj = rpcResponse.parseJson
if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
raise newException(CatchableError, rpcResponseObj{"error"}.getStr)
if rpcResponseObj{"response"}.kind != JNull and rpcResponseObj{"response"}.getStr != "ok":
raise newException(CatchableError, "invalid response")
proc getSavedAddresses*(self: Service): seq[SavedAddressDto] = arg.address = rpcResponseObj{"address"}.getStr
return self.savedAddresses arg.ens = rpcResponseObj{"ens"}.getStr
except Exception as e:
proc createOrUpdateSavedAddress*(self: Service, name: string, address: string, favourite: bool, chainShortNames: string, ens: string): string = error "onDeleteSavedAddress", msg = e.msg
try: arg.errorMsg = e.msg
let isTestAddress = self.settingsService.areTestNetworksEnabled() self.fetchAddresses()
discard backend.upsertSavedAddress(backend.SavedAddress(name: name, address: address, favourite: favourite, chainShortNames: chainShortNames, ens: ens, isTest: isTestAddress)) self.events.emit(SIGNAL_SAVED_ADDRESS_DELETED, arg)
self.updateAddresses()
return ""
except Exception as e:
let errDesription = e.msg
error "error: ", errDesription
return errDesription
proc deleteSavedAddress*(self: Service, address: string, ens: string): string =
try:
let isTestAddress = self.settingsService.areTestNetworksEnabled()
var response = backend.deleteSavedAddress(address, ens, isTestAddress)
if not response.error.isNil:
raise newException(Exception, response.error.message)
self.updateAddresses()
return ""
except Exception as e:
let errDesription = e.msg
return errDesription

View File

@ -3,6 +3,7 @@ import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.13
import StatusQ.Layout 0.1 import StatusQ.Layout 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import utils 1.0 import utils 1.0
import shared.controls 1.0 import shared.controls 1.0
@ -18,7 +19,6 @@ Item {
id: root id: root
property bool hideSignPhraseModal: false property bool hideSignPhraseModal: false
property bool showAllAccounts: true
property var store property var store
property var contactsStore property var contactsStore
property var emojiPopup: null property var emojiPopup: null
@ -26,7 +26,9 @@ Item {
property var networkConnectionStore property var networkConnectionStore
property bool appMainVisible property bool appMainVisible
onAppMainVisibleChanged: showSigningPhrasePopup() onAppMainVisibleChanged: {
resetView()
}
onVisibleChanged: { onVisibleChanged: {
resetView() resetView()
@ -36,7 +38,7 @@ Item {
target: walletSection target: walletSection
function onFilterChanged(address, allAddresses) { function onFilterChanged(address, allAddresses) {
root.showAllAccounts = allAddresses RootStore.selectedAddress = allAddresses ? "" : address
} }
function onDisplayKeypairImportPopup() { function onDisplayKeypairImportPopup() {
@ -49,14 +51,54 @@ Item {
} }
function showSigningPhrasePopup(){ function showSigningPhrasePopup(){
if(!hideSignPhraseModal && !RootStore.hideSignPhraseModal && visible && appMainVisible){
}
function resetView() {
if (!visible || !appMainVisible) {
return
}
d.displayAllAddresses()
if (!!rightPanelStackView.currentItem.resetView) {
rightPanelStackView.currentItem.resetView()
}
if(!hideSignPhraseModal && !RootStore.hideSignPhraseModal){
signPhrasePopup.open(); signPhrasePopup.open();
} }
} }
function resetView() { QtObject {
if (!!rightPanelStackView.currentItem.resetView) id: d
rightPanelStackView.currentItem.resetView()
readonly property bool showSavedAddresses: RootStore.showSavedAddresses
onShowSavedAddressesChanged: {
if(showSavedAddresses) {
rightPanelStackView.replace(cmpSavedAddresses)
} else {
rightPanelStackView.replace(walletContainer)
}
RootStore.backButtonName = ""
}
function displayAllAddresses() {
RootStore.showSavedAddresses = false
RootStore.selectedAddress = ""
RootStore.setFillterAllAddresses()
}
function displayAddress(address) {
RootStore.showSavedAddresses = false
RootStore.selectedAddress = address
RootStore.setFilterAddress(address)
}
function displaySavedAddresses() {
RootStore.showSavedAddresses = true
RootStore.selectedAddress = ""
}
} }
SignPhraseModal { SignPhraseModal {
@ -74,11 +116,15 @@ Item {
Component { Component {
id: cmpSavedAddresses id: cmpSavedAddresses
SavedAddressesView { SavedAddressesView {
anchors.top: parent ? parent.top: undefined store: root.store
anchors.left: parent ? parent.left: undefined
anchors.right: parent ? parent.right: undefined
contactsStore: root.contactsStore contactsStore: root.contactsStore
sendModal: root.sendModalPopup sendModal: root.sendModalPopup
networkFilter.visible: false
headerButton.text: qsTr("Add new address")
headerButton.onClicked: {
Global.openAddEditSavedAddressesPopup({})
}
} }
} }
@ -89,7 +135,9 @@ Item {
contactsStore: root.contactsStore contactsStore: root.contactsStore
sendModal: root.sendModalPopup sendModal: root.sendModalPopup
networkConnectionStore: root.networkConnectionStore networkConnectionStore: root.networkConnectionStore
showAllAccounts: leftTab.showAllAccounts
headerButton.text: RootStore.overview.ens || StatusQUtils.Utils.elideText(RootStore.overview.mixedcaseAddress, 6, 4)
headerButton.visible: !RootStore.overview.isAllAccounts
onLaunchShareAddressModal: Global.openPopup(receiveModalComponent); onLaunchShareAddressModal: Global.openPopup(receiveModalComponent);
} }
} }
@ -110,24 +158,18 @@ Item {
leftPanel: LeftTabView { leftPanel: LeftTabView {
id: leftTab id: leftTab
anchors.fill: parent anchors.fill: parent
changeSelectedAccount: function(address) {
root.resetView()
RootStore.setFilterAddress(address)
}
selectAllAccounts: function() {
root.resetView()
RootStore.setFillterAllAddresses()
}
onCurrentAddressChanged: root.resetView()
onShowSavedAddressesChanged: {
if(showSavedAddresses)
rightPanelStackView.replace(cmpSavedAddresses)
else
rightPanelStackView.replace(walletContainer)
RootStore.backButtonName = ""
}
emojiPopup: root.emojiPopup emojiPopup: root.emojiPopup
networkConnectionStore: root.networkConnectionStore networkConnectionStore: root.networkConnectionStore
changeSelectedAccount: function(address) {
d.displayAddress(address)
}
selectAllAccounts: function() {
d.displayAllAddresses()
}
selectSavedAddresses: function() {
d.displaySavedAddresses()
}
} }
centerPanel: StackView { centerPanel: StackView {
@ -155,9 +197,9 @@ Item {
readonly property bool isCommunityCollectible: !!walletStore.currentViewedCollectible ? walletStore.currentViewedCollectible.communityId !== "" : false readonly property bool isCommunityCollectible: !!walletStore.currentViewedCollectible ? walletStore.currentViewedCollectible.communityId !== "" : false
readonly property bool isOwnerCommunityCollectible: isCommunityCollectible ? (walletStore.currentViewedCollectible.communityPrivilegesLevel === Constants.TokenPrivilegesLevel.Owner) : false readonly property bool isOwnerCommunityCollectible: isCommunityCollectible ? (walletStore.currentViewedCollectible.communityPrivilegesLevel === Constants.TokenPrivilegesLevel.Owner) : false
visible: !root.showAllAccounts visible: !RootStore.showAllAccounts
width: parent.width width: parent.width
height: root.showAllAccounts ? implicitHeight : 61 height: RootStore.showAllAccounts ? implicitHeight : 61
walletStore: RootStore walletStore: RootStore
networkConnectionStore: root.networkConnectionStore networkConnectionStore: root.networkConnectionStore
isCommunityOwnershipTransfer: footer.isHoldingSelected && footer.isOwnerCommunityCollectible isCommunityOwnershipTransfer: footer.isHoldingSelected && footer.isOwnerCommunityCollectible

View File

@ -27,7 +27,6 @@ StatusListItem {
property bool areTestNetworksEnabled: false property bool areTestNetworksEnabled: false
property bool isSepoliaEnabled: false property bool isSepoliaEnabled: false
property var saveAddress: function (name, address, favourite, chainShortNames, ens) {} property var saveAddress: function (name, address, favourite, chainShortNames, ens) {}
property var deleteSavedAddress: function (address, ens) {}
signal openSendModal(string recipient) signal openSendModal(string recipient)
@ -100,12 +99,11 @@ StatusListItem {
type: StatusRoundButton.Type.Tertiary type: StatusRoundButton.Type.Tertiary
icon.name: "add" icon.name: "add"
onClicked: { onClicked: {
Global.openPopup(addEditSavedAddress, Global.openAddEditSavedAddressesPopup({
{ addAddress: true,
addAddress: true, address: d.visibleAddress,
address: d.visibleAddress, ens: root.ens
ens: root.ens })
})
} }
} }
] ]
@ -142,15 +140,14 @@ StatusListItem {
objectName: "editroot" objectName: "editroot"
assetSettings.name: "pencil-outline" assetSettings.name: "pencil-outline"
onTriggered: { onTriggered: {
Global.openPopup(addEditSavedAddress, Global.openAddEditSavedAddressesPopup({
{ edit: true,
edit: true, address: editDeleteMenu.contactAddress,
address: editDeleteMenu.contactAddress, name: editDeleteMenu.contactName,
name: editDeleteMenu.contactName, favourite: editDeleteMenu.storeFavourite,
favourite: editDeleteMenu.storeFavourite, chainShortNames: editDeleteMenu.contactChainShortNames,
chainShortNames: editDeleteMenu.contactChainShortNames, ens: editDeleteMenu.contactEns
ens: editDeleteMenu.contactEns })
})
} }
} }
StatusAction { StatusAction {
@ -214,65 +211,13 @@ StatusListItem {
assetSettings.name: "delete" assetSettings.name: "delete"
objectName: "deleteSavedAddress" objectName: "deleteSavedAddress"
onTriggered: { onTriggered: {
deleteAddressConfirm.name = editDeleteMenu.contactName; Global.openDeleteSavedAddressesPopup({
deleteAddressConfirm.address = editDeleteMenu.contactAddress; name: editDeleteMenu.contactName,
deleteAddressConfirm.favourite = editDeleteMenu.storeFavourite; address: editDeleteMenu.contactAddress,
deleteAddressConfirm.ens = editDeleteMenu.contactEns favourite: editDeleteMenu.storeFavourite,
deleteAddressConfirm.open() ens: editDeleteMenu.contactEns
})
} }
} }
} }
Component {
id: addEditSavedAddress
AddEditSavedAddressPopup {
id: addEditModal
anchors.centerIn: parent
onClosed: destroy()
contactsStore: root.contactsStore
store: root.store
onSave: {
root.saveAddress(name, address, favourite, chainShortNames, ens)
close()
}
}
}
StatusModal {
id: deleteAddressConfirm
property string address
property string ens
property string name
property bool favourite
anchors.centerIn: parent
headerSettings.title: qsTr("Are you sure?")
headerSettings.subTitle: name
contentItem: StatusBaseText {
anchors.centerIn: parent
height: contentHeight + topPadding + bottomPadding
text: qsTr("Are you sure you want to remove '%1' from your saved addresses?").arg(name)
font.pixelSize: 15
color: Theme.palette.directColor1
wrapMode: Text.Wrap
topPadding: Style.current.padding
rightPadding: Style.current.padding
bottomPadding: Style.current.padding
leftPadding: Style.current.padding
}
rightButtons: [
StatusButton {
text: qsTr("Cancel")
onClicked: deleteAddressConfirm.close()
},
StatusButton {
type: StatusBaseButton.Type.Danger
objectName: "confirmDeleteSavedAddress"
text: qsTr("Delete")
onClicked: {
root.deleteSavedAddress(deleteAddressConfirm.address, deleteAddressConfirm.ens)
deleteAddressConfirm.close()
}
}
]
}
} }

View File

@ -1,19 +1,18 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13
import utils 1.0 import utils 1.0
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
Item { Item {
id: addEditError id: root
anchors.left: parent.left
anchors.right: parent.right
property alias text: label.text property alias text: label.text
implicitHeight: childrenRect.height
implicitWidth: childrenRect.width
StatusIcon { StatusIcon {
id: errorIcon id: errorIcon
icon: "warning" icon: "warning"
@ -21,6 +20,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
} }
StatusBaseText { StatusBaseText {
id: label id: label
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter

View File

@ -20,8 +20,10 @@ Item {
property var store property var store
property var walletStore property var walletStore
signal launchShareAddressModal() property alias headerButton: headerButton
signal switchHideWatchOnlyAccounts() property alias networkFilter: networkFilter
signal buttonClicked()
implicitHeight: 88 implicitHeight: 88
@ -37,19 +39,30 @@ Item {
objectName: "accountName" objectName: "accountName"
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: overview.isAllAccounts ? Theme.palette.directColor5 : Utils.getColorForId(overview.colorId) color: {
if (root.walletStore.showSavedAddresses)
return Theme.palette.directColor1
return overview.isAllAccounts ? Theme.palette.directColor5 : Utils.getColorForId(overview.colorId)
}
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
lineHeight: 38 lineHeight: 38
font.bold: true font.bold: true
font.pixelSize: 28 font.pixelSize: 28
text: overview.isAllAccounts ? qsTr("All Accounts") : overview.name text: {
if (root.walletStore.showSavedAddresses)
return qsTr("Saved addresses")
return overview.isAllAccounts ? qsTr("All Accounts") : overview.name
}
} }
StatusEmoji { StatusEmoji {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 28 Layout.preferredWidth: 28
Layout.preferredHeight: 28 Layout.preferredHeight: 28
emojiId: StatusQUtils.Emoji.iconId(overview.emoji ?? "", StatusQUtils.Emoji.size.big) || "" emojiId: !!root.overview && StatusQUtils.Emoji.iconId(root.overview.emoji ?? "", StatusQUtils.Emoji.size.big) || ""
visible: !overview.isAllAccounts visible: !root.walletStore.showSavedAddresses &&
!!root.overview && !root.overview.isAllAccounts
} }
} }
@ -59,27 +72,24 @@ Item {
Layout.topMargin: 5 Layout.topMargin: 5
StatusButton { StatusButton {
id: headerButton
Layout.preferredHeight: 38 Layout.preferredHeight: 38
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
spacing: 8 spacing: 8
size: StatusBaseButton.Size.Small size: StatusBaseButton.Size.Small
borderColor: Theme.palette.directColor7 borderColor: root.walletStore.showSavedAddresses? "transparent" : Theme.palette.directColor7
normalColor: Theme.palette.transparent normalColor: root.walletStore.showSavedAddresses? Theme.palette.primaryColor3 : Theme.palette.transparent
hoverColor: Theme.palette.baseColor2 hoverColor: root.walletStore.showSavedAddresses? Theme.palette.primaryColor2 : Theme.palette.baseColor2
font.weight: Font.Normal font.weight: root.walletStore.showSavedAddresses? Font.Medium : Font.Normal
textPosition: StatusBaseButton.TextPosition.Left textPosition: StatusBaseButton.TextPosition.Left
textColor: Theme.palette.baseColor1 textColor: root.walletStore.showSavedAddresses? Theme.palette.primaryColor1 : Theme.palette.baseColor1
text: overview.ens || StatusQUtils.Utils.elideText(overview.mixedcaseAddress, 6, 4)
icon.name: "invite-users" icon.name: root.walletStore.showSavedAddresses? "" : "invite-users"
icon.height: 16 icon.height: 16
icon.width: 16 icon.width: 16
icon.color: hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1 icon.color: hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1
onClicked: launchShareAddressModal()
visible: !overview.isAllAccounts
} }
// network filter // network filter
@ -101,13 +111,18 @@ Item {
RowLayout { RowLayout {
spacing: 4 spacing: 4
visible: !networkConnectionStore.accountBalanceNotAvailable visible: !root.walletStore.showSavedAddresses &&
!!root.networkConnectionStore &&
!networkConnectionStore.accountBalanceNotAvailable
StatusTextWithLoadingState { StatusTextWithLoadingState {
font.pixelSize: 28 font.pixelSize: 28
font.bold: true font.bold: true
customColor: Theme.palette.directColor1 customColor: Theme.palette.directColor1
text: loading ? Constants.dummyText : LocaleUtils.currencyAmountToLocaleString(root.overview.currencyBalance) text: loading ?
loading: root.overview.balanceLoading Constants.dummyText :
!!root.overview?
LocaleUtils.currencyAmountToLocaleString(root.overview.currencyBalance) : ""
loading: !!root.overview && root.overview.balanceLoading
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
lineHeight: 38 lineHeight: 38
} }

View File

@ -25,7 +25,7 @@ import ".."
StatusDialog { StatusDialog {
id: root id: root
closePolicy: Popup.CloseOnEscape closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
property bool edit: false property bool edit: false
property bool addAddress: false property bool addAddress: false
@ -35,10 +35,18 @@ StatusDialog {
property alias name: nameInput.text property alias name: nameInput.text
property bool favourite: false property bool favourite: false
property var contactsStore
property var store
signal save(string name, string address, string chainShortNames, string ens) property var allNetworks
function applyParams(params = {}) {
root.addAddress = params.addAddress?? false
root.address = params.address?? Constants.zeroAddress
root.ens = params.ens?? ""
root.edit = params.edit?? false
root.name = params.name?? ""
root.favourite = params.favourite?? false
root.chainShortNames = params.chainShortNames?? ""
}
QtObject { QtObject {
id: d id: d
@ -339,7 +347,10 @@ StatusDialog {
StatusButton { StatusButton {
text: root.edit ? qsTr("Save") : qsTr("Add address") text: root.edit ? qsTr("Save") : qsTr("Add address")
enabled: d.valid && d.dirty enabled: d.valid && d.dirty
onClicked: root.save(name, address, chainShortNames, ens) onClicked: {
RootStore.createOrUpdateSavedAddress(name, address, root.favourite, chainShortNames, ens)
root.close()
}
objectName: "addSavedAddress" objectName: "addSavedAddress"
} }
} }
@ -348,7 +359,7 @@ StatusDialog {
CloneModel { CloneModel {
id: allNetworksModelCopy id: allNetworksModelCopy
sourceModel: store.allNetworks sourceModel: root.allNetworks
roles: ["layer", "chainId", "chainColor", "chainName","shortName", "iconUrl"] roles: ["layer", "chainId", "chainColor", "chainName","shortName", "iconUrl"]
rolesOverride: [{ role: "isEnabled", transform: (modelData) => Boolean(false) }] rolesOverride: [{ role: "isEnabled", transform: (modelData) => Boolean(false) }]

View File

@ -311,8 +311,7 @@ StatusMenu {
} }
icon.name: "star-icon-outline" icon.name: "star-icon-outline"
onTriggered: { onTriggered: {
Global.openPopup(addEditSavedAddress, Global.openAddEditSavedAddressesPopup({
{
addAddress: true, addAddress: true,
address: d.selectedAddress, address: d.selectedAddress,
ens: d.addressEns, ens: d.addressEns,
@ -325,8 +324,7 @@ StatusMenu {
enabled: false enabled: false
text: qsTr("Edit saved address") text: qsTr("Edit saved address")
icon.name: "pencil-outline" icon.name: "pencil-outline"
onTriggered: Global.openPopup(addEditSavedAddress, onTriggered: Global.openAddEditSavedAddressesPopup({
{
edit: true, edit: true,
name: d.addressName, name: d.addressName,
address: d.selectedAddress, address: d.selectedAddress,
@ -351,21 +349,6 @@ StatusMenu {
onTriggered: root.openSendModal(d.selectedAddress) onTriggered: root.openSendModal(d.selectedAddress)
} }
Component {
id: addEditSavedAddress
AddEditSavedAddressPopup {
id: addEditModal
anchors.centerIn: parent
onClosed: destroy()
contactsStore: root.contactsStore
store: WalletStores.RootStore
onSave: {
RootStore.createOrUpdateSavedAddress(name, address, false, chainShortNames, ens)
close()
}
}
}
Component { Component {
id: addressQr id: addressQr
ReceiveModal { ReceiveModal {

View File

@ -3,3 +3,4 @@ ActivityFilterMenu 1.0 ActivityFilterMenu.qml
ActivityPeriodFilterSubMenu 1.0 filterSubMenus/ActivityPeriodFilterSubMenu.qml ActivityPeriodFilterSubMenu 1.0 filterSubMenus/ActivityPeriodFilterSubMenu.qml
ActivityTypeFilterSubMenu 1.0 filterSubMenus/ActivityTypeFilterSubMenu.qml ActivityTypeFilterSubMenu 1.0 filterSubMenus/ActivityTypeFilterSubMenu.qml
ReceiveModal 1.0 ReceiveModal.qml ReceiveModal 1.0 ReceiveModal.qml
AddEditSavedAddressPopup 1.0 AddEditSavedAddressPopup.qml

View File

@ -13,6 +13,14 @@ import StatusQ.Core.Utils 0.1 as SQUtils
QtObject { QtObject {
id: root id: root
property bool showSavedAddresses: false
property string selectedAddress: ""
readonly property bool showAllAccounts: !root.showSavedAddresses && !root.selectedAddress
property var lastCreatedSavedAddress
property bool addingSavedAddress: false
property bool deletingSavedAddress: false
readonly property TokensStore tokensStore: TokensStore {} readonly property TokensStore tokensStore: TokensStore {}
readonly property WalletAssetsStore walletAssetsStore: WalletAssetsStore { readonly property WalletAssetsStore walletAssetsStore: WalletAssetsStore {
walletTokensStore: tokensStore walletTokensStore: tokensStore
@ -41,6 +49,7 @@ QtObject {
// "walletSection" is a context property slow to lookup, so we cache it here // "walletSection" is a context property slow to lookup, so we cache it here
property var mainModuleInst: mainModule property var mainModuleInst: mainModule
property var walletSectionInst: walletSection property var walletSectionInst: walletSection
property var walletSectionSavedAddressesInst: walletSectionSavedAddresses
property var totalCurrencyBalance: walletSectionInst.totalCurrencyBalance property var totalCurrencyBalance: walletSectionInst.totalCurrencyBalance
property var activityController: walletSectionInst.activityController property var activityController: walletSectionInst.activityController
property var tmpActivityController: walletSectionInst.tmpActivityController property var tmpActivityController: walletSectionInst.tmpActivityController
@ -322,11 +331,13 @@ QtObject {
} }
function createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens) { function createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens) {
return walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens) root.addingSavedAddress = true
walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens)
} }
function deleteSavedAddress(address, ens) { function deleteSavedAddress(address, ens) {
return walletSectionSavedAddresses.deleteSavedAddress(address, ens) root.deletingSavedAddress = true
walletSectionSavedAddresses.deleteSavedAddress(address, ens)
} }
function toggleNetwork(chainId) { function toggleNetwork(chainId) {

View File

@ -35,9 +35,9 @@ Item {
QtObject { QtObject {
id: d id: d
property var marketValueStore : RootStore.marketValueStore property var marketValueStore : RootStore.marketValueStore
readonly property string symbol: root.token ? root.token.symbol : "" readonly property string symbol: !!root.token? root.token.symbol?? "" : ""
property bool marketDetailsLoading: token && token.marketDetailsLoading ? token.marketDetailsLoading: false property bool marketDetailsLoading: !!root.token? root.token.marketDetailsLoading?? false : false
property bool tokenDetailsLoading: token && token.detailsLoading? token.detailsLoading: false property bool tokenDetailsLoading: !!root.token? root.token.detailsLoading?? false: false
readonly property LeftJoinModel addressPerChainModel: LeftJoinModel { readonly property LeftJoinModel addressPerChainModel: LeftJoinModel {
leftModel: token && token.addressPerChain ? token.addressPerChain: null leftModel: token && token.addressPerChain ? token.addressPerChain: null

View File

@ -30,31 +30,7 @@ Rectangle {
property var networkConnectionStore property var networkConnectionStore
property var selectAllAccounts: function(){} property var selectAllAccounts: function(){}
property var changeSelectedAccount: function(){} property var changeSelectedAccount: function(){}
property bool showSavedAddresses: false property var selectSavedAddresses: function(){}
property bool showAllAccounts: true
property string currentAddress: ""
onCurrentAddressChanged: {
if (!currentAddress)
return
root.showAllAccounts = false
root.showSavedAddresses = false
}
onShowSavedAddressesChanged: {
if (!showSavedAddresses)
return
root.currentAddress = ""
root.showAllAccounts = false
}
onShowAllAccountsChanged: {
if (!showAllAccounts)
return
root.currentAddress = ""
root.showSavedAddresses = false
}
property var emojiPopup: null property var emojiPopup: null
color: Style.current.secondaryMenuBackground color: Style.current.secondaryMenuBackground
@ -169,10 +145,6 @@ Rectangle {
function onDestroyAddAccountPopup() { function onDestroyAddAccountPopup() {
addAccount.active = false addAccount.active = false
} }
function onFilterChanged(address, allAddresses) {
root.currentAddress = allAddresses ? "" : address
root.showAllAccounts = allAddresses
}
} }
MouseArea { MouseArea {
@ -273,7 +245,7 @@ Rectangle {
objectName: "walletAccount-" + model.name objectName: "walletAccount-" + model.name
readonly property bool itemLoaded: !model.assetsLoading // needed for e2e tests readonly property bool itemLoaded: !model.assetsLoading // needed for e2e tests
width: ListView.view.width - Style.current.padding * 2 width: ListView.view.width - Style.current.padding * 2
highlighted: root.currentAddress.toLowerCase() === model.address.toLowerCase() highlighted: RootStore.selectedAddress.toLowerCase() === model.address.toLowerCase()
onHighlightedChanged: { onHighlightedChanged: {
if (highlighted) if (highlighted)
ListView.view.currentIndex = index ListView.view.currentIndex = index
@ -324,7 +296,7 @@ Rectangle {
id: header id: header
verticalPadding: Style.current.padding verticalPadding: Style.current.padding
horizontalPadding: Style.current.padding horizontalPadding: Style.current.padding
highlighted: root.showAllAccounts highlighted: RootStore.showAllAccounts
objectName: "allAccountsBtn" objectName: "allAccountsBtn"
leftInset: Style.current.padding leftInset: Style.current.padding
@ -442,7 +414,7 @@ Rectangle {
contentItem: StatusFlatButton { contentItem: StatusFlatButton {
objectName: "savedAddressesBtn" objectName: "savedAddressesBtn"
highlighted: root.showSavedAddresses highlighted: RootStore.showSavedAddresses
hoverColor: Style.current.backgroundHover hoverColor: Style.current.backgroundHover
asset.bgColor: Theme.palette.primaryColor3 asset.bgColor: Theme.palette.primaryColor3
text: qsTr("Saved addresses") text: qsTr("Saved addresses")
@ -454,7 +426,7 @@ Rectangle {
textColor: Theme.palette.directColor1 textColor: Theme.palette.directColor1
textFillWidth: true textFillWidth: true
spacing: walletAccountsListView.firstItem.statusListItemTitleArea.anchors.leftMargin spacing: walletAccountsListView.firstItem.statusListItemTitleArea.anchors.leftMargin
onClicked: root.showSavedAddresses = true onClicked: root.selectSavedAddresses()
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent

View File

@ -0,0 +1,47 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import "../stores"
import "../panels"
FocusScope {
id: root
property var store
property var contactsStore
property var networkConnectionStore
property var sendModal
property alias header: header
property alias headerButton: header.headerButton
property alias networkFilter: header.networkFilter
default property Item content
Component.onCompleted: {
content.parent = contentWrapper
}
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
WalletHeader {
id: header
Layout.fillWidth: true
overview: RootStore.overview
store: root.store
walletStore: RootStore
networkConnectionStore: root.networkConnectionStore
}
Column {
id: contentWrapper
Layout.fillWidth: true
}
}
}

View File

@ -16,18 +16,17 @@ import "../stores"
import "../panels" import "../panels"
import "../views/collectibles" import "../views/collectibles"
Item { RightTabBaseView {
id: root id: root
property alias currentTabIndex: walletTabBar.currentIndex property alias currentTabIndex: walletTabBar.currentIndex
property var store
property var contactsStore
property var sendModal
property var networkConnectionStore
property bool showAllAccounts: false
signal launchShareAddressModal() signal launchShareAddressModal()
headerButton.onClicked: {
root.launchShareAddressModal()
}
function resetView() { function resetView() {
stack.currentIndex = 0 stack.currentIndex = 0
root.currentTabIndex = 0 root.currentTabIndex = 0
@ -38,48 +37,42 @@ Item {
stack.currentIndex = 0; stack.currentIndex = 0;
} }
Connections {
target: walletSection
function onFilterChanged() {
root.resetStack()
}
}
QtObject {
id: d
function getBackButtonText(index) {
switch(index) {
case 1:
return qsTr("Collectibles")
case 2:
return qsTr("Assets")
case 3:
return qsTr("Activity")
default:
return ""
}
}
}
StackLayout { StackLayout {
id: stack id: stack
anchors.fill: parent width: parent.width
Connections {
target: walletSection
function onFilterChanged() {
root.resetStack()
}
}
onCurrentIndexChanged: { onCurrentIndexChanged: {
RootStore.backButtonName = d.getBackButtonText(currentIndex) RootStore.backButtonName = d.getBackButtonText(currentIndex)
} }
ColumnLayout { QtObject {
spacing: 0 id: d
WalletHeader { function getBackButtonText(index) {
Layout.fillWidth: true switch(index) {
overview: RootStore.overview case 1:
store: root.store return qsTr("Collectibles")
walletStore: RootStore case 2:
networkConnectionStore: root.networkConnectionStore return qsTr("Assets")
onLaunchShareAddressModal: root.launchShareAddressModal() case 3:
onSwitchHideWatchOnlyAccounts: RootStore.toggleWatchOnlyAccounts() return qsTr("Activity")
default:
return ""
}
} }
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 0
ImportKeypairInfo { ImportKeypairInfo {
Layout.fillWidth: true Layout.fillWidth: true
@ -187,7 +180,7 @@ Item {
HistoryView { HistoryView {
id: historyView id: historyView
overview: RootStore.overview overview: RootStore.overview
showAllAccounts: root.showAllAccounts showAllAccounts: RootStore.showAllAccounts
sendModal: root.sendModal sendModal: root.sendModal
filterVisible: filterButton.checked filterVisible: filterButton.checked
onLaunchTransactionDetail: function (entry, entryIndex) { onLaunchTransactionDetail: function (entry, entryIndex) {
@ -214,7 +207,7 @@ Item {
allNetworksModel: RootStore.allNetworks allNetworksModel: RootStore.allNetworks
address: RootStore.overview.mixedcaseAddress address: RootStore.overview.mixedcaseAddress
showAllAccounts: root.showAllAccounts showAllAccounts: RootStore.showAllAccounts
currencyStore: RootStore.currencyStore currencyStore: RootStore.currencyStore
networkFilters: RootStore.networkFilters networkFilters: RootStore.networkFilters
@ -240,7 +233,7 @@ Item {
transaction = null transaction = null
} }
} }
showAllAccounts: root.showAllAccounts showAllAccounts: RootStore.showAllAccounts
sendModal: root.sendModal sendModal: root.sendModal
contactsStore: root.contactsStore contactsStore: root.contactsStore
visible: (stack.currentIndex === 3) visible: (stack.currentIndex === 3)

View File

@ -0,0 +1,114 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Components 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import SortFilterProxyModel 0.2
import shared.controls 1.0
import "../stores"
import "../controls"
ColumnLayout {
id: root
property var sendModal
property var contactsStore
QtObject {
id: d
function saveAddress(name, address, favourite, chainShortNames, ens) {
RootStore.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens)
}
function reset() {
RootStore.lastCreatedSavedAddress = undefined
}
}
SavedAddressesError {
id: error
Layout.alignment: Qt.AlignHCenter
text: RootStore.lastCreatedSavedAddress? RootStore.lastCreatedSavedAddress.error?? "" : ""
visible: !!text
height: visible ? 36 : 0
}
ShapeRectangle {
id: noSavedAddresses
Layout.fillWidth: true
visible: listView.count === 0
text: qsTr("Your saved addresses will appear here")
}
StatusLoadingIndicator {
id: loadingIndicator
Layout.alignment: Qt.AlignHCenter
visible: RootStore.addingSavedAddress || RootStore.deletingSavedAddress
color: Theme.palette.directColor4
}
Item {
visible: error.visible || noSavedAddresses.visible || loadingIndicator.visible
Layout.fillWidth: true
Layout.fillHeight: true
}
StatusListView {
id: listView
objectName: "SavedAddressesView_savedAddresses"
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 5
visible: count > 0
model: SortFilterProxyModel {
sourceModel: RootStore.savedAddresses
sorters: RoleSorter { roleName: "createdAt"; sortOrder: Qt.DescendingOrder }
}
delegate: SavedAddressesDelegate {
id: savedAddressDelegate
objectName: "savedAddressView_Delegate_" + name
name: model.name
address: model.address
chainShortNames: model.chainShortNames
ens: model.ens
favourite: model.favourite
store: RootStore
contactsStore: root.contactsStore
areTestNetworksEnabled: RootStore.areTestNetworksEnabled
isSepoliaEnabled: RootStore.isSepoliaEnabled
onOpenSendModal: root.sendModal.open(recipient);
saveAddress: function(name, address, favourite, chainShortNames, ens) {
d.saveAddress(name, address, favourite, chainShortNames, ens)
}
states: [
State {
name: "highlighted"
when: RootStore.lastCreatedSavedAddress ? (RootStore.lastCreatedSavedAddress.address.toLowerCase() === address.toLowerCase() &&
RootStore.lastCreatedSavedAddress.ens === ens) : false
PropertyChanges { target: savedAddressDelegate; color: Theme.palette.baseColor2 }
StateChangeScript {
script: Qt.callLater(d.reset)
}
}
]
transitions: [
Transition {
from: "highlighted"
ColorAnimation {
target: savedAddressDelegate
duration: 3000
}
}
]
}
}
}

View File

@ -1,174 +1,13 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import utils 1.0 RightTabBaseView {
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Popups 0.1
import shared.controls 1.0
import SortFilterProxyModel 0.2
import "../stores"
import "../popups"
import "../controls"
Item {
id: root id: root
anchors.leftMargin: Style.current.padding
anchors.rightMargin: Style.current.padding
property var sendModal SavedAddresses {
property var contactsStore width: root.width
height: root.height - header.height
QtObject { sendModal: root.sendModal
id: _internal contactsStore: root.contactsStore
property bool loading: false
property string error: ""
property var lastCreatedAddress // used to display animation for the newly saved address
function saveAddress(name, address, favourite, chainShortNames, ens) {
loading = true
error = RootStore.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens)
loading = false
}
function deleteSavedAddress(address, ens) {
loading = true
error = RootStore.deleteSavedAddress(address, ens)
loading = false
}
function resetLastCreatedAddress() {
lastCreatedAddress = undefined
}
}
Item {
id: header
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: btnAdd.height
StatusBaseText {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
id: title
text: qsTr("Saved addresses")
font.weight: Font.Bold
font.pixelSize: 28
color: Theme.palette.directColor1
}
StatusButton {
objectName: "addNewAddressBtn"
id: btnAdd
anchors.right: parent.right
anchors.top: parent.top
anchors.verticalCenter: parent.verticalCenter
size: StatusBaseButton.Size.Small
font.weight: Font.Medium
text: qsTr("Add new address")
visible: !_internal.loading
onClicked: {
Global.openPopup(addEditSavedAddress)
}
}
StatusLoadingIndicator {
anchors.centerIn: parent
visible: _internal.loading
color: Theme.palette.directColor4
}
}
SavedAddressesError {
id: errorMessage
anchors.top: header.bottom
anchors.topMargin: Style.current.padding
visible: _internal.error !== ""
text: _internal.error
height: visible ? 36 : 0
}
StatusBaseText {
anchors.centerIn: parent
visible: listView.count === 0
color: Theme.palette.baseColor1
text: qsTr("No saved addresses")
}
StatusListView {
id: listView
objectName: "SavedAddressesView_savedAddresses"
anchors.top: header.bottom
anchors.topMargin: Style.current.padding
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
spacing: 5
visible: count > 0
model: SortFilterProxyModel {
sourceModel: RootStore.savedAddresses
sorters: RoleSorter { roleName: "createdAt"; sortOrder: Qt.DescendingOrder }
}
delegate: SavedAddressesDelegate {
id: savedAddressDelegate
objectName: "savedAddressView_Delegate_" + name
name: model.name
address: model.address
chainShortNames: model.chainShortNames
ens: model.ens
favourite: model.favourite
store: RootStore
contactsStore: root.contactsStore
areTestNetworksEnabled: RootStore.areTestNetworksEnabled
isSepoliaEnabled: RootStore.isSepoliaEnabled
onOpenSendModal: root.sendModal.open(recipient);
saveAddress: function(name, address, favourite, chainShortNames, ens) {
_internal.saveAddress(name, address, favourite, chainShortNames, ens)
}
deleteSavedAddress: function(address, ens) {
_internal.deleteSavedAddress(address, ens)
}
states: [
State {
name: "highlighted"
when: _internal.lastCreatedAddress ? (_internal.lastCreatedAddress.address.toLowerCase() === address.toLowerCase() &&
_internal.lastCreatedAddress.ens === ens) : false
PropertyChanges { target: savedAddressDelegate; color: Theme.palette.baseColor2 }
StateChangeScript {
script: Qt.callLater(_internal.resetLastCreatedAddress)
}
}
]
transitions: [
Transition {
from: "highlighted"
ColorAnimation {
target: savedAddressDelegate
duration: 3000
}
}
]
}
}
Component {
id: addEditSavedAddress
AddEditSavedAddressPopup {
id: addEditModal
anchors.centerIn: parent
onClosed: destroy()
contactsStore: root.contactsStore
store: RootStore
onSave: {
_internal.lastCreatedAddress = { address: address, ens: ens }
_internal.saveAddress(name, address, favourite, chainShortNames, ens)
close()
}
}
} }
} }

View File

@ -1,2 +1,3 @@
CollectiblesView 1.0 CollectiblesView.qml CollectiblesView 1.0 CollectiblesView.qml
AssetsDetailView 1.0 AssetsDetailView.qml AssetsDetailView 1.0 AssetsDetailView.qml
SavedAddresses 1.0 SavedAddresses.qml

View File

@ -40,6 +40,7 @@ import AppLayouts.stores 1.0
import AppLayouts.Chat.stores 1.0 as ChatStores import AppLayouts.Chat.stores 1.0 as ChatStores
import AppLayouts.Communities.stores 1.0 import AppLayouts.Communities.stores 1.0
import AppLayouts.Wallet.stores 1.0 as WalletStore import AppLayouts.Wallet.stores 1.0 as WalletStore
import AppLayouts.Wallet.popups 1.0 as WalletPopups
import AppLayouts.Wallet.views.walletconnect 1.0 import AppLayouts.Wallet.views.walletconnect 1.0
import mainui.activitycenter.stores 1.0 import mainui.activitycenter.stores 1.0
@ -345,6 +346,14 @@ Item {
function onSwitchToCommunity(communityId: string) { function onSwitchToCommunity(communityId: string) {
appMain.communitiesStore.setActiveCommunity(communityId) appMain.communitiesStore.setActiveCommunity(communityId)
} }
function onOpenAddEditSavedAddressesPopup(params) {
addEditSavedAddress.open(params)
}
function onOpenDeleteSavedAddressesPopup(params) {
deleteSavedAddress.open(params)
}
} }
Connections { Connections {
@ -1233,7 +1242,7 @@ Item {
appMainVisible: appMain.visible appMainVisible: appMain.visible
} }
onLoaded: { onLoaded: {
item.showSigningPhrasePopup() item.resetView()
} }
} }
@ -1672,6 +1681,132 @@ Item {
} }
} }
Loader {
id: addEditSavedAddress
active: false
property var params
function open(params = {}) {
addEditSavedAddress.params = params
addEditSavedAddress.active = true
}
function close() {
addEditSavedAddress.active = false
}
onLoaded: {
addEditSavedAddress.item.applyParams(addEditSavedAddress.params)
addEditSavedAddress.item.open()
}
sourceComponent: WalletPopups.AddEditSavedAddressPopup {
allNetworks: RootStore.allNetworks
onClosed: {
addEditSavedAddress.close()
}
}
Connections {
target: WalletStore.RootStore.walletSectionSavedAddressesInst
function onSavedAddressUpdated(address: string, ens: string, errorMsg: string) {
WalletStore.RootStore.addingSavedAddress = false
if (!!errorMsg) {
WalletStore.RootStore.lastCreatedSavedAddress = { error: errorMsg }
return
}
WalletStore.RootStore.lastCreatedSavedAddress = { address: address, ens: ens }
}
}
}
Loader {
id: deleteSavedAddress
active: false
property var params
function open(params = {}) {
deleteSavedAddress.params = params
deleteSavedAddress.active = true
}
function close() {
deleteSavedAddress.active = false
}
onLoaded: {
deleteSavedAddress.item.address = deleteSavedAddress.params.address?? ""
deleteSavedAddress.item.ens = deleteSavedAddress.params.ens?? ""
deleteSavedAddress.item.name = deleteSavedAddress.params.name?? ""
deleteSavedAddress.item.favourite = deleteSavedAddress.params.favourite?? false
deleteSavedAddress.item.open()
}
sourceComponent: StatusModal {
property string address
property string ens
property string name
property bool favourite
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
headerSettings.title: qsTr("Are you sure?")
headerSettings.subTitle: name
contentItem: StatusBaseText {
anchors.centerIn: parent
height: contentHeight + topPadding + bottomPadding
text: qsTr("Are you sure you want to remove '%1' from your saved addresses?").arg(name)
font.pixelSize: 15
color: Theme.palette.directColor1
wrapMode: Text.Wrap
topPadding: Style.current.padding
rightPadding: Style.current.padding
bottomPadding: Style.current.padding
leftPadding: Style.current.padding
}
onClosed: {
deleteSavedAddress.close()
}
rightButtons: [
StatusButton {
text: qsTr("Cancel")
onClicked: close()
},
StatusButton {
type: StatusBaseButton.Type.Danger
objectName: "confirmDeleteSavedAddress"
text: qsTr("Delete")
onClicked: {
WalletStore.RootStore.deleteSavedAddress(address, ens)
close()
}
}
]
}
Connections {
target: WalletStore.RootStore.walletSectionSavedAddressesInst
function onSavedAddressDeleted(address: string, ens: string, errorMsg: string) {
WalletStore.RootStore.deletingSavedAddress = false
WalletStore.RootStore.lastCreatedSavedAddress = { error: errorMsg }
}
}
}
DropAreaPanel { DropAreaPanel {
id: rootDropAreaPanel id: rootDropAreaPanel

View File

@ -191,14 +191,6 @@ QtObject {
return walletSectionSavedAddresses.getEnsForAddress(address) return walletSectionSavedAddresses.getEnsForAddress(address)
} }
function createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens) {
return walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens)
}
function deleteSavedAddress(addresse, ens) {
return walletSectionSavedAddresses.deleteSavedAddress(address, ens)
}
function getCurrencyAmount(amount, symbol) { function getCurrencyAmount(amount, symbol) {
return currencyStore.getCurrencyAmount(amount, symbol) return currencyStore.getCurrencyAmount(amount, symbol)
} }

View File

@ -84,6 +84,9 @@ QtObject {
signal popupWalletConnect() signal popupWalletConnect()
signal openAddEditSavedAddressesPopup(var params)
signal openDeleteSavedAddressesPopup(var params)
function openProfilePopup(publicKey, parentPopup, cb) { function openProfilePopup(publicKey, parentPopup, cb) {
root.openProfilePopupRequested(publicKey, parentPopup, cb) root.openProfilePopupRequested(publicKey, parentPopup, cb)
} }