status-desktop/ui/app/AppLayouts/Wallet/adaptors/RecipientViewAdaptor.qml
Cuteivist 28690379e1
feat: Simple send modal recipient view (#17096)
* feat: Simple send modal recipient view

* feat: Handle duplicate entries in recent recipient view
2025-01-27 17:35:59 +01:00

274 lines
9.7 KiB
QML

import QtQuick 2.15
import SortFilterProxyModel 0.2
import StatusQ 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1
import utils 1.0
QObject {
id: root
/**
Output roles:
address [string] - unique wallet address
name [string] (optional)
color [string] (optional)
colorId [string] (optional)
emoji [string] (optional)
ens [string] (optional)
*/
// input API
/**
Expected model structure:
address [string]
name [string]
colorId [string]
emoji [string]
ens [string]
**/
required property var savedAddressesModel
/**
Expected model structure:
address [string]
name [string]
color [string]
colorId [string]
emoji [string]
**/
required property var accountsModel
/**
Expected model structure:
activityEntry:
sender [string] - sender wallet address
recipient [string] - recipient wallet address
txType [int] - transaction type (Constants.TransactionType)
**/
required property var recentRecipientsModel
/** Currently selected recipient type **/
property int selectedRecipientType
/** Search pattern for filtering recipients **/
property string searchPattern
/** Currently selected sender address **/
property string selectedSenderAddress
readonly property SortFilterProxyModel recipientsModel: SortFilterProxyModel {
objectName: "RecipientViewAdaptor_recipientsModel"
sourceModel: concatModel
function isSameModelType(recipientType, whichModel) {
switch(recipientType) {
case Constants.RecipientAddressObjectType.RecentsAddress:
return whichModel === concatModel.recentRecipientsModelKey
case Constants.RecipientAddressObjectType.SavedAddress:
return whichModel === concatModel.savedAddressesModelKey
case Constants.RecipientAddressObjectType.Account:
return whichModel === concatModel.accountsModelKey
default:
return true
}
}
filters: [
FastExpressionFilter {
expectedRoles: ["which_model"]
expression: root.recipientsModel.isSameModelType(root.selectedRecipientType, model.which_model)
}
]
}
readonly property SortFilterProxyModel recipientsFilterModel: SortFilterProxyModel {
objectName: "RecipientViewAdaptor_recipientsFilterModel"
sourceModel: concatModel
filters: [
ValueFilter {
enabled: !!root.searchPattern
// Ignore duplicates from recents model
roleName: "cherrypicked"
value: true
inverted: true
},
AnyOf {
enabled: !!root.searchPattern
RegExpFilter {
roleName: "name"
caseSensitivity: Qt.CaseInsensitive
pattern: `^${root.searchPattern}.*`
}
RegExpFilter {
roleName: "address"
caseSensitivity: Qt.CaseInsensitive
pattern: `^${root.searchPattern}.*`
}
RegExpFilter {
roleName: "ens"
caseSensitivity: Qt.CaseInsensitive
pattern: `^${root.searchPattern}.*`
}
}
]
}
ConcatModel {
id: concatModel
objectName: "RecipientViewAdaptor_concatModel"
readonly property string accountsModelKey: "accounts_model"
readonly property string savedAddressesModelKey: "saved_addresses_model"
readonly property string recentRecipientsModelKey: "recents_model"
sources: [
SourceModel {
model: accountsModelProxyModel
markerRoleValue: concatModel.accountsModelKey
},
SourceModel {
model: root.savedAddressesModel
markerRoleValue: concatModel.savedAddressesModelKey
},
SourceModel {
model: recentsModel
markerRoleValue: concatModel.recentRecipientsModelKey
}
]
expectedRoles: ["name", "address", "color", "colorId", "emoji", "ens", "cherrypicked", "duplicate"]
markerRoleName: "which_model"
}
SortFilterProxyModel {
id: recentsModel
objectName: "RecipientViewAdaptor_recentsModel"
function isValidEntry(entry) {
return entry && (entry.txType === Constants.TransactionType.Receive || entry.txType === Constants.TransactionType.Send)
}
function getAddressFromEntry(entry) {
return entry.txType === Constants.TransactionType.Receive ? entry.sender : entry.recipient
}
function refreshRecentsModelFlatList() {
let list = []
for (let i = 0 ; i < recentRecipientsExtractModel.count ; i++) {
const address = getAddressFromEntry(ModelUtils.get(recentRecipientsExtractModel, i, "activityEntry"))
if (!!address)
list.push(address)
}
recentRecipientsAddressesFlatList = list
}
// NOTE Additional model is used to filter out duplicate entries later on
readonly property SortFilterProxyModel recentRecipientsExtractModel: SortFilterProxyModel {
sourceModel: root.recentRecipientsModel
filters: [
FastExpressionFilter {
expectedRoles: ["activityEntry"]
expression: recentsModel.isValidEntry(model.activityEntry)
}
]
onRowsInserted: recentsModel.refreshRecentsModelFlatList()
onRowsRemoved: recentsModel.refreshRecentsModelFlatList()
onRowsMoved: recentsModel.refreshRecentsModelFlatList()
}
property var recentRecipientsAddressesFlatList: []
Component.onCompleted: refreshRecentsModelFlatList()
sourceModel: ObjectProxyModel {
sourceModel: recentsModel.recentRecipientsExtractModel
delegate: QtObject {
id: recentsDelegate
readonly property bool duplicate: recentsModel.recentRecipientsAddressesFlatList.indexOf(address) < model.index
readonly property string address: recentsModel.getAddressFromEntry(model.activityEntry)
readonly property string name: {
if (recentsAccountsModelEntry.available)
return recentsAccountsModelEntry.item.name
if (recentsSavedAddressModelEntry.available)
return recentsAccountsModelEntry.item.name
return ""
}
readonly property string color: {
if (recentsAccountsModelEntry.available)
return recentsAccountsModelEntry.item.color
if (recentsSavedAddressModelEntry.available)
return recentsSavedAddressModelEntry.item.color
return ""
}
readonly property string colorId: {
if (recentsAccountsModelEntry.available)
return recentsAccountsModelEntry.item.colorId
if (recentsSavedAddressModelEntry.available)
return recentsSavedAddressModelEntry.item.colorId
return ""
}
readonly property string emoji: {
if (recentsAccountsModelEntry.available)
return recentsAccountsModelEntry.item.emoji
return ""
}
// Only used internally for filtering out duplicates in search. Recents can have same entries as in saved addresses or accounts.
readonly property bool cherrypicked: recentsSavedAddressModelEntry.available || recentsAccountsModelEntry.available || duplicate
readonly property ModelEntry recentsSavedAddressModelEntry: ModelEntry {
sourceModel: recentsDelegate.duplicate ? null : root.savedAddressesModel
key: "address"
value: recentsDelegate.address
}
readonly property ModelEntry recentsAccountsModelEntry: ModelEntry {
sourceModel: recentsDelegate.duplicate ? null : root.accountsModel
key: "address"
value: recentsDelegate.address
}
}
expectedRoles: ["activityEntry"]
exposedRoles: ["name", "address", "color", "colorId", "emoji", "cherrypicked", "duplicate"]
}
filters: [
ValueFilter {
// NOTE duplicate property was used instead of filter, becuase removal of row doesn't re-evaluate existing results
roleName: "duplicate"
value: false
}
]
}
SortFilterProxyModel {
id: accountsModelProxyModel
objectName: "RecipientViewAdaptor_accountsModel"
sourceModel: root.accountsModel
filters: [
ValueFilter {
roleName: "walletType"
value: Constants.watchWalletType
inverted: true
},
SearchFilter {
enabled: !!root.selectedSenderAddress
roleName: "address"
searchPhrase: root.selectedSenderAddress
inverted: true
}
]
}
}