From cc55f811b17f62a83aeb4f1b9a1f9f3f78f474f3 Mon Sep 17 00:00:00 2001 From: Khushboo Mehta Date: Tue, 1 Mar 2022 11:14:13 +0100 Subject: [PATCH] fix(@desktop/wallet): Re-add saved addresses view from Wallet V2 fixes #4747 --- .../saved_addresses/controller.nim | 8 +- .../saved_addresses/controller_interface.nim | 4 +- .../saved_addresses/io_interface.nim | 4 +- .../wallet_section/saved_addresses/module.nim | 8 +- .../wallet_section/saved_addresses/view.nim | 8 +- .../service/saved_address/service.nim | 20 +- .../saved_address/service_interface.nim | 4 +- ui/app/AppLayouts/Wallet/WalletLayout.qml | 40 +++- .../Wallet/controls/SavedAddressesError.qml | 32 +++ .../popups/AddEditSavedAddressPopup.qml | 106 +++++++++ ui/app/AppLayouts/Wallet/stores/RootStore.qml | 4 +- .../AppLayouts/Wallet/views/LeftTabView.qml | 30 ++- .../Wallet/views/SavedAddressesView.qml | 223 ++++++++++++++++++ ui/app/AppMain.qml | 1 + ui/imports/shared/controls/AddressInput.qml | 2 + .../shared/controls/ContactsListAndSearch.qml | 2 +- .../shared/controls/RecipientSelector.qml | 7 +- 17 files changed, 468 insertions(+), 35 deletions(-) create mode 100644 ui/app/AppLayouts/Wallet/controls/SavedAddressesError.qml create mode 100644 ui/app/AppLayouts/Wallet/popups/AddEditSavedAddressPopup.qml create mode 100644 ui/app/AppLayouts/Wallet/views/SavedAddressesView.qml diff --git a/src/app/modules/main/wallet_section/saved_addresses/controller.nim b/src/app/modules/main/wallet_section/saved_addresses/controller.nim index d6a485011e..c608940b86 100644 --- a/src/app/modules/main/wallet_section/saved_addresses/controller.nim +++ b/src/app/modules/main/wallet_section/saved_addresses/controller.nim @@ -31,8 +31,8 @@ method init*(self: Controller) = method getSavedAddresses*(self: Controller): seq[saved_address_service.SavedAddressDto] = return self.savedAddressService.getSavedAddresses() -method createOrUpdateSavedAddress*(self: Controller, name, address: string) = - self.savedAddressService.createOrUpdateSavedAddress(name, address) +method createOrUpdateSavedAddress*(self: Controller, name, address: string): string = + return self.savedAddressService.createOrUpdateSavedAddress(name, address) -method deleteSavedAddress*(self: Controller, address: string) = - self.savedAddressService.deleteSavedAddress(address) +method deleteSavedAddress*(self: Controller, address: string): string = + return self.savedAddressService.deleteSavedAddress(address) diff --git a/src/app/modules/main/wallet_section/saved_addresses/controller_interface.nim b/src/app/modules/main/wallet_section/saved_addresses/controller_interface.nim index 46f0c0e8a6..8d241a7a9f 100644 --- a/src/app/modules/main/wallet_section/saved_addresses/controller_interface.nim +++ b/src/app/modules/main/wallet_section/saved_addresses/controller_interface.nim @@ -13,10 +13,10 @@ method init*(self: AccessInterface) {.base.} = method getSavedAddresses*(self: AccessInterface): seq[saved_address_service.SavedAddressDto] {.base.} = raise newException(ValueError, "No implementation available") -method createOrUpdateSavedAddress*(self: AccessInterface, name, address: string) {.base.} = +method createOrUpdateSavedAddress*(self: AccessInterface, name, address: string): string {.base.} = raise newException(ValueError, "No implementation available") -method deleteSavedAddress*(self: AccessInterface, address: string) {.base.} = +method deleteSavedAddress*(self: AccessInterface, address: string): string {.base.} = raise newException(ValueError, "No implementation available") type diff --git a/src/app/modules/main/wallet_section/saved_addresses/io_interface.nim b/src/app/modules/main/wallet_section/saved_addresses/io_interface.nim index 82ac062f68..b02e287614 100644 --- a/src/app/modules/main/wallet_section/saved_addresses/io_interface.nim +++ b/src/app/modules/main/wallet_section/saved_addresses/io_interface.nim @@ -17,10 +17,10 @@ method viewDidLoad*(self: AccessInterface) {.base.} = method loadSavedAddresses*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method createOrUpdateSavedAddress*(self: AccessInterface, name, address: string) {.base.} = +method createOrUpdateSavedAddress*(self: AccessInterface, name, address: string): string {.base.} = raise newException(ValueError, "No implementation available") -method deleteSavedAddress*(self: AccessInterface, address: string) {.base.} = +method deleteSavedAddress*(self: AccessInterface, address: string): string {.base.} = raise newException(ValueError, "No implementation available") type diff --git a/src/app/modules/main/wallet_section/saved_addresses/module.nim b/src/app/modules/main/wallet_section/saved_addresses/module.nim index be7b52119a..373785d4ca 100644 --- a/src/app/modules/main/wallet_section/saved_addresses/module.nim +++ b/src/app/modules/main/wallet_section/saved_addresses/module.nim @@ -53,8 +53,8 @@ method viewDidLoad*(self: Module) = self.moduleLoaded = true self.delegate.savedAddressesModuleDidLoad() -method createOrUpdateSavedAddress*(self: Module, name: string, address: string) = - self.controller.createOrUpdateSavedAddress(name, address) +method createOrUpdateSavedAddress*(self: Module, name: string, address: string): string = + return self.controller.createOrUpdateSavedAddress(name, address) -method deleteSavedAddress*(self: Module, address: string) = - self.controller.deleteSavedAddress(address) +method deleteSavedAddress*(self: Module, address: string): string = + return self.controller.deleteSavedAddress(address) diff --git a/src/app/modules/main/wallet_section/saved_addresses/view.nim b/src/app/modules/main/wallet_section/saved_addresses/view.nim index 64b0fe242d..10d66a72e5 100644 --- a/src/app/modules/main/wallet_section/saved_addresses/view.nim +++ b/src/app/modules/main/wallet_section/saved_addresses/view.nim @@ -37,8 +37,8 @@ QtObject: proc setItems*(self: View, items: seq[Item]) = self.model.setItems(items) - proc createOrUpdateSavedAddress*(self: View, name: string, address: string) {.slot.} = - self.delegate.createOrUpdateSavedAddress(name, address) + proc createOrUpdateSavedAddress*(self: View, name: string, address: string): string {.slot.} = + return self.delegate.createOrUpdateSavedAddress(name, address) - proc deleteSavedAddress*(self: View, address: string) {.slot.} = - self.delegate.deleteSavedAddress(address) + proc deleteSavedAddress*(self: View, address: string): string {.slot.} = + return self.delegate.deleteSavedAddress(address) diff --git a/src/app_service/service/saved_address/service.nim b/src/app_service/service/saved_address/service.nim index 2f63b62ea2..671be10c6f 100644 --- a/src/app_service/service/saved_address/service.nim +++ b/src/app_service/service/saved_address/service.nim @@ -40,9 +40,13 @@ method init*(self: Service) = method getSavedAddresses(self: Service): seq[SavedAddressDto] = return self.savedAddresses -method createOrUpdateSavedAddress(self: Service, name, address: string) = +method createOrUpdateSavedAddress(self: Service, name, address: string): string = try: - discard backend.addSavedAddress(name, address) + let response = backend.addSavedAddress(name, address) + + if not response.error.isNil: + raise newException(Exception, response.error.message) + var found = false for savedAddress in self.savedAddresses: if savedAddress.address == address: @@ -54,14 +58,18 @@ method createOrUpdateSavedAddress(self: Service, name, address: string) = self.savedAddresses.add(newSavedAddressDto(name, address)) self.events.emit(SIGNAL_SAVED_ADDRESS_CHANGED, Args()) + return "" except Exception as e: let errDesription = e.msg error "error: ", errDesription - return + return errDesription -method deleteSavedAddress(self: Service, address: string) = +method deleteSavedAddress(self: Service, address: string): string = try: - discard backend.deleteSavedAddress(address) + let response = backend.deleteSavedAddress(address) + + if not response.error.isNil: + raise newException(Exception, response.error.message) for i in 0.. listView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff + clip: true ListView { id: listView spacing: 5 - anchors.fill: parent + anchors.top: parent.top + width: parent.width + height: parent.height boundsBehavior: Flickable.StopAtBounds + clip: true delegate: WalletDelegate { currency: RootStore.currentCurrency @@ -150,6 +156,7 @@ Rectangle { selectedAccountIndex: walletInfoContainer.selectedAccountIndex onClicked: { changeSelectedAccount(index) + showSavedAddresses(false) } } @@ -157,4 +164,21 @@ Rectangle { // model: RootStore.exampleWalletModel } } + + StatusNavigationListItem { + id: btnSavedAddresses + + anchors.bottom: parent.bottom + anchors.bottomMargin: Style.current.halfPadding + anchors.left: parent.left + anchors.leftMargin: Style.current.smallPadding + anchors.right: parent.right + anchors.rightMargin: Style.current.smallPadding + + title: qsTr("Saved addresses") + icon.name: "address" + onClicked: { + showSavedAddresses(true) + } + } } diff --git a/ui/app/AppLayouts/Wallet/views/SavedAddressesView.qml b/ui/app/AppLayouts/Wallet/views/SavedAddressesView.qml new file mode 100644 index 0000000000..9e7a8489e5 --- /dev/null +++ b/ui/app/AppLayouts/Wallet/views/SavedAddressesView.qml @@ -0,0 +1,223 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.13 + +import utils 1.0 + +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 "../popups" +import "../controls" +import "../stores" + +Item { + id: root + anchors.leftMargin: 80 + anchors.rightMargin: 80 + anchors.topMargin: 62 + + property var contactsStore + + QtObject { + id: _internal + property bool loading: false + property string error: "" + } + + Item { + id: header + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + height: btnAdd.height + + Row { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: btnAdd.left + spacing: 10 + + StatusIcon { + icon: "address" + color: Theme.palette.primaryColor1 + width: undefined + height: 35 + anchors.verticalCenter: parent.verticalCenter + } + StatusBaseText { + id: title + text: qsTr("Saved addresses") + font.weight: Font.Medium + font.pixelSize: 28 + anchors.verticalCenter: parent.verticalCenter + color: Theme.palette.directColor1 + } + } + StatusButton { + id: btnAdd + anchors.right: parent.right + anchors.top: parent.top + text: "Add new +" + leftPadding: 8 + rightPadding: 11 + visible: !_internal.loading + onClicked: { + Global.openPopup(addEditSavedAddress) + } + } + StatusLoadingIndicator { + anchors.centerIn: parent + visible: _internal.loading + color: Theme.palette.directColor4 + } + } + + Component { + id: delegateSavedAddress + StatusListItem { + id: savedAddress + title: name + subTitle: address + icon.name: "wallet" + implicitWidth: parent.width + property bool showButtons: sensor.containsMouse + components: [ + StatusRoundButton { + color: hovered ? Theme.palette.dangerColor2 : Theme.palette.dangerColor3 + icon.color: Theme.palette.dangerColor1 + visible: showButtons + icon.name: "delete" + onClicked: { + deleteAddressConfirm.name = name + deleteAddressConfirm.address = address + deleteAddressConfirm.open() + } + }, + StatusRoundButton { + icon.name: "pencil" + visible: showButtons + onClicked: Global.openPopup(addEditSavedAddress, + { + edit: true, + address: address, + name: name + }) + }, + StatusRoundButton { + icon.name: "send" + visible: showButtons + }, + StatusRoundButton { + color: hovered ? Theme.palette.pinColor2 : Theme.palette.pinColor3 + icon.color: Theme.palette.pinColor1 + icon.name: "favourite" + visible: showButtons + } + ] + } + } + + Component { + id: addEditSavedAddress + AddEditSavedAddressPopup { + id: addEditModal + anchors.centerIn: parent + onClosed: destroy() + contactsStore: root.contactsStore + onSave: { + _internal.loading = true + _internal.error = RootStore.createOrUpdateSavedAddress(name, address) + _internal.loading = false + close() + } + } + } + + StatusModal { + id: deleteAddressConfirm + property string address + property string name + // NOTE: the `text` property was created as a workaround because + // setting StatusBaseText.text to `qsTr("...").arg("...")` + // caused no text to render + property string text: qsTr("Are you sure you want to remove '%1' from your saved addresses?").arg(name) + anchors.centerIn: parent + header.title: "Are you sure?" + header.subTitle: name + contentItem: StatusBaseText { + anchors.centerIn: parent + height: contentHeight + topPadding + bottomPadding + text: deleteAddressConfirm.text + 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 + text: qsTr("Delete") + onClicked: { + _internal.loading = true + _internal.error = RootStore.deleteSavedAddress(deleteAddressConfirm.address) + deleteAddressConfirm.close() + _internal.loading = false + } + } + ] + } + + SavedAddressesError { + id: errorMessage + anchors.top: header.bottom + anchors.topMargin: Style.current.padding + visible: _internal.error !== "" + text: _internal.error + height: visible ? 36 : 0 + } + + StatusBaseText { + anchors.top: errorMessage.bottom + anchors.topMargin: Style.current.padding + anchors.centerIn: parent + Layout.fillWidth: true + Layout.fillHeight: true + visible: listView.count === 0 + color: Theme.palette.baseColor1 + text: qsTr("No saved addresses") + } + + ScrollView { + anchors.top: errorMessage.bottom + anchors.topMargin: Style.current.padding + anchors.bottom: parent.bottom + anchors.bottomMargin: Style.current.halfPadding + anchors.right: parent.right + anchors.left: parent.left + visible: listView.count > 0 + Layout.fillWidth: true + Layout.fillHeight: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + + ListView { + id: listView + model: RootStore.savedAddresses + clip: true + spacing: 5 + anchors.fill: parent + boundsBehavior: Flickable.StopAtBounds + delegate: delegateSavedAddress + } + } +} diff --git a/ui/app/AppMain.qml b/ui/app/AppMain.qml index 37d93cfb79..b6760749f8 100644 --- a/ui/app/AppMain.qml +++ b/ui/app/AppMain.qml @@ -486,6 +486,7 @@ Item { Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.fillHeight: true store: appMain.rootStore + contactsStore: appMain.rootStore.profileSectionStore.contactsStore } Component { diff --git a/ui/imports/shared/controls/AddressInput.qml b/ui/imports/shared/controls/AddressInput.qml index d619987b6e..28c4a54141 100644 --- a/ui/imports/shared/controls/AddressInput.qml +++ b/ui/imports/shared/controls/AddressInput.qml @@ -20,6 +20,7 @@ Item { property alias isPending: contactFieldAndList.loading property bool isResolvedAddress: false property int parentWidth + property bool addContactEnabled: true height: contactFieldAndList.chatKey.height @@ -52,6 +53,7 @@ Item { anchors.bottom: parent.bottom width: parent.width showContactList: false + addContactEnabled: root.addContactEnabled contactsStore: root.contactsStore diff --git a/ui/imports/shared/controls/ContactsListAndSearch.qml b/ui/imports/shared/controls/ContactsListAndSearch.qml index eef716d564..30f909eaa6 100644 --- a/ui/imports/shared/controls/ContactsListAndSearch.qml +++ b/ui/imports/shared/controls/ContactsListAndSearch.qml @@ -138,7 +138,7 @@ Item { anchors.rightMargin: Style.current.padding anchors.verticalCenter: parent.verticalCenter icon.name: "clear" - visible: chatKey.text !== "" + visible: chatKey.text !== "" && !chatKey.readOnly icon.width: 20 icon.height: 20 type: StatusFlatRoundButton.Type.Tertiary diff --git a/ui/imports/shared/controls/RecipientSelector.qml b/ui/imports/shared/controls/RecipientSelector.qml index 689bea2189..982fbc338e 100644 --- a/ui/imports/shared/controls/RecipientSelector.qml +++ b/ui/imports/shared/controls/RecipientSelector.qml @@ -19,6 +19,8 @@ Item { property int inputWidth: 272 property int sourceSelectWidth: 136 property alias label: txtLabel.text + property alias labelFont: txtLabel.font + property alias input: inpAddress.input // If supplied, additional info will be displayed top-right in danger colour (red) property alias additionalInfo: txtAddlInfo.text property var selectedRecipient @@ -27,6 +29,8 @@ Item { //% "Invalid ethereum address" readonly property string addressValidationError: qsTrId("invalid-ethereum-address") property bool isValid: false + property bool isSelectorVisible: true + property bool addContactEnabled: true property bool isPending: { if (!selAddressSource.selectedSource) { return false @@ -159,6 +163,7 @@ Item { Layout.fillWidth: true validationError: root.addressValidationError parentWidth: parent.width + addContactEnabled: root.addContactEnabled onSelectedAddressChanged: { if (!selAddressSource.selectedSource || (selAddressSource.selectedSource && selAddressSource.selectedSource.value !== RecipientSelector.Type.Address)) { return @@ -210,7 +215,7 @@ Item { } AddressSourceSelector { id: selAddressSource - visible: !root.readOnly + visible: isSelectorVisible && !root.readOnly sources: root.sources.filter(source => source.visible) width: sourceSelectWidth Layout.preferredWidth: root.sourceSelectWidth