fix(@desktop/wallet): Re-add saved addresses view from Wallet V2

fixes #4747
This commit is contained in:
Khushboo Mehta 2022-03-01 11:14:13 +01:00 committed by Khushboo-dev-cpp
parent 02bc62a9a2
commit cc55f811b1
17 changed files with 468 additions and 35 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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..<self.savedAddresses.len:
if self.savedAddresses[i].address == address:
@ -69,5 +77,7 @@ method deleteSavedAddress(self: Service, address: string) =
break
self.events.emit(SIGNAL_SAVED_ADDRESS_CHANGED, Args())
return ""
except Exception as e:
let errDesription = e.msg
return errDesription

View File

@ -15,8 +15,8 @@ method init*(self: ServiceInterface) {.base.} =
method getSavedAddresses*(self: ServiceInterface): seq[SavedAddressDto] {.base.} =
raise newException(ValueError, "No implementation available")
method createOrUpdateSavedAddress*(self: ServiceInterface, name: string, address: string) {.base.} =
method createOrUpdateSavedAddress*(self: ServiceInterface, name: string, address: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method deleteSavedAddress*(self: ServiceInterface, address: string) {.base.} =
method deleteSavedAddress*(self: ServiceInterface, address: string): string {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -17,6 +17,7 @@ Item {
property bool hideSignPhraseModal: false
property var store
property var contactsStore
function showSigningPhrasePopup(){
if(!hideSignPhraseModal && !RootStore.hideSignPhraseModal){
@ -36,6 +37,24 @@ Item {
anchors.top: parent.top
}
Component {
id: cmpSavedAddresses
SavedAddressesView {
anchors.top: parent ? parent.top: undefined
anchors.left: parent ? parent.left: undefined
anchors.right: parent ? parent.right: undefined
contactsStore: walletView.contactsStore
}
}
Component {
id: walletContainer
RightTabView {
changeSelectedAccount: leftTab.changeSelectedAccount
store: walletView.store
}
}
StatusAppTwoPanelLayout {
anchors.top: seedPhraseWarning.bottom
@ -67,15 +86,26 @@ Item {
}
selectedAccountIndex = newIndex
RootStore.switchAccount(newIndex)
walletContainer.currentTabIndex = 0;
}
showSavedAddresses: function(showSavedAddresses) {
if(showSavedAddresses)
rightPanelStackView.replace(cmpSavedAddresses)
else
rightPanelStackView.replace(walletContainer)
}
}
rightPanel: RightTabView {
id: walletContainer
rightPanel: StackView {
id: rightPanelStackView
anchors.fill: parent
changeSelectedAccount: leftTab.changeSelectedAccount
store: walletView.store
initialItem: walletContainer
replaceEnter: Transition {
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 400; easing.type: Easing.OutCubic }
}
replaceExit: Transition {
NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 400; easing.type: Easing.OutCubic }
}
}
}
}

View File

@ -0,0 +1,32 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import utils 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
Item {
id: addEditError
anchors.left: parent.left
anchors.right: parent.right
property alias text: label.text
StatusIcon {
id: errorIcon
icon: "warning"
color: Theme.palette.dangerColor1
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
}
StatusBaseText {
id: label
anchors.verticalCenter: parent.verticalCenter
anchors.left: errorIcon.right
anchors.leftMargin: Style.current.halfPadding
font.pixelSize: 13
color: Theme.palette.dangerColor1
}
}

View File

@ -0,0 +1,106 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3
import utils 1.0
import shared.controls 1.0
import shared.panels 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import StatusQ.Popups 0.1
import "../stores"
StatusModal {
id: root
property bool edit: false
property string address
property alias name: nameInput.text
property var contactsStore
signal save(string name, string address)
QtObject {
id: _internal
property int validationMode: root.edit ?
StatusInput.ValidationMode.Always
: StatusInput.ValidationMode.OnlyWhenDirty
property bool valid: addressInput.isValid && nameInput.valid // TODO: Add network preference and emoji
property bool dirty: nameInput.input.dirty
}
width: 574
height: 490
header.title: edit ? qsTr("Edit saved address") : qsTr("Add saved address")
header.subTitle: edit ? name : ""
onOpened: {
if(edit) {
addressInput.input.text = root.address
}
nameInput.input.edit.forceActiveFocus(Qt.MouseFocusReason)
}
contentItem: Column {
anchors.left: parent.left
anchors.leftMargin: 8
anchors.right: parent.right
anchors.rightMargin: 10
height: childrenRect.height
topPadding: Style.current.xlPadding
spacing: Style.current.bigPadding
StatusInput {
id: nameInput
width: parent.width
input.implicitHeight: 56
input.placeholderText: qsTr("Enter a name")
label: qsTr("Name")
validators: [
StatusMinLengthValidator {
minLength: 1
errorMessage: qsTr("Name must not be blank")
}
]
validationMode: _internal.validationMode
}
// To-Do use StatusInput within the below component
Item {
width: parent.width
height: addressInput.height
RecipientSelector {
id: addressInput
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.top: parent.top
width: parent.width
accounts: RootStore.accounts
contactsStore: root.contactsStore
label: qsTr("Address")
labelFont.pixelSize: 15
labelFont.weight: Font.Normal
input.implicitHeight: 56
isSelectorVisible: false
addContactEnabled: false
onSelectedRecipientChanged: {
root.address = selectedRecipient.address
}
readOnly: root.edit
}
}
}
rightButtons: [
StatusButton {
text: root.edit ? qsTr("Save") : qsTr("Add address")
enabled: _internal.valid && _internal.dirty
onClicked: save(name, address)
}
]
}

View File

@ -191,11 +191,11 @@ QtObject {
}
function createOrUpdateSavedAddress(name, address) {
walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address)
return walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address)
}
function deleteSavedAddress(address) {
walletSectionSavedAddresses.deleteSavedAddress(address)
return walletSectionSavedAddresses.deleteSavedAddress(address)
}
function toggleNetwork(chainId) {

View File

@ -8,6 +8,8 @@ import shared 1.0
import shared.panels 1.0
import shared.controls 1.0
import StatusQ.Components 0.1
import "../controls"
import "../popups"
import "../stores"
@ -17,6 +19,7 @@ Rectangle {
property int selectedAccountIndex: 0
property var changeSelectedAccount: function(){}
property var showSavedAddresses: function(showSavedAddresses){}
function onAfterAddAccount () {
walletInfoContainer.changeSelectedAccount(RootStore.accounts.rowCount() - 1)
@ -128,21 +131,24 @@ Rectangle {
ScrollView {
anchors.bottom: parent.bottom
anchors.bottomMargin: btnSavedAddresses.height + Style.current.padding
anchors.top: walletValueTextContainer.bottom
anchors.topMargin: Style.current.padding
anchors.right: parent.right
anchors.left: parent.left
Layout.fillWidth: true
Layout.fillHeight: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: listView.contentHeight > 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)
}
}
}

View File

@ -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
}
}
}

View File

@ -486,6 +486,7 @@ Item {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillHeight: true
store: appMain.rootStore
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
}
Component {

View File

@ -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

View File

@ -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

View File

@ -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