feat(wallet): add favorites for saved addresses

Depends on statug-go favourite flag extension and merging of `favourites`
with `saved_address` tables and API

Additional changes:

- Remove duplicate name instead of ESN

Closes: #6546
This commit is contained in:
Stefan 2022-08-18 16:44:48 +02:00 committed by Stefan Dunca
parent 95b1333758
commit 7af95eaada
17 changed files with 78 additions and 46 deletions

View File

@ -29,8 +29,8 @@ proc init*(self: Controller) =
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, address: string): string = proc createOrUpdateSavedAddress*(self: Controller, name: string, address: string, favourite: bool): string =
return self.savedAddressService.createOrUpdateSavedAddress(name, address) return self.savedAddressService.createOrUpdateSavedAddress(name, address, favourite)
proc deleteSavedAddress*(self: Controller, address: string): string = proc deleteSavedAddress*(self: Controller, address: string): string =
return self.savedAddressService.deleteSavedAddress(address) return self.savedAddressService.deleteSavedAddress(address)

View File

@ -17,7 +17,7 @@ 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, address: string): string {.base.} = method createOrUpdateSavedAddress*(self: AccessInterface, name: string, address: string, favourite: bool): string {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method deleteSavedAddress*(self: AccessInterface, address: string): string {.base.} = method deleteSavedAddress*(self: AccessInterface, address: string): string {.base.} =

View File

@ -4,18 +4,22 @@ type
Item* = object Item* = object
name: string name: string
address: string address: string
favourite: bool
proc initItem*( proc initItem*(
name: string, name: string,
address: string, address: string,
favourite: bool
): Item = ): Item =
result.name = name result.name = name
result.address = address result.address = address
result.favourite = favourite
proc `$`*(self: Item): string = proc `$`*(self: Item): string =
result = fmt"""AllTokensItem( result = fmt"""AllTokensItem(
name: {self.name}, name: {self.name},
address: {self.address}, address: {self.address},
favourite: {self.favourite},
]""" ]"""
proc getName*(self: Item): string = proc getName*(self: Item): string =
@ -23,3 +27,6 @@ proc getName*(self: Item): string =
proc getAddress*(self: Item): string = proc getAddress*(self: Item): string =
return self.address return self.address
proc getFavourite*(self: Item): bool =
return self.favourite

View File

@ -6,6 +6,7 @@ type
ModelRole {.pure.} = enum ModelRole {.pure.} = enum
Name = UserRole + 1, Name = UserRole + 1,
Address Address
Favourite
QtObject: QtObject:
type type
@ -43,6 +44,7 @@ QtObject:
{ {
ModelRole.Name.int:"name", ModelRole.Name.int:"name",
ModelRole.Address.int:"address", ModelRole.Address.int:"address",
ModelRole.Favourite.int:"favourite",
}.toTable }.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant = method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -60,6 +62,8 @@ QtObject:
result = newQVariant(item.getName()) result = newQVariant(item.getName())
of ModelRole.Address: of ModelRole.Address:
result = newQVariant(item.getAddress()) result = newQVariant(item.getAddress())
of ModelRole.Favourite:
result = newQVariant(item.getFavourite())
proc rowData(self: Model, index: int, column: string): string {.slot.} = proc rowData(self: Model, index: int, column: string): string {.slot.} =
if (index >= self.items.len): if (index >= self.items.len):
@ -68,6 +72,7 @@ QtObject:
case column: case column:
of "name": result = $item.getName() of "name": result = $item.getName()
of "address": result = $item.getAddress() of "address": result = $item.getAddress()
of "favourite": result = $item.getFavourite()
proc setItems*(self: Model, items: seq[Item]) = proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel() self.beginResetModel()

View File

@ -36,6 +36,7 @@ method loadSavedAddresses*(self: Module) =
savedAddresses.map(s => initItem( savedAddresses.map(s => initItem(
s.name, s.name,
s.address, s.address,
s.favourite
)) ))
) )
@ -53,8 +54,8 @@ method viewDidLoad*(self: Module) =
self.moduleLoaded = true self.moduleLoaded = true
self.delegate.savedAddressesModuleDidLoad() self.delegate.savedAddressesModuleDidLoad()
method createOrUpdateSavedAddress*(self: Module, name: string, address: string): string = method createOrUpdateSavedAddress*(self: Module, name: string, address: string, favourite: bool): string =
return self.controller.createOrUpdateSavedAddress(name, address) return self.controller.createOrUpdateSavedAddress(name, address, favourite)
method deleteSavedAddress*(self: Module, address: string): string = method deleteSavedAddress*(self: Module, address: string): string =
return self.controller.deleteSavedAddress(address) return self.controller.deleteSavedAddress(address)

View File

@ -37,8 +37,8 @@ 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): string {.slot.} = proc createOrUpdateSavedAddress*(self: View, name: string, address: string, favourite: bool): string {.slot.} =
return self.delegate.createOrUpdateSavedAddress(name, address) return self.delegate.createOrUpdateSavedAddress(name, address, favourite)
proc deleteSavedAddress*(self: View, address: string): string {.slot.} = proc deleteSavedAddress*(self: View, address: string): string {.slot.} =
return self.delegate.deleteSavedAddress(address) return self.delegate.deleteSavedAddress(address)

View File

@ -6,17 +6,21 @@ type
SavedAddressDto* = ref object of RootObj SavedAddressDto* = ref object of RootObj
name*: string name*: string
address*: string address*: string
favourite*: bool
proc newSavedAddressDto*( proc newSavedAddressDto*(
name: string, name: string,
address: string, address: string,
favourite: bool
): SavedAddressDto = ): SavedAddressDto =
return SavedAddressDto( return SavedAddressDto(
name: name, name: name,
address: address, address: address,
favourite: favourite
) )
proc toSavedAddressDto*(jsonObj: JsonNode): SavedAddressDto = proc toSavedAddressDto*(jsonObj: JsonNode): SavedAddressDto =
result = SavedAddressDto() result = SavedAddressDto()
discard jsonObj.getProp("name", result.name) discard jsonObj.getProp("name", result.name)
discard jsonObj.getProp("address", result.address) discard jsonObj.getProp("address", result.address)
discard jsonObj.getProp("favourite", result.favourite)

View File

@ -40,14 +40,17 @@ proc fetchAddresses(self: Service) =
proc init*(self: Service) = proc init*(self: Service) =
self.fetchAddresses() self.fetchAddresses()
proc updateAddresses(self: Service) =
self.fetchAddresses()
self.events.emit(SIGNAL_SAVED_ADDRESS_CHANGED, Args())
proc getSavedAddresses*(self: Service): seq[SavedAddressDto] = proc getSavedAddresses*(self: Service): seq[SavedAddressDto] =
return self.savedAddresses return self.savedAddresses
proc createOrUpdateSavedAddress*(self: Service, name, address: string): string = proc createOrUpdateSavedAddress*(self: Service, name: string, address: string, favourite: bool): string =
try: try:
discard backend.addSavedAddress(backend.SavedAddress(name: name, address: address)) discard backend.addSavedAddress(backend.SavedAddress(name: name, address: address, favourite: favourite))
self.fetchAddresses() self.updateAddresses()
self.events.emit(SIGNAL_SAVED_ADDRESS_CHANGED, Args())
return "" return ""
except Exception as e: except Exception as e:
let errDesription = e.msg let errDesription = e.msg
@ -56,13 +59,11 @@ proc createOrUpdateSavedAddress*(self: Service, name, address: string): string =
proc deleteSavedAddress*(self: Service, address: string): string = proc deleteSavedAddress*(self: Service, address: string): string =
try: try:
let response = backend.deleteSavedAddress(address) var response = backend.deleteSavedAddress(address)
if not response.error.isNil: if not response.error.isNil:
raise newException(Exception, response.error.message) raise newException(Exception, response.error.message)
self.fetchAddresses() self.updateAddresses()
self.events.emit(SIGNAL_SAVED_ADDRESS_CHANGED, Args())
return "" return ""
except Exception as e: except Exception as e:
let errDesription = e.msg let errDesription = e.msg

View File

@ -28,6 +28,7 @@ type
SavedAddress* = ref object of RootObj SavedAddress* = ref object of RootObj
name* {.serializedFieldName("name").}: string name* {.serializedFieldName("name").}: string
address* {.serializedFieldName("address").}: string address* {.serializedFieldName("address").}: string
favourite* {.serializedFieldName("favourite").}: bool
Network* = ref object of RootObj Network* = ref object of RootObj
chainId* {.serializedFieldName("chainId").}: int chainId* {.serializedFieldName("chainId").}: int

View File

@ -20,6 +20,7 @@ StatusDialog {
property bool edit: false property bool edit: false
property string address property string address
property alias name: nameInput.text property alias name: nameInput.text
property bool favourite: false
property var contactsStore property var contactsStore
signal save(string name, string address) signal save(string name, string address)

View File

@ -167,8 +167,8 @@ QtObject {
// walletModelV2Inst.collectiblesView.collections.getCollectionTraitMaxValue(collectionIndex, traitType).toString(); // walletModelV2Inst.collectiblesView.collections.getCollectionTraitMaxValue(collectionIndex, traitType).toString();
} }
function createOrUpdateSavedAddress(name, address) { function createOrUpdateSavedAddress(name, address, favourite) {
return walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address) return walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address, favourite)
} }
function deleteSavedAddress(address) { function deleteSavedAddress(address) {

View File

@ -6,6 +6,7 @@ import utils 1.0
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Popups 0.1 import StatusQ.Popups 0.1
import shared.controls 1.0 import shared.controls 1.0
@ -26,9 +27,9 @@ Item {
id: _internal id: _internal
property bool loading: false property bool loading: false
property string error: "" property string error: ""
function saveAddress(name, address) { function saveAddress(name, address, favourite) {
loading = true loading = true
error = RootStore.createOrUpdateSavedAddress(name, address) error = RootStore.createOrUpdateSavedAddress(name, address, favourite)
loading = false loading = false
} }
function deleteSavedAddress(address) { function deleteSavedAddress(address) {
@ -106,11 +107,12 @@ Item {
delegate: SavedAddressesDelegate { delegate: SavedAddressesDelegate {
name: model.name name: model.name
address: model.address address: model.address
favourite: model.favourite
store: RootStore store: RootStore
contactsStore: root.contactsStore contactsStore: root.contactsStore
onOpenSendModal: root.sendModal.open(address); onOpenSendModal: root.sendModal.open(address);
saveAddress: function(name, address) { saveAddress: function(name, address, favourite) {
_internal.saveAddress(name, address) _internal.saveAddress(name, address, favourite)
} }
deleteSavedAddress: function(address) { deleteSavedAddress: function(address) {
_internal.deleteSavedAddress(address) _internal.deleteSavedAddress(address)
@ -126,7 +128,7 @@ Item {
onClosed: destroy() onClosed: destroy()
contactsStore: root.contactsStore contactsStore: root.contactsStore
onSave: { onSave: {
_internal.saveAddress(name, address) _internal.saveAddress(name, address, favourite)
close() close()
} }
} }

View File

@ -6,6 +6,7 @@ import utils 1.0
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Popups 0.1 import StatusQ.Popups 0.1
import shared.controls 1.0 import shared.controls 1.0
@ -20,8 +21,11 @@ StatusListItem {
property var contactsStore property var contactsStore
property string name property string name
property string address property string address
property var saveAddress: function (name, address) {} property bool favourite: false
property var saveAddress: function (name, address, favourite) {}
property var deleteSavedAddress: function (address) {} property var deleteSavedAddress: function (address) {}
// TODO: fetch this from status-go
readonly property string ensName: ""
signal openSendModal() signal openSendModal()
@ -29,11 +33,11 @@ StatusListItem {
title: name title: name
objectName: name objectName: name
subTitle: name + " \u2022 " + Utils.getElidedCompressedPk(address) subTitle: (ensName.length > 0 ? ensName + " \u2022 " : "")
+ Utils.elideText(address, 6, 4)
color: "transparent" color: "transparent"
border.color: Theme.palette.baseColor5 border.color: Theme.palette.baseColor5
//TODO uncomment when #6456 is fixed titleTextIcon: root.favourite ? "star-icon" : ""
//titleTextIcon: RootStore.favouriteAddress ? "star-icon" : ""
statusListItemComponentsSlot.spacing: 0 statusListItemComponentsSlot.spacing: 0
property bool showButtons: sensor.containsMouse property bool showButtons: sensor.containsMouse
@ -51,22 +55,21 @@ StatusListItem {
store: root.store store: root.store
textToCopy: root.address textToCopy: root.address
}, },
//TODO uncomment when #6456 is fixed StatusRoundButton {
// StatusRoundButton { icon.color: root.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
// icon.color: root.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1 type: StatusRoundButton.Type.Tertiary
// type: StatusRoundButton.Type.Tertiary icon.name: root.favourite ? "unfavourite" : "favourite"
// icon.name: root.favouriteAddress ? "favourite" : "unfavourite" onClicked: {
// onClicked: { root.saveAddress(root.name, root.address, !root.favourite)
// RootStore.setFavourite(); }
// } },
// },
StatusRoundButton { StatusRoundButton {
visible: !!root.name visible: !!root.name
icon.color: root.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1 icon.color: root.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
type: StatusRoundButton.Type.Tertiary type: StatusRoundButton.Type.Tertiary
icon.name: "more" icon.name: "more"
onClicked: { onClicked: {
editDeleteMenu.openMenu(root.name, root.address); editDeleteMenu.openMenu(root.name, root.address, root.favourite);
} }
}, },
StatusRoundButton { StatusRoundButton {
@ -88,14 +91,17 @@ StatusListItem {
id: editDeleteMenu id: editDeleteMenu
property string contactName property string contactName
property string contactAddress property string contactAddress
function openMenu(name, address) { property bool storeFavourite
function openMenu(name, address, favourite) {
contactName = name; contactName = name;
contactAddress = address; contactAddress = address;
storeFavourite = favourite;
popup(); popup();
} }
onClosed: { onClosed: {
contactName = ""; contactName = "";
contactAddress = ""; contactAddress = "";
storeFavourite = false;
} }
StatusMenuItem { StatusMenuItem {
text: qsTr("Edit") text: qsTr("Edit")
@ -106,7 +112,8 @@ StatusListItem {
{ {
edit: true, edit: true,
address: editDeleteMenu.contactAddress, address: editDeleteMenu.contactAddress,
name: editDeleteMenu.contactName name: editDeleteMenu.contactName,
favourite: editDeleteMenu.storeFavourite
}) })
} }
} }
@ -119,6 +126,7 @@ StatusListItem {
onTriggered: { onTriggered: {
deleteAddressConfirm.name = editDeleteMenu.contactName; deleteAddressConfirm.name = editDeleteMenu.contactName;
deleteAddressConfirm.address = editDeleteMenu.contactAddress; deleteAddressConfirm.address = editDeleteMenu.contactAddress;
deleteAddressConfirm.favourite = editDeleteMenu.storeFavourite;
deleteAddressConfirm.open() deleteAddressConfirm.open()
} }
} }
@ -132,7 +140,7 @@ StatusListItem {
onClosed: destroy() onClosed: destroy()
contactsStore: root.contactsStore contactsStore: root.contactsStore
onSave: { onSave: {
root.saveAddress(name, address) root.saveAddress(name, address, favourite)
close() close()
} }
} }
@ -142,6 +150,7 @@ StatusListItem {
id: deleteAddressConfirm id: deleteAddressConfirm
property string address property string address
property string name property string name
property bool favourite
anchors.centerIn: parent anchors.centerIn: parent
header.title: qsTr("Are you sure?") header.title: qsTr("Are you sure?")
header.subTitle: name header.subTitle: name

View File

@ -21,6 +21,7 @@ StatusDialog {
property bool addAddress: false property bool addAddress: false
property string address property string address
property alias name: nameInput.text property alias name: nameInput.text
property bool favourite: false
property var contactsStore property var contactsStore
signal save(string name, string address) signal save(string name, string address)

View File

@ -184,8 +184,8 @@ QtObject {
return walletSectionSavedAddresses.getNameByAddress(address) return walletSectionSavedAddresses.getNameByAddress(address)
} }
function createOrUpdateSavedAddress(name, address) { function createOrUpdateSavedAddress(name, address, favourite) {
return walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address) return walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address, favourite)
} }
function deleteSavedAddress(address) { function deleteSavedAddress(address) {

View File

@ -100,8 +100,8 @@ Item {
store: RootStore store: RootStore
contactsStore: root.contactsStore contactsStore: root.contactsStore
onOpenSendModal: root.sendModal.open(address); onOpenSendModal: root.sendModal.open(address);
saveAddress: function(name, address) { saveAddress: function(name, address, favourite) {
RootStore.createOrUpdateSavedAddress(name, address) RootStore.createOrUpdateSavedAddress(name, address, favourite)
} }
deleteSavedAddress: function(address) { deleteSavedAddress: function(address) {
RootStore.deleteSavedAddress(address) RootStore.deleteSavedAddress(address)

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 1485b3b4c808dd875d30f17e4be842fcc44c8d35 Subproject commit 9db69df9a06badd099df462d92375f77fd69ff23