status-desktop/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml
Jonathan Rainville 50132c5a0e
Refactor contacts models to have a single model, remove useless properties and improve updating (#16667)
* refactor(contacts): refactor 5 contact models into one and filter in QML

Fixes #16549

Refactors the 5 types of contact models (all, mutuals, banned, received and sent) into only the `allContacts` and use an Adaptor on the QML side to filter into the needed models.
This cleans the Nim side a lot and makes applying updates to the contacts' model way simpler.

* chore(contacts): remove useless and duplicated contact properties

OptionalName and isSyncing were never used.
DefaultDisplayName was not really used and is actually a duplication of preferredDisplayName, so I replaced the limited usages of DefaultDisplayName by preferredDisplayName

* refactor(contacts): improve updates by not removing and re-adding

We used to update contact items by removing them from the models and re-adding them. This is highly inefficient.
Instead, the proper way is to update only the values that changed.

* user_model: onItemChanged signal removed

* user_model: sorting by online status no longer needed on nim side

* Chat/RootStore: contactsModel property removed

* ContactsStore encapsulation improved

* ContactsStore: contacts model adaptor moved outside store

---------

Co-authored-by: Michał Cieślak <michalcieslak@status.im>
2024-11-28 09:15:34 -05:00

153 lines
5.3 KiB
QML

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import StatusQ 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
import utils 1.0
import SortFilterProxyModel 0.2
Item {
id: root
implicitHeight: (title.height + contactsList.height)
property var contactsModel
property int panelUsage: Constants.contactsPanelUsage.unknownPosition
property string title: ""
property string searchString: ""
readonly property int count: contactsList.count
signal openContactContextMenu(string publicKey)
signal sendMessageActionTriggered(string publicKey)
signal showVerificationRequest(string publicKey)
signal contactRequestAccepted(string publicKey)
signal contactRequestRejected(string publicKey)
signal rejectionRemoved(string publicKey)
StyledText {
id: title
height: visible ? contentHeight : 0
anchors.left: parent.left
anchors.leftMargin: Theme.padding
visible: contactsList.count > 0 && root.title !== ""
text: root.title
font.weight: Font.Medium
font.pixelSize: 15
color: Theme.palette.secondaryText
}
StatusListView {
id: contactsList
objectName: "ContactListPanel_ListView"
anchors.top: title.bottom
anchors.left: parent.left
anchors.right: parent.right
onCountChanged: {
height = (count*64);
}
interactive: false
model: SortFilterProxyModel {
id: filteredModel
sourceModel: root.contactsModel
function panelUsagePredicate(isVerified) {
if (panelUsage === Constants.contactsPanelUsage.verifiedMutualContacts)
return isVerified
if (panelUsage === Constants.contactsPanelUsage.mutualContacts)
return !isVerified
return true
}
function searchPredicate(name, pubkey, compressedPubKey) {
const lowerCaseSearchString = root.searchString.toLowerCase()
return name.toLowerCase().includes(lowerCaseSearchString) ||
pubkey.toLowerCase().includes(lowerCaseSearchString) ||
compressedPubKey.toLowerCase().includes(lowerCaseSearchString)
}
filters: [
FastExpressionFilter {
expression: filteredModel.panelUsagePredicate(model.isVerified)
expectedRoles: ["isVerified"]
},
FastExpressionFilter {
enabled: root.searchString !== ""
expression: {
root.searchString // ensure expression is reevaluated when searchString changes
return filteredModel.searchPredicate(model.displayName, model.pubKey, model.compressedPubKey)
}
expectedRoles: ["displayName", "pubKey", "compressedPubKey"]
}
]
sorters: StringSorter {
roleName: "preferredDisplayName"
caseSensitivity: Qt.CaseInsensitive
}
}
delegate: ContactPanel {
id: panelDelegate
width: ListView.view.width
name: model.preferredDisplayName
iconSource: model.thumbnailImage
subTitle: model.ensVerified ? "" : Utils.getElidedCompressedPk(model.pubKey)
pubKeyColor: Utils.colorForPubkey(model.pubKey)
colorHash: Utils.getColorHashAsJson(model.pubKey, model.ensVerified)
showSendMessageButton: model.isContact && !model.isBlocked
showRejectContactRequestButton: {
if (root.panelUsage === Constants.contactsPanelUsage.receivedContactRequest
&& !model.verificationRequestStatus)
return true
return false
}
showAcceptContactRequestButton: {
if (root.panelUsage === Constants.contactsPanelUsage.receivedContactRequest
&& !model.verificationRequestStatus)
return true
return false
}
showRemoveRejectionButton: {
if (root.panelUsage === Constants.contactsPanelUsage.rejectedReceivedContactRequest)
return true
return false
}
contactText: {
if (root.panelUsage === Constants.contactsPanelUsage.sentContactRequest)
return qsTr("Contact Request Sent")
if (root.panelUsage === Constants.contactsPanelUsage.rejectedSentContactRequest)
return qsTr("Contact Request Rejected")
return ""
}
onContextMenuRequested: root.openContactContextMenu(model.pubKey)
onSendMessageRequested: root.sendMessageActionTriggered(model.pubKey)
onAcceptContactRequested: root.contactRequestAccepted(model.pubKey)
onRejectRequestRequested: root.contactRequestRejected(model.pubKey)
onRemoveRejectionRequested: root.rejectionRemoved(model.pubKey)
onShowVerificationRequestRequested: root.showVerificationRequest(model.pubKey)
}
}
}