mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-09 13:56:10 +00:00
fix: Optimize ContactsView & MembersTabPanel settings pages
- removed nested ListViews inside StackLayouts, in order to reduce the memory footprint and improve performance, and also to be able to better manage the scrolling - no more unrolled multiple listviews, which again hurt the performance; now the views instantiate the delegates dynamically on the fly - the tab bar and the search fields now stick to the top of the page, with the users list view scrolling independently - both views now uniformly use the common `ContactListItemDelegate` - the received/sent CRs are now combined into one `pendingContacts` model - factored out common search/filter criteria into a new, separate SFPM `UserFilterContainer` component - fix an issue where StatusContactVerificationIcons wasn't properly displaying the "blocked" state/icon - fix documentation comments, removed relative imports, and updated some Fixes #16612 Fixes #16958
This commit is contained in:
parent
9131487638
commit
3d3a996fa2
68
storybook/pages/ContactsViewPage.qml
Normal file
68
storybook/pages/ContactsViewPage.qml
Normal file
@ -0,0 +1,68 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import StatusQ 0.1
|
||||
|
||||
import Models 1.0
|
||||
import Storybook 1.0
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import shared.stores 1.0 as SharedStores
|
||||
import AppLayouts.Profile.views 1.0
|
||||
import AppLayouts.Profile.stores 1.0
|
||||
import mainui.adaptors 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
ContactsView {
|
||||
sectionTitle: "Contacts"
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 64
|
||||
anchors.topMargin: 16
|
||||
contentWidth: 560
|
||||
|
||||
contactsStore: ContactsStore {
|
||||
function joinPrivateChat(pubKey) {}
|
||||
function acceptContactRequest(pubKey, contactRequestId) {}
|
||||
function dismissContactRequest(pubKey, contactRequestId) {}
|
||||
}
|
||||
utilsStore: SharedStores.UtilsStore {
|
||||
function getEmojiHash(publicKey) {
|
||||
if (publicKey === "")
|
||||
return ""
|
||||
|
||||
return JSON.stringify(["👨🏻🍼", "🏃🏿♂️", "🌇", "🤶🏿", "🏮","🤷🏻♂️", "🤦🏻", "📣", "🤎", "👷🏽", "😺", "🥞", "🔃", "🧝🏽♂️"])
|
||||
}
|
||||
}
|
||||
|
||||
mutualContactsModel: adaptor.mutualContacts
|
||||
blockedContactsModel: adaptor.blockedContacts
|
||||
pendingContactsModel: adaptor.pendingContacts
|
||||
pendingReceivedContactsCount: adaptor.pendingReceivedRequestContacts.count
|
||||
}
|
||||
|
||||
ContactsModelAdaptor {
|
||||
id: adaptor
|
||||
allContacts: SortFilterProxyModel {
|
||||
sourceModel: UsersModel {}
|
||||
proxyRoles: [
|
||||
FastExpressionRole {
|
||||
function displayNameProxy(localNickname, ensName, displayName, aliasName) {
|
||||
return ProfileUtils.displayName(localNickname, ensName, displayName, aliasName)
|
||||
}
|
||||
|
||||
name: "preferredDisplayName"
|
||||
expectedRoles: ["localNickname", "displayName", "ensName", "alias"]
|
||||
expression: displayNameProxy(model.localNickname, model.ensName, model.displayName, model.alias)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Views
|
||||
// status: good
|
@ -8,6 +8,7 @@ import AppLayouts.Communities.panels 1.0
|
||||
import AppLayouts.Chat.stores 1.0 as ChatStores
|
||||
import AppLayouts.Profile.stores 1.0 as ProfileStores
|
||||
|
||||
import shared.stores 1.0
|
||||
import utils 1.0
|
||||
|
||||
import Models 1.0
|
||||
@ -15,8 +16,6 @@ import SortFilterProxyModel 0.2
|
||||
import Storybook 1.0
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core.Utils 0.1 as SQUtils
|
||||
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
@ -24,46 +23,27 @@ SplitView {
|
||||
orientation: Qt.Vertical
|
||||
Logs { id: logs }
|
||||
|
||||
// Utils.globalUtilsInst mock
|
||||
QtObject {
|
||||
function getEmojiHashAsJson(publicKey) {
|
||||
return JSON.stringify(["👨🏻🍼", "🏃🏿♂️", "🌇", "🤶🏿", "🏮","🤷🏻♂️", "🤦🏻", "📣", "🤎", "👷🏽", "😺", "🥞", "🔃", "🧝🏽♂️"])
|
||||
}
|
||||
|
||||
function getColorId(publicKey) {
|
||||
return SQUtils.ModelUtils.getByKey(usersModel, "pubKey", publicKey, "colorId")
|
||||
}
|
||||
|
||||
function getCompressedPk(publicKey) { return "zx3sh" + publicKey }
|
||||
|
||||
function getColorHashAsJson(publicKey) {
|
||||
return JSON.stringify([{colorId: 0, segmentLength: 1},
|
||||
{colorId: 19, segmentLength: 2}])
|
||||
}
|
||||
|
||||
function isCompressedPubKey(publicKey) { return true }
|
||||
|
||||
Component.onCompleted: {
|
||||
Utils.globalUtilsInst = this
|
||||
}
|
||||
Component.onDestruction: {
|
||||
Utils.globalUtilsInst = {}
|
||||
}
|
||||
}
|
||||
|
||||
MembersTabPanel {
|
||||
id: membersTabPanelPage
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
placeholderText: "Search users"
|
||||
model: usersModelWithMembershipState
|
||||
panelType: viewStateSelector.currentValue
|
||||
searchString: ctrlSearch.text
|
||||
|
||||
rootStore: ChatStores.RootStore {
|
||||
contactsStore: ProfileStores.ContactsStore {
|
||||
readonly property string myPublicKey: "0x000"
|
||||
}
|
||||
}
|
||||
utilsStore: UtilsStore {
|
||||
function getEmojiHash(publicKey) {
|
||||
if (publicKey === "")
|
||||
return ""
|
||||
|
||||
return JSON.stringify(["👨🏻🍼", "🏃🏿♂️", "🌇", "🤶🏿", "🏮","🤷🏻♂️", "🤦🏻", "📣", "🤎", "👷🏽", "😺", "🥞", "🔃", "🧝🏽♂️"])
|
||||
}
|
||||
}
|
||||
|
||||
onKickUserClicked: {
|
||||
logs.logEvent("MembersTabPanel::onKickUserClicked", ["id", "name"], arguments)
|
||||
@ -132,7 +112,7 @@ SplitView {
|
||||
}
|
||||
|
||||
LogsAndControlsPanel {
|
||||
SplitView.minimumHeight: 100
|
||||
SplitView.minimumHeight: 200
|
||||
SplitView.preferredHeight: 320
|
||||
|
||||
logsView.logText: logs.logText
|
||||
@ -144,6 +124,7 @@ SplitView {
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
Layout.preferredWidth: 300
|
||||
id: viewStateSelector
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
@ -155,6 +136,13 @@ SplitView {
|
||||
ListElement { text: "Declined Members"; value: MembersTabPanel.TabType.DeclinedRequests }
|
||||
}
|
||||
}
|
||||
|
||||
Label { text: "Search" }
|
||||
TextField {
|
||||
id: ctrlSearch
|
||||
Layout.preferredWidth: 300
|
||||
placeholderText: "Search by member name or chat key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,4 +151,6 @@ SplitView {
|
||||
}
|
||||
}
|
||||
|
||||
// category: Panels
|
||||
// status: good
|
||||
// https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba⎜Desktop?type=design&node-id=35909-605774&mode=design&t=KfrAekLfW5mTy68x-0
|
||||
|
@ -29,7 +29,7 @@ Item {
|
||||
StatusTabButton {
|
||||
width: implicitWidth
|
||||
enabled: false
|
||||
text: qsTr("Blocked & disabled")
|
||||
text: "Blocked & disabled"
|
||||
}
|
||||
StatusTabButton {
|
||||
width: implicitWidth
|
||||
|
@ -9,9 +9,10 @@ ListModel {
|
||||
compressedPubKey: "zQ3shQBu4PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsBDF4",
|
||||
onlineStatus: Constants.onlineStatus.online,
|
||||
isContact: true,
|
||||
isBlocked: false,
|
||||
isVerified: false,
|
||||
isAdmin: false,
|
||||
isUntrustworthy: true,
|
||||
isUntrustworthy: false,
|
||||
displayName: "Mike has a very long name that should elide " +
|
||||
"eventually and result in a tooltip displayed instead",
|
||||
alias: "",
|
||||
@ -26,13 +27,15 @@ ListModel {
|
||||
],
|
||||
isAwaitingAddress: false,
|
||||
memberRole: Constants.memberRole.none,
|
||||
trustStatus: Constants.trustStatus.untrustworthy
|
||||
trustStatus: Constants.trustStatus.unknown
|
||||
},
|
||||
{
|
||||
pubKey: "0x04df12f12f12f12f1234",
|
||||
compressedPubKey: "zQ3shQBAAPRDX17vewYyvSczbTj344viTXxcMNvQLeyQsBDF4",
|
||||
onlineStatus: Constants.onlineStatus.inactive,
|
||||
isContact: false,
|
||||
contactRequest: Constants.ContactRequestState.Sent,
|
||||
isBlocked: false,
|
||||
isVerified: false,
|
||||
isAdmin: false,
|
||||
isUntrustworthy: false,
|
||||
@ -49,13 +52,14 @@ ListModel {
|
||||
],
|
||||
isAwaitingAddress: false,
|
||||
memberRole: Constants.memberRole.owner,
|
||||
trustStatus: Constants.trustStatus.trusted
|
||||
trustStatus: Constants.trustStatus.unknown
|
||||
},
|
||||
{
|
||||
pubKey: "0x04d1b7cc0ef3f470f1238",
|
||||
compressedPubKey: "zQ3shQ7u3PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsCDF4",
|
||||
onlineStatus: Constants.onlineStatus.inactive,
|
||||
isContact: false,
|
||||
isBlocked: true,
|
||||
isVerified: false,
|
||||
isAdmin: false,
|
||||
isUntrustworthy: true,
|
||||
@ -66,6 +70,10 @@ ListModel {
|
||||
icon: ModelsData.icons.dragonereum,
|
||||
colorId: 4,
|
||||
isEnsVerified: false,
|
||||
colorHash: [
|
||||
{ colorId: 7, segmentLength: 3 },
|
||||
{ colorId: 12, segmentLength: 1 }
|
||||
],
|
||||
isAwaitingAddress: false,
|
||||
memberRole: Constants.memberRole.none,
|
||||
trustStatus: Constants.trustStatus.untrustworthy
|
||||
@ -75,16 +83,17 @@ ListModel {
|
||||
compressedPubKey: "zQ3shQAL4PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsBDF4",
|
||||
onlineStatus: Constants.onlineStatus.online,
|
||||
isContact: true,
|
||||
isVerified: true,
|
||||
isBlocked: false,
|
||||
isVerified: false,
|
||||
isAdmin: false,
|
||||
isUntrustworthy: true,
|
||||
displayName: "Maria",
|
||||
alias: "meth",
|
||||
localNickname: "86.eth",
|
||||
ensName: "8⃣_6⃣.eth",
|
||||
localNickname: "",
|
||||
ensName: "",
|
||||
icon: "",
|
||||
colorId: 5,
|
||||
isEnsVerified: true,
|
||||
isEnsVerified: false,
|
||||
isAwaitingAddress: false,
|
||||
memberRole: Constants.memberRole.none,
|
||||
trustStatus: Constants.trustStatus.untrustworthy
|
||||
@ -93,8 +102,10 @@ ListModel {
|
||||
pubKey: "0x04d1bed192343f470f1255",
|
||||
compressedPubKey: "zQ3shQBu4PGDX17vewYyvSczbTj344viTXxcMNvQLeyQsBD1A",
|
||||
onlineStatus: Constants.onlineStatus.online,
|
||||
isContact: true,
|
||||
isVerified: true,
|
||||
isContact: false,
|
||||
contactRequest: Constants.ContactRequestState.Received,
|
||||
isBlocked: false,
|
||||
isVerified: false,
|
||||
isAdmin: true,
|
||||
isUntrustworthy: true,
|
||||
displayName: "",
|
||||
@ -113,7 +124,8 @@ ListModel {
|
||||
compressedPubKey: "zQ3shQBk4PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsB994",
|
||||
onlineStatus: Constants.onlineStatus.inactive,
|
||||
isContact: true,
|
||||
isVerified: false,
|
||||
isBlocked: false,
|
||||
isVerified: true,
|
||||
isAdmin: false,
|
||||
isUntrustworthy: false,
|
||||
displayName: "",
|
||||
|
@ -1,10 +1,5 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
Flow {
|
||||
id: root
|
||||
|
@ -52,7 +52,7 @@ settingsContentBaseScrollView_ContactListPanel = {"container": mainWindow_Contac
|
||||
settingsContentBaseScrollView_Item = {"container": mainWindow_ContactsView, "type": "Item", "unnamed": 1, "visible": True}
|
||||
settingsContentBaseScrollView_sentRequests_ContactsListPanel = {"container": mainWindow_ContactsView, "objectName": "sentRequests_ContactsListPanel", "type": "ContactsListPanel", "visible": True}
|
||||
contactsTabBar_Contacts_StatusTabButton = {"container": mainWindow_ContactsView, "id": "contactsBtn", "type": "StatusTabButton", "unnamed": 1, "visible": True}
|
||||
settingsContentBaseScrollView_receivedRequests_ContactsListPanel = {"container": mainWindow_ContactsView, "objectName": "receivedRequests_ContactsListPanel", "type": "ContactsListPanel", "visible": True}
|
||||
settingsContentBaseScrollView_receivedRequests_ContactsListPanel = {"container": mainWindow_ContactsView, "objectName": "ContactsListPanel", "type": "ContactsListPanel", "visible": True}
|
||||
settingsContentBaseScrollView_mutualContacts_ContactsListPanel = {"container": mainWindow_ContactsView, "id": "mutualContacts", "type": "ContactsListPanel", "unnamed": 1, "visible": True}
|
||||
settingsContentBaseScrollView_Invite_friends_StatusButton = {"container": mainWindow_ContactsView, "type": "StatusButton", "unnamed": 1, "visible": True}
|
||||
settingsContentBaseScrollView_NoFriendsRectangle = {"container": mainWindow_ContactsView, "type": "NoFriendsRectangle", "unnamed": 1, "visible": True}
|
||||
|
@ -71,7 +71,7 @@ Row {
|
||||
}
|
||||
|
||||
spacing: 4
|
||||
visible: root.isContact || (root.trustIndicator !== StatusContactVerificationIcons.TrustedType.None)
|
||||
visible: root.isContact || root.isBlocked || (root.trustIndicator !== StatusContactVerificationIcons.TrustedType.None)
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
@ -104,7 +104,8 @@ Row {
|
||||
|
||||
// (un)trusted
|
||||
StatusRoundIcon {
|
||||
visible: !root.isBlocked && root.trustIndicator !== StatusContactVerificationIcons.TrustedType.None
|
||||
visible: !root.isBlocked && (root.trustIndicator === StatusContactVerificationIcons.TrustedType.Untrustworthy ||
|
||||
(root.isContact && trustIndicator === StatusContactVerificationIcons.TrustedType.Verified))
|
||||
asset: root.trustContactIcon
|
||||
}
|
||||
|
||||
|
@ -58,22 +58,27 @@ ItemDelegate {
|
||||
*/
|
||||
property string pubKey: ""
|
||||
/*!
|
||||
\qmlproperty string StatusMemberListItem::isContact
|
||||
\qmlproperty bool StatusMemberListItem::isContact
|
||||
This property holds if the member represented is contact.
|
||||
*/
|
||||
property bool isContact: false
|
||||
/*!
|
||||
\qmlproperty string StatusMemberListItem::isVerified
|
||||
\qmlproperty bool StatusMemberListItem::isVerified
|
||||
This property holds if the member represented is verified contact.
|
||||
*/
|
||||
property bool isVerified: false
|
||||
/*!
|
||||
\qmlproperty string StatusMemberListItem::isUntrustworthy
|
||||
\qmlproperty bool StatusMemberListItem::isUntrustworthy
|
||||
This property holds if the member represented is untrustworthy.
|
||||
*/
|
||||
property bool isUntrustworthy: false
|
||||
/*!
|
||||
\qmlproperty string StatusMemberListItem::status
|
||||
\qmlproperty bool StatusMemberListItem::isBlocked
|
||||
This property holds if the member represented is blocked.
|
||||
*/
|
||||
property bool isBlocked: false
|
||||
/*!
|
||||
\qmlproperty int StatusMemberListItem::status
|
||||
This property holds the connectivity status of the member represented.
|
||||
|
||||
int unknown: -1
|
||||
@ -84,7 +89,7 @@ ItemDelegate {
|
||||
// FIXME: move Constants.onlineStatus from status-desktop
|
||||
property int status: 0
|
||||
/*!
|
||||
\qmlproperty string StatusMemberListItem::isAdmin
|
||||
\qmlproperty bool StatusMemberListItem::isAdmin
|
||||
This property holds the admin status of the member represented.
|
||||
*/
|
||||
property bool isAdmin: false
|
||||
@ -126,7 +131,7 @@ ItemDelegate {
|
||||
property alias badge: identicon.badge
|
||||
|
||||
/*!
|
||||
\qmlsignal
|
||||
\qmlsignal clicked
|
||||
This signal is emitted when the StatusMemberListItem is clicked.
|
||||
*/
|
||||
signal clicked(var mouse)
|
||||
@ -158,9 +163,9 @@ ItemDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
horizontalPadding: 8
|
||||
horizontalPadding: Theme.halfPadding
|
||||
verticalPadding: 12
|
||||
spacing: 8
|
||||
spacing: Theme.halfPadding
|
||||
|
||||
icon.width: 32
|
||||
icon.height: 32
|
||||
@ -170,7 +175,7 @@ ItemDelegate {
|
||||
|
||||
background: Rectangle {
|
||||
color: root.color
|
||||
radius: 8
|
||||
radius: Theme.radius
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
@ -200,9 +205,8 @@ ItemDelegate {
|
||||
// badge
|
||||
badge.visible: true
|
||||
badge.color: root.status === 1 ? Theme.palette.successColor1 : Theme.palette.baseColor1 // FIXME, see root.status
|
||||
badge.anchors.top: undefined
|
||||
badge.border.width: 2
|
||||
badge.border.color: Theme.palette.statusListItem.backgroundColor
|
||||
badge.border.color: root.hovered ? Theme.palette.statusBadge.hoverBorderColor : Theme.palette.statusBadge.borderColor
|
||||
badge.implicitHeight: 12 // 8 px + 2 px * 2 borders
|
||||
badge.implicitWidth: 12 // 8 px + 2 px * 2 borders
|
||||
}
|
||||
@ -243,7 +247,7 @@ ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
text: d.composeSubtitle()
|
||||
font.pixelSize: 10
|
||||
font.pixelSize: Theme.asideTextFontSize
|
||||
color: Theme.palette.baseColor1
|
||||
visible: !!text
|
||||
|
||||
@ -280,6 +284,7 @@ ItemDelegate {
|
||||
id: statusContactVerificationIcons
|
||||
StatusContactVerificationIcons {
|
||||
isContact: root.isContact
|
||||
isBlocked: root.isBlocked
|
||||
trustIndicator: {
|
||||
if (root.isVerified)
|
||||
return StatusContactVerificationIcons.TrustedType.Verified
|
||||
|
@ -1,4 +1,4 @@
|
||||
import QtQuick 2.14
|
||||
import QtQuick 2.15
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
@ -12,4 +12,5 @@ StatusFlatRoundButton {
|
||||
implicitHeight: 24
|
||||
icon.color: Theme.palette.directColor9
|
||||
backgroundHoverColor: "transparent"
|
||||
tooltip.text: qsTr("Clear")
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import QtQuick 2.14
|
||||
import QtQuick 2.15
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: statusFlatRoundButton
|
||||
|
||||
property StatusAssetSettings icon: StatusAssetSettings {
|
||||
width: 23
|
||||
height: 23
|
||||
width: 24
|
||||
height: 24
|
||||
rotation: 0
|
||||
|
||||
color: {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Controls 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Components 0.1
|
||||
@ -17,8 +17,10 @@ Control {
|
||||
|
||||
signal toggled
|
||||
|
||||
padding: 4
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 16
|
||||
spacing: Theme.padding
|
||||
|
||||
StatusRoundIcon {
|
||||
asset.name: root.icon
|
||||
@ -26,22 +28,21 @@ Control {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
StatusBaseText {
|
||||
text: root.title
|
||||
color: Theme.palette.directColor1
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
Layout.fillHeight: true
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: root.title
|
||||
visible: !!text
|
||||
color: Theme.palette.directColor1
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
text: root.subTitle
|
||||
visible: !!text
|
||||
color: Theme.palette.baseColor1
|
||||
font.pixelSize: 15
|
||||
lineHeight: 1.2
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
@ -51,6 +52,7 @@ Control {
|
||||
StatusSwitch {
|
||||
id: switchItem
|
||||
objectName: "switchItem"
|
||||
padding: 0
|
||||
|
||||
onToggled: root.toggled()
|
||||
}
|
||||
|
@ -1,13 +1,17 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import AppLayouts.Communities.controls 1.0
|
||||
|
||||
Page {
|
||||
id: root
|
||||
|
||||
leftPadding: 64
|
||||
topPadding: 16
|
||||
leftPadding: Theme.xlPadding*2
|
||||
topPadding: Theme.padding
|
||||
|
||||
readonly property int preferredContentWidth: 560
|
||||
|
||||
property alias buttons: pageHeader.buttons
|
||||
property alias subtitle: pageHeader.subtitle
|
||||
@ -18,8 +22,8 @@ Page {
|
||||
id: pageHeader
|
||||
|
||||
height: 44
|
||||
leftPadding: 64
|
||||
rightPadding: width - 560 - leftPadding
|
||||
leftPadding: root.leftPadding
|
||||
rightPadding: width - root.preferredContentWidth - leftPadding
|
||||
|
||||
title: root.title
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import shared.controls 1.0
|
||||
import shared.stores 1.0 as SharedStores
|
||||
import utils 1.0
|
||||
|
||||
@ -72,14 +74,15 @@ SettingsPage {
|
||||
membersTabBar.currentIndex = tabButton.TabBar.index
|
||||
}
|
||||
|
||||
spacing: 19
|
||||
spacing: Theme.padding
|
||||
|
||||
StatusTabBar {
|
||||
id: membersTabBar
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 5
|
||||
Layout.preferredWidth: root.preferredContentWidth
|
||||
|
||||
StatusTabButton {
|
||||
readonly property int subSection: MembersTabPanel.TabType.AllMembers
|
||||
|
||||
id: allMembersBtn
|
||||
objectName: "allMembersButton"
|
||||
width: implicitWidth
|
||||
@ -87,6 +90,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
StatusTabButton {
|
||||
readonly property int subSection: MembersTabPanel.TabType.PendingRequests
|
||||
|
||||
id: pendingRequestsBtn
|
||||
objectName: "pendingRequestsButton"
|
||||
width: implicitWidth
|
||||
@ -95,6 +100,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
StatusTabButton {
|
||||
readonly property int subSection: MembersTabPanel.TabType.DeclinedRequests
|
||||
|
||||
id: declinedRequestsBtn
|
||||
objectName: "declinedRequestsButton"
|
||||
width: implicitWidth
|
||||
@ -103,6 +110,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
StatusTabButton {
|
||||
readonly property int subSection: MembersTabPanel.TabType.BannedMembers
|
||||
|
||||
id: bannedBtn
|
||||
objectName: "bannedButton"
|
||||
width: implicitWidth
|
||||
@ -111,21 +120,36 @@ SettingsPage {
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
currentIndex: membersTabBar.currentIndex
|
||||
SearchBox {
|
||||
id: memberSearch
|
||||
Layout.preferredWidth: root.preferredContentWidth
|
||||
placeholderText: qsTr("Search by name or chat key")
|
||||
enabled: membersTabBar.currentItem.enabled
|
||||
}
|
||||
|
||||
MembersTabPanel {
|
||||
model: root.membersModel
|
||||
Layout.preferredWidth: root.preferredContentWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
panelType: membersTabBar.currentItem.subSection
|
||||
model: {
|
||||
switch (panelType) {
|
||||
case MembersTabPanel.TabType.PendingRequests:
|
||||
return root.pendingMembersModel
|
||||
case MembersTabPanel.TabType.DeclinedRequests:
|
||||
return root.declinedMembersModel
|
||||
case MembersTabPanel.TabType.BannedMembers:
|
||||
return root.bannedMembersModel
|
||||
case MembersTabPanel.TabType.AllMembers:
|
||||
default:
|
||||
return root.membersModel
|
||||
}
|
||||
}
|
||||
|
||||
searchString: memberSearch.text
|
||||
rootStore: root.rootStore
|
||||
utilsStore: root.utilsStore
|
||||
memberRole: root.memberRole
|
||||
panelType: MembersTabPanel.TabType.AllMembers
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
onKickUserClicked: {
|
||||
kickBanPopup.mode = KickBanPopup.Mode.Kick
|
||||
@ -133,59 +157,18 @@ SettingsPage {
|
||||
kickBanPopup.userId = id
|
||||
kickBanPopup.open()
|
||||
}
|
||||
|
||||
onBanUserClicked: {
|
||||
kickBanPopup.mode = KickBanPopup.Mode.Ban
|
||||
kickBanPopup.username = name
|
||||
kickBanPopup.userId = id
|
||||
kickBanPopup.open()
|
||||
}
|
||||
|
||||
onViewMemberMessagesClicked: root.viewMemberMessagesClicked(pubKey, displayName)
|
||||
}
|
||||
|
||||
MembersTabPanel {
|
||||
model: root.pendingMembersModel
|
||||
rootStore: root.rootStore
|
||||
utilsStore: root.utilsStore
|
||||
memberRole: root.memberRole
|
||||
panelType: MembersTabPanel.TabType.PendingRequests
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
onUnbanUserClicked: root.unbanUserClicked(id)
|
||||
onAcceptRequestToJoin: root.acceptRequestToJoin(id)
|
||||
onDeclineRequestToJoin: root.declineRequestToJoin(id)
|
||||
}
|
||||
|
||||
MembersTabPanel {
|
||||
model: root.declinedMembersModel
|
||||
rootStore: root.rootStore
|
||||
utilsStore: root.utilsStore
|
||||
memberRole: root.memberRole
|
||||
panelType: MembersTabPanel.TabType.DeclinedRequests
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
onAcceptRequestToJoin: root.acceptRequestToJoin(id)
|
||||
}
|
||||
|
||||
MembersTabPanel {
|
||||
model: root.bannedMembersModel
|
||||
rootStore: root.rootStore
|
||||
utilsStore: root.utilsStore
|
||||
memberRole: root.memberRole
|
||||
panelType: MembersTabPanel.TabType.BannedMembers
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
onUnbanUserClicked: root.unbanUserClicked(id)
|
||||
onViewMemberMessagesClicked: root.viewMemberMessagesClicked(pubKey, displayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KickBanPopup {
|
||||
id: kickBanPopup
|
||||
|
@ -10,25 +10,27 @@ import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
|
||||
import shared.controls 1.0
|
||||
import shared 1.0
|
||||
import shared.controls.chat 1.0
|
||||
import shared.controls.delegates 1.0
|
||||
import shared.stores 1.0 as SharedStores
|
||||
import shared.views.chat 1.0
|
||||
import utils 1.0
|
||||
|
||||
import AppLayouts.Chat.stores 1.0
|
||||
import AppLayouts.Communities.layouts 1.0
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string placeholderText: qsTr("Search by member name or chat key")
|
||||
property var model
|
||||
required property var model
|
||||
|
||||
property string searchString
|
||||
property RootStore rootStore
|
||||
property SharedStores.UtilsStore utilsStore
|
||||
|
||||
property int panelType: MembersTabPanel.TabType.AllMembers
|
||||
property int memberRole: Constants.memberRole.none
|
||||
|
||||
readonly property bool isOwner: memberRole === Constants.memberRole.owner
|
||||
@ -49,38 +51,13 @@ Item {
|
||||
DeclinedRequests
|
||||
}
|
||||
|
||||
property int panelType: MembersTabPanel.TabType.AllMembers
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 30
|
||||
|
||||
SearchBox {
|
||||
id: memberSearch
|
||||
Layout.preferredWidth: 400
|
||||
Layout.leftMargin: 12
|
||||
placeholderText: root.placeholderText
|
||||
enabled: !!root.model && !root.model.ModelCount.empty
|
||||
}
|
||||
|
||||
StatusListView {
|
||||
id: membersList
|
||||
objectName: "CommunityMembersTabPanel_MembersListViews"
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
anchors.fill: parent
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: filteredModel
|
||||
sourceModel: root.model
|
||||
|
||||
function searchPredicate(ensName, displayName, aliasName) {
|
||||
const lowerCaseSearchString = memberSearch.text.toLowerCase()
|
||||
const secondaryName = ProfileUtils.displayName("", ensName, displayName, aliasName)
|
||||
|
||||
return secondaryName.toLowerCase().includes(lowerCaseSearchString)
|
||||
}
|
||||
|
||||
sorters: [
|
||||
StringSorter {
|
||||
roleName: "preferredDisplayName"
|
||||
@ -88,30 +65,16 @@ Item {
|
||||
}
|
||||
]
|
||||
|
||||
filters: AnyOf {
|
||||
enabled: memberSearch.text !== ""
|
||||
// substring search for either nickname or the other primary/secondary display name
|
||||
SearchFilter {
|
||||
roleName: "localNickname"
|
||||
searchPhrase: memberSearch.text
|
||||
}
|
||||
FastExpressionFilter {
|
||||
expression: {
|
||||
memberSearch.text
|
||||
return filteredModel.searchPredicate(model.ensName, model.displayName, model.alias)
|
||||
}
|
||||
expectedRoles: ["ensName", "displayName", "alias"]
|
||||
}
|
||||
// exact search for the full key
|
||||
ValueFilter {
|
||||
roleName: "compressedPubKey"
|
||||
value: memberSearch.text
|
||||
}
|
||||
filters: [
|
||||
UserSearchFilterContainer {
|
||||
searchString: root.searchString
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
|
||||
delegate: StatusMemberListItem {
|
||||
delegate: ContactListItemDelegate {
|
||||
id: memberItem
|
||||
|
||||
// Buttons visibility conditions:
|
||||
@ -163,7 +126,6 @@ Item {
|
||||
// The admin that initited the pending state can change the state. Actions are not visible for other admins
|
||||
readonly property bool ctaAllowed: !isRejectedPending && !isAcceptedPending && !isBanPending && !isUnbanPending && !isKickPending
|
||||
|
||||
readonly property bool isHovered: memberItem.hovered
|
||||
readonly property bool canBeBanned: {
|
||||
if (model.isCurrentUser)
|
||||
return false
|
||||
@ -179,7 +141,7 @@ Item {
|
||||
default: return true
|
||||
}
|
||||
}
|
||||
readonly property bool showOnHover: isHovered && ctaAllowed
|
||||
readonly property bool showOnHover: hovered && ctaAllowed
|
||||
readonly property bool canDeleteMessages: model.isCurrentUser || model.memberRole !== Constants.memberRole.owner
|
||||
|
||||
/// Button visibility ///
|
||||
@ -206,9 +168,6 @@ Item {
|
||||
|
||||
isAwaitingAddress: model.membershipRequestState === Constants.CommunityMembershipRequestState.AwaitingAddress
|
||||
|
||||
rightPadding: 75
|
||||
leftPadding: 12
|
||||
|
||||
components: [
|
||||
StatusBaseText {
|
||||
id: pendingText
|
||||
@ -249,7 +208,6 @@ Item {
|
||||
},
|
||||
|
||||
StatusButton {
|
||||
id: kickButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
objectName: "MemberListItem_KickButton"
|
||||
text: qsTr("Kick")
|
||||
@ -260,7 +218,6 @@ Item {
|
||||
},
|
||||
|
||||
StatusButton {
|
||||
id: banButton
|
||||
objectName: "MemberListItem_BanButton"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: banButtonVisible
|
||||
@ -283,9 +240,10 @@ Item {
|
||||
StatusButton {
|
||||
id: acceptButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: acceptButtonVisible
|
||||
visible: acceptButtonVisible
|
||||
text: qsTr("Accept")
|
||||
type: StatusBaseButton.Type.Success
|
||||
size: StatusBaseButton.Size.Small
|
||||
icon.name: "checkmark-circle"
|
||||
icon.color: enabled ? Theme.palette.successColor1 : disabledTextColor
|
||||
loading: model.requestToJoinLoading
|
||||
@ -295,9 +253,10 @@ Item {
|
||||
|
||||
StatusButton {
|
||||
id: rejectButton
|
||||
opacity: rejectButtonVisible
|
||||
visible: rejectButtonVisible
|
||||
text: qsTr("Reject")
|
||||
type: StatusBaseButton.Type.Danger
|
||||
size: StatusBaseButton.Size.Small
|
||||
icon.name: "close-circle"
|
||||
icon.color: enabled ? Theme.palette.dangerColor1 : disabledTextColor
|
||||
enabled: !rejectPendingButtonVisible
|
||||
@ -307,19 +266,10 @@ Item {
|
||||
|
||||
readonly property string title: model.preferredDisplayName
|
||||
|
||||
width: membersList.width
|
||||
color: "transparent"
|
||||
width: ListView.view.width
|
||||
|
||||
pubKey: model.isEnsVerified ? "" : Utils.getElidedCompressedPk(model.pubKey)
|
||||
nickName: model.localNickname
|
||||
userName: ProfileUtils.displayName("", model.ensName, model.displayName, model.alias)
|
||||
status: model.onlineStatus
|
||||
icon.color: Utils.colorForColorId(model.colorId)
|
||||
icon.name: model.icon
|
||||
icon.width: 40
|
||||
icon.height: 40
|
||||
ringSettings.ringSpecModel: model.colorHash
|
||||
badge.visible: (root.panelType === MembersTabPanel.TabType.AllMembers)
|
||||
|
||||
onClicked: {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
@ -341,14 +291,12 @@ Item {
|
||||
hasLocalNickname: !!model.localNickname
|
||||
}
|
||||
|
||||
Global.openMenu(memberContextMenuComponent, this, params)
|
||||
memberContextMenuComponent.createObject(root, params).popup(this)
|
||||
} else if (mouse.button === Qt.LeftButton) {
|
||||
Global.openProfilePopup(model.pubKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: memberContextMenuComponent
|
||||
@ -377,6 +325,7 @@ Item {
|
||||
onClosed: destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
@ -384,5 +333,6 @@ Item {
|
||||
// so that the text aligned on all rows (the text might be different on each row)
|
||||
property real pendingTextMaxWidth: 0
|
||||
}
|
||||
|
||||
onPanelTypeChanged: { d.pendingTextMaxWidth = 0 }
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQml.Models 2.15
|
||||
import QtGraphicalEffects 1.15
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
@ -25,44 +25,30 @@ StatusDialog {
|
||||
Kick, Ban
|
||||
}
|
||||
|
||||
width: 400
|
||||
width: 480
|
||||
|
||||
title: root.mode === KickBanPopup.Mode.Kick
|
||||
? qsTr("Kick %1").arg(root.username)
|
||||
: qsTr("Ban %1").arg(root.username)
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
font.pixelSize: Theme.primaryTextFontSize
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
text: root.mode === KickBanPopup.Mode.Kick
|
||||
? qsTr("Are you sure you want to kick <b>%1</b> from %2?")
|
||||
.arg(root.username).arg(root.communityName)
|
||||
: qsTr("Are you sure you want to ban <b>%1</b> from %2? This means that they will be kicked from this community and banned from re-joining.")
|
||||
.arg(root.username).arg(root.communityName)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: root.mode === KickBanPopup.Mode.Ban
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Delete all messages posted by the user")
|
||||
font.pixelSize: Theme.primaryTextFontSize
|
||||
? qsTr("Are you sure you want to kick <b>%1</b> from %2?").arg(root.username).arg(root.communityName)
|
||||
: qsTr("Are you sure you want to ban <b>%1</b> from %2? This means that they will be kicked from this community and banned from re-joining.").arg(root.username).arg(root.communityName)
|
||||
}
|
||||
|
||||
StatusSwitch {
|
||||
Layout.fillWidth: true
|
||||
id: deleteAllMessagesSwitch
|
||||
|
||||
checked: false
|
||||
}
|
||||
visible: root.mode === KickBanPopup.Mode.Ban
|
||||
leftSide: false
|
||||
text: qsTr("Delete all messages posted by the user")
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,8 +60,6 @@ StatusDialog {
|
||||
onClicked: root.close()
|
||||
}
|
||||
StatusButton {
|
||||
id: banButton
|
||||
|
||||
objectName: root.mode === KickBanPopup.Mode.Kick
|
||||
? "CommunityMembers_KickModal_KickButton"
|
||||
: "CommunityMembers_BanModal_BanButton"
|
||||
|
@ -55,8 +55,8 @@ StatusSectionLayout {
|
||||
|
||||
property var mutualContactsModel
|
||||
property var blockedContactsModel
|
||||
property var pendingReceivedRequestContactsModel
|
||||
property var pendingSentRequestContactsModel
|
||||
property var pendingContactsModel
|
||||
property int pendingReceivedContactsCount
|
||||
|
||||
required property bool isCentralizedMetricsEnabled
|
||||
|
||||
@ -116,7 +116,7 @@ StatusSectionLayout {
|
||||
|
||||
syncingBadgeCount: root.store.devicesStore.devicesModel.count -
|
||||
root.store.devicesStore.devicesModel.pairedCount
|
||||
messagingBadgeCount: root.pendingReceivedRequestContactsModel.ModelCount.count
|
||||
messagingBadgeCount: root.pendingReceivedContactsCount
|
||||
}
|
||||
|
||||
headerBackground: AccountHeaderGradient {
|
||||
@ -244,8 +244,8 @@ StatusSectionLayout {
|
||||
|
||||
mutualContactsModel: root.mutualContactsModel
|
||||
blockedContactsModel: root.blockedContactsModel
|
||||
pendingReceivedRequestContactsModel: root.pendingReceivedRequestContactsModel
|
||||
pendingSentRequestContactsModel: root.pendingSentRequestContactsModel
|
||||
pendingContactsModel: root.pendingContactsModel
|
||||
pendingReceivedContactsCount: root.pendingReceivedContactsCount
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,7 +280,7 @@ StatusSectionLayout {
|
||||
contentWidth: d.contentWidth
|
||||
|
||||
sectionTitle: settingsEntriesModel.getNameForSubsection(Constants.settingsSubsection.messaging)
|
||||
requestsCount: root.pendingReceivedRequestContactsModel.ModelCount.count
|
||||
requestsCount: root.pendingReceivedContactsCount
|
||||
messagingStore: root.store.messagingStore
|
||||
}
|
||||
}
|
||||
|
@ -1,49 +1,26 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import utils 1.0
|
||||
import shared.controls.delegates 1.0
|
||||
|
||||
StatusListItem {
|
||||
ContactListItemDelegate {
|
||||
id: root
|
||||
|
||||
width: parent.width
|
||||
height: visible ? implicitHeight : 0
|
||||
title: root.name
|
||||
|
||||
property string name
|
||||
property string iconSource
|
||||
|
||||
property color pubKeyColor
|
||||
property var colorHash
|
||||
|
||||
property bool showSendMessageButton: false
|
||||
property bool showRejectContactRequestButton: false
|
||||
property bool showAcceptContactRequestButton: false
|
||||
property bool showRemoveRejectionButton: false
|
||||
property string contactText: ""
|
||||
|
||||
signal contextMenuRequested
|
||||
signal sendMessageRequested
|
||||
signal showVerificationRequestRequested
|
||||
signal acceptContactRequested
|
||||
signal rejectRequestRequested
|
||||
signal removeRejectionRequested
|
||||
|
||||
asset.width: 40
|
||||
asset.height: 40
|
||||
asset.color: root.pubKeyColor
|
||||
asset.letterSize: asset._twoLettersSize
|
||||
asset.charactersLen: 2
|
||||
asset.name: root.iconSource
|
||||
asset.isLetterIdenticon: root.iconSource.toString() === ""
|
||||
ringSettings {
|
||||
ringSpecModel: root.colorHash
|
||||
ringPxSize: Math.max(asset.width / 24.0)
|
||||
}
|
||||
icon.width: 40
|
||||
icon.height: 40
|
||||
|
||||
components: [
|
||||
StatusFlatRoundButton {
|
||||
@ -53,6 +30,7 @@ StatusListItem {
|
||||
height: visible ? 32 : 0
|
||||
icon.name: "chat"
|
||||
icon.color: Theme.palette.directColor1
|
||||
tooltip.text: qsTr("Send message")
|
||||
onClicked: root.sendMessageRequested()
|
||||
},
|
||||
StatusFlatRoundButton {
|
||||
@ -62,6 +40,7 @@ StatusListItem {
|
||||
height: visible ? 32 : 0
|
||||
icon.name: "close-circle"
|
||||
icon.color: Theme.palette.dangerColor1
|
||||
tooltip.text: qsTr("Reject")
|
||||
onClicked: root.rejectRequestRequested()
|
||||
},
|
||||
StatusFlatRoundButton {
|
||||
@ -71,26 +50,16 @@ StatusListItem {
|
||||
height: visible ? 32 : 0
|
||||
icon.name: "checkmark-circle"
|
||||
icon.color: Theme.palette.successColor1
|
||||
tooltip.text: qsTr("Accept")
|
||||
onClicked: root.acceptContactRequested()
|
||||
},
|
||||
StatusFlatRoundButton {
|
||||
objectName: "removeRejectBtn"
|
||||
visible: showRemoveRejectionButton
|
||||
width: visible ? 32 : 0
|
||||
height: visible ? 32 : 0
|
||||
icon.name: "cancel"
|
||||
icon.color: Theme.palette.dangerColor1
|
||||
onClicked: root.removeRejectionRequested()
|
||||
},
|
||||
StatusBaseText {
|
||||
text: root.contactText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
color: Theme.palette.baseColor1
|
||||
},
|
||||
StatusFlatRoundButton {
|
||||
objectName: "moreBtn"
|
||||
id: menuButton
|
||||
width: 32
|
||||
height: 32
|
||||
icon.name: "more"
|
||||
|
@ -1,152 +1,71 @@
|
||||
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 {
|
||||
StatusListView {
|
||||
id: root
|
||||
implicitHeight: (title.height + contactsList.height)
|
||||
|
||||
property var contactsModel
|
||||
|
||||
required 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"]
|
||||
UserSearchFilterContainer {
|
||||
searchString: root.searchString
|
||||
}
|
||||
]
|
||||
|
||||
sorters: StringSorter {
|
||||
sorters: [
|
||||
FilterSorter { // Trusted contacts first
|
||||
enabled: root.panelUsage === Constants.contactsPanelUsage.mutualContacts
|
||||
ValueFilter { roleName: "isVerified"; value: true }
|
||||
},
|
||||
FilterSorter { // Received CRs first
|
||||
id: pendingFilter
|
||||
readonly property int received: Constants.ContactRequestState.Received
|
||||
enabled: root.panelUsage === Constants.contactsPanelUsage.pendingContacts
|
||||
ValueFilter { roleName: "contactRequest"; value: pendingFilter.received }
|
||||
},
|
||||
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 ""
|
||||
}
|
||||
showRejectContactRequestButton: root.panelUsage === Constants.contactsPanelUsage.pendingContacts &&
|
||||
model.contactRequest === Constants.ContactRequestState.Received
|
||||
showAcceptContactRequestButton: showRejectContactRequestButton
|
||||
|
||||
contactText: root.panelUsage === Constants.contactsPanelUsage.pendingContacts &&
|
||||
model.contactRequest === Constants.ContactRequestState.Sent ? qsTr("Contact Request Sent")
|
||||
: ""
|
||||
|
||||
onClicked: Global.openProfilePopup(model.pubKey)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
ContactPanel 1.0 ContactPanel.qml
|
||||
ContactsListPanel 1.0 ContactsListPanel.qml
|
||||
ProfileDescriptionPanel 1.0 ProfileDescriptionPanel.qml
|
||||
ProfileShowcaseAccountsPanel 1.0 ProfileShowcaseAccountsPanel.qml
|
||||
ProfileShowcaseAssetsPanel 1.0 ProfileShowcaseAssetsPanel.qml
|
||||
|
@ -12,7 +12,7 @@ import StatusQ.Core.Backpressure 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
|
||||
import "../stores"
|
||||
import AppLayouts.Profile.stores 1.0
|
||||
|
||||
StatusModal {
|
||||
id: root
|
||||
|
@ -10,3 +10,4 @@ TokenListPopup 1.0 TokenListPopup.qml
|
||||
WalletKeypairAccountMenu 1.0 WalletKeypairAccountMenu.qml
|
||||
WalletAddressMenu 1.0 WalletAddressMenu.qml
|
||||
ConfirmChangePasswordModal 1.0 ConfirmChangePasswordModal.qml
|
||||
SendContactRequestModal 1.0 SendContactRequestModal.qml
|
||||
|
@ -18,9 +18,9 @@ import shared.stores 1.0 as SharedStores
|
||||
import shared.views 1.0
|
||||
import shared.views.chat 1.0
|
||||
|
||||
import "../stores"
|
||||
import "../panels"
|
||||
import "../popups"
|
||||
import AppLayouts.Profile.stores 1.0
|
||||
import AppLayouts.Profile.panels 1.0
|
||||
import AppLayouts.Profile.popups 1.0
|
||||
|
||||
SettingsContentBase {
|
||||
id: root
|
||||
@ -28,20 +28,17 @@ SettingsContentBase {
|
||||
property ContactsStore contactsStore
|
||||
property SharedStores.UtilsStore utilsStore
|
||||
|
||||
property var mutualContactsModel
|
||||
property var blockedContactsModel
|
||||
property var pendingReceivedRequestContactsModel
|
||||
property var pendingSentRequestContactsModel
|
||||
required property var mutualContactsModel
|
||||
required property var blockedContactsModel
|
||||
required property var pendingContactsModel
|
||||
required property int pendingReceivedContactsCount
|
||||
|
||||
property alias searchStr: searchBox.text
|
||||
property bool isPending: false
|
||||
|
||||
titleRowComponentLoader.sourceComponent: StatusButton {
|
||||
objectName: "ContactsView_ContactRequest_Button"
|
||||
text: qsTr("Send contact request to chat key")
|
||||
onClicked: {
|
||||
Global.openPopup(sendContactRequest);
|
||||
}
|
||||
onClicked: sendContactRequestComponent.createObject(root).open()
|
||||
}
|
||||
|
||||
function openContextMenu(model, pubKey) {
|
||||
@ -67,11 +64,108 @@ SettingsContentBase {
|
||||
Global.openMenu(contactContextMenuComponent, this, params)
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentItem
|
||||
headerComponents: ColumnLayout {
|
||||
width: root.contentWidth
|
||||
height: (searchBox.height + contactsTabBar.height
|
||||
+ stackLayout.height + (2 * Theme.bigPadding))
|
||||
spacing: Theme.padding
|
||||
|
||||
StatusTabBar {
|
||||
id: contactsTabBar
|
||||
Layout.fillWidth: true
|
||||
|
||||
StatusTabButton {
|
||||
readonly property int panelUsage: Constants.contactsPanelUsage.mutualContacts
|
||||
|
||||
width: implicitWidth
|
||||
text: qsTr("Contacts")
|
||||
}
|
||||
StatusTabButton {
|
||||
readonly property int panelUsage: Constants.contactsPanelUsage.pendingContacts
|
||||
|
||||
objectName: "ContactsView_PendingRequest_Button"
|
||||
width: implicitWidth
|
||||
enabled: !!root.pendingContactsModel && !root.pendingContactsModel.ModelCount.empty
|
||||
text: qsTr("Pending Requests")
|
||||
badge.value: root.pendingReceivedContactsCount
|
||||
}
|
||||
StatusTabButton {
|
||||
readonly property int panelUsage: Constants.contactsPanelUsage.blockedContacts
|
||||
|
||||
objectName: "ContactsView_Blocked_Button"
|
||||
width: implicitWidth
|
||||
enabled: !!root.blockedContactsModel && !root.blockedContactsModel.ModelCount.empty
|
||||
text: qsTr("Blocked")
|
||||
}
|
||||
}
|
||||
|
||||
SearchBox {
|
||||
id: searchBox
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Search by name or chat key")
|
||||
}
|
||||
}
|
||||
|
||||
ContactsListPanel {
|
||||
id: contactsListPanel
|
||||
width: root.contentWidth
|
||||
height: root.availableHeight
|
||||
|
||||
panelUsage: contactsTabBar.currentItem.panelUsage
|
||||
contactsModel: {
|
||||
switch (panelUsage) {
|
||||
case Constants.contactsPanelUsage.pendingContacts:
|
||||
return root.pendingContactsModel
|
||||
case Constants.contactsPanelUsage.blockedContacts:
|
||||
return root.blockedContactsModel
|
||||
case Constants.contactsPanelUsage.mutualContacts:
|
||||
default:
|
||||
return root.mutualContactsModel
|
||||
}
|
||||
}
|
||||
section.property: {
|
||||
switch (contactsListPanel.panelUsage) {
|
||||
case Constants.contactsPanelUsage.pendingContacts:
|
||||
return "contactRequest"
|
||||
case Constants.contactsPanelUsage.mutualContacts:
|
||||
return "isVerified"
|
||||
case Constants.contactsPanelUsage.blockedContacts:
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
section.delegate: SectionComponent {
|
||||
text: {
|
||||
switch (contactsListPanel.panelUsage) {
|
||||
case Constants.contactsPanelUsage.pendingContacts:
|
||||
return section === `${Constants.ContactRequestState.Received}` ? qsTr("Received") : qsTr("Sent")
|
||||
case Constants.contactsPanelUsage.mutualContacts:
|
||||
return section === "true" ? qsTr("Trusted Contacts") : qsTr("Contacts")
|
||||
case Constants.contactsPanelUsage.blockedContacts:
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
|
||||
|
||||
header: NoFriendsRectangle {
|
||||
width: ListView.view.width
|
||||
visible: ListView.view.count === 0
|
||||
inviteButtonVisible: searchBox.text === ""
|
||||
}
|
||||
|
||||
searchString: searchBox.text
|
||||
onOpenContactContextMenu: root.openContextMenu(contactsModel, publicKey)
|
||||
onSendMessageActionTriggered: root.contactsStore.joinPrivateChat(publicKey)
|
||||
onContactRequestAccepted: root.contactsStore.acceptContactRequest(publicKey, "")
|
||||
onContactRequestRejected: root.contactsStore.dismissContactRequest(publicKey, "")
|
||||
|
||||
Component {
|
||||
id: sendContactRequestComponent
|
||||
SendContactRequestModal {
|
||||
contactsStore: root.contactsStore
|
||||
onClosed: destroy()
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: contactContextMenuComponent
|
||||
@ -98,191 +192,27 @@ SettingsContentBase {
|
||||
onClosed: destroy()
|
||||
}
|
||||
}
|
||||
SearchBox {
|
||||
id: searchBox
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
placeholderText: qsTr("Search by a display name or chat key")
|
||||
}
|
||||
|
||||
StatusTabBar {
|
||||
id: contactsTabBar
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: searchBox.bottom
|
||||
anchors.topMargin: Theme.padding
|
||||
component SectionComponent: Rectangle {
|
||||
required property string section
|
||||
property alias text: sectionText.text
|
||||
|
||||
StatusTabButton {
|
||||
id: contactsBtn
|
||||
leftPadding: Theme.padding
|
||||
width: implicitWidth
|
||||
text: qsTr("Contacts")
|
||||
}
|
||||
StatusTabButton {
|
||||
id: pendingRequestsBtn
|
||||
objectName: "ContactsView_PendingRequest_Button"
|
||||
width: implicitWidth
|
||||
enabled: !root.pendingReceivedRequestContactsModel.ModelCount.empty ||
|
||||
!root.pendingSentRequestContactsModel.ModelCount.empty
|
||||
text: qsTr("Pending Requests")
|
||||
badge.value: root.pendingReceivedRequestContactsModel.ModelCount.count
|
||||
}
|
||||
StatusTabButton {
|
||||
id: blockedBtn
|
||||
objectName: "ContactsView_Blocked_Button"
|
||||
width: implicitWidth
|
||||
enabled: !root.blockedContactsModel.ModelCount.empty
|
||||
text: qsTr("Blocked")
|
||||
}
|
||||
}
|
||||
width: ListView.view.width
|
||||
height: sectionText.implicitHeight
|
||||
color: Theme.palette.statusListItem.backgroundColor
|
||||
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: contactsTabBar.bottom
|
||||
currentIndex: contactsTabBar.currentIndex
|
||||
anchors.topMargin: Theme.padding
|
||||
// CONTACTS
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 0
|
||||
Layout.maximumHeight: (verifiedContacts.height + mutualContacts.height + noFriendsItem.height)
|
||||
visible: (stackLayout.currentIndex === 0)
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
stackLayout.height = height+contactsTabBar.anchors.topMargin;
|
||||
}
|
||||
}
|
||||
spacing: Theme.padding
|
||||
ContactsListPanel {
|
||||
id: verifiedContacts
|
||||
StatusBaseText {
|
||||
id: sectionText
|
||||
width: parent.width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
topPadding: Theme.halfPadding
|
||||
bottomPadding: Theme.halfPadding
|
||||
|
||||
Layout.fillWidth: true
|
||||
title: qsTr("Trusted Contacts")
|
||||
visible: !noFriendsItem.visible && count > 0
|
||||
contactsModel: root.mutualContactsModel
|
||||
searchString: searchBox.text
|
||||
onOpenContactContextMenu: root.openContextMenu(contactsModel, publicKey)
|
||||
panelUsage: Constants.contactsPanelUsage.verifiedMutualContacts
|
||||
onSendMessageActionTriggered: {
|
||||
root.contactsStore.joinPrivateChat(publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
ContactsListPanel {
|
||||
id: mutualContacts
|
||||
|
||||
Layout.fillWidth: true
|
||||
visible: !noFriendsItem.visible && count > 0
|
||||
title: qsTr("Contacts")
|
||||
contactsModel: root.mutualContactsModel
|
||||
searchString: searchBox.text
|
||||
onOpenContactContextMenu: root.openContextMenu(contactsModel, publicKey)
|
||||
panelUsage: Constants.contactsPanelUsage.mutualContacts
|
||||
|
||||
onSendMessageActionTriggered: {
|
||||
root.contactsStore.joinPrivateChat(publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: noFriendsItem
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: visible ? (root.contentHeight - (2*searchBox.height) - contactsTabBar.height - contactsTabBar.anchors.topMargin) : 0
|
||||
visible: root.mutualContactsModel.ModelCount.empty
|
||||
|
||||
NoFriendsRectangle {
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("You don't have any contacts yet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PENDING REQUESTS
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 0
|
||||
Layout.maximumHeight: (receivedRequests.height + sentRequests.height)
|
||||
spacing: Theme.padding
|
||||
visible: (stackLayout.currentIndex === 1)
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
stackLayout.height = height+contactsTabBar.anchors.topMargin;
|
||||
}
|
||||
}
|
||||
ContactsListPanel {
|
||||
id: receivedRequests
|
||||
|
||||
objectName: "receivedRequests_ContactsListPanel"
|
||||
Layout.fillWidth: true
|
||||
title: qsTr("Received")
|
||||
searchString: searchBox.text
|
||||
visible: count > 0
|
||||
onOpenContactContextMenu: root.openContextMenu(contactsModel, publicKey)
|
||||
contactsModel: root.pendingReceivedRequestContactsModel
|
||||
panelUsage: Constants.contactsPanelUsage.receivedContactRequest
|
||||
|
||||
onSendMessageActionTriggered: {
|
||||
root.contactsStore.joinPrivateChat(publicKey)
|
||||
}
|
||||
|
||||
onContactRequestAccepted: {
|
||||
root.contactsStore.acceptContactRequest(publicKey, "")
|
||||
}
|
||||
|
||||
onContactRequestRejected: {
|
||||
root.contactsStore.dismissContactRequest(publicKey, "")
|
||||
}
|
||||
}
|
||||
|
||||
ContactsListPanel {
|
||||
id: sentRequests
|
||||
|
||||
objectName: "sentRequests_ContactsListPanel"
|
||||
Layout.fillWidth: true
|
||||
title: qsTr("Sent")
|
||||
searchString: searchBox.text
|
||||
visible: count > 0
|
||||
onOpenContactContextMenu: root.openContextMenu(contactsModel, publicKey)
|
||||
contactsModel: root.pendingSentRequestContactsModel
|
||||
panelUsage: Constants.contactsPanelUsage.sentContactRequest
|
||||
}
|
||||
}
|
||||
|
||||
// BLOCKED
|
||||
ContactsListPanel {
|
||||
id: blockedContacts
|
||||
|
||||
Layout.fillWidth: true
|
||||
searchString: searchBox.text
|
||||
onOpenContactContextMenu: root.openContextMenu(contactsModel, publicKey)
|
||||
contactsModel: root.blockedContactsModel
|
||||
panelUsage: Constants.contactsPanelUsage.blockedContacts
|
||||
visible: (stackLayout.currentIndex === 2)
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
stackLayout.height = height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loadingIndicator
|
||||
StatusLoadingIndicator {
|
||||
width: 12
|
||||
height: 12
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: sendContactRequest
|
||||
SendContactRequestModal {
|
||||
contactsStore: root.contactsStore
|
||||
onClosed: destroy()
|
||||
}
|
||||
color: Theme.palette.baseColor1
|
||||
font.pixelSize: Theme.additionalTextSize
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ AboutView 1.0 AboutView.qml
|
||||
AppearanceView 1.0 AppearanceView.qml
|
||||
ChangePasswordView 1.0 ChangePasswordView.qml
|
||||
CommunitiesView 1.0 CommunitiesView.qml
|
||||
ContactsView 1.0 ContactsView.qml
|
||||
CurrenciesModel 1.0 CurrenciesModel.qml
|
||||
LanguageView 1.0 LanguageView.qml
|
||||
NotificationsView 1.0 NotificationsView.qml
|
||||
PrivacyAndSecurityView 1.0 PrivacyAndSecurityView.qml
|
||||
SyncingView 1.0 SyncingView.qml
|
||||
SettingsContentBase 1.0 SettingsContentBase.qml
|
||||
|
@ -541,7 +541,7 @@ Item {
|
||||
Global.displayToastMessage(toastTitle, toastSubtitle, toastIcon, toastLoading, toastType, toastLink)
|
||||
}
|
||||
|
||||
function onCommunityMemberStatusEphemeralNotification(communityName: string, memberName: string, state: CommunityMembershipRequestState) {
|
||||
function onCommunityMemberStatusEphemeralNotification(communityName: string, memberName: string, state: int) {
|
||||
var text = ""
|
||||
switch (state) {
|
||||
case Constants.CommunityMembershipRequestState.Banned:
|
||||
@ -1746,8 +1746,8 @@ Item {
|
||||
|
||||
mutualContactsModel: contactsModelAdaptor.mutualContacts
|
||||
blockedContactsModel: contactsModelAdaptor.blockedContacts
|
||||
pendingReceivedRequestContactsModel: contactsModelAdaptor.pendingReceivedRequestContacts
|
||||
pendingSentRequestContactsModel: contactsModelAdaptor.pendingSentRequestContacts
|
||||
pendingContactsModel: contactsModelAdaptor.pendingContacts
|
||||
pendingReceivedContactsCount: contactsModelAdaptor.pendingReceivedRequestContacts.count
|
||||
|
||||
Binding on settingsSubsection {
|
||||
value: profileLoader.settingsSubsection
|
||||
|
@ -19,7 +19,7 @@ QObject {
|
||||
localNickname [string] - local nickname set by the current user
|
||||
alias [string] - generated 3 word name
|
||||
icon [string] - thumbnail image of the user
|
||||
colorId [string] - generated color ID for the user's profile
|
||||
colorId [int] - generated color ID for the user's profile
|
||||
colorHash [string] - generated color hash for the user's profile
|
||||
onlineStatus [int] - the online status of the member
|
||||
isContact [bool] - whether the user is a mutual contact or not
|
||||
@ -75,4 +75,21 @@ QObject {
|
||||
value: Constants.ContactRequestState.Sent
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var pendingContacts: SortFilterProxyModel {
|
||||
sourceModel: root.allContacts ?? null
|
||||
|
||||
filters: [
|
||||
AnyOf {
|
||||
ValueFilter {
|
||||
roleName: "contactRequest"
|
||||
value: Constants.ContactRequestState.Received
|
||||
}
|
||||
ValueFilter {
|
||||
roleName: "contactRequest"
|
||||
value: Constants.ContactRequestState.Sent
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
39
ui/imports/shared/UserSearchFilterContainer.qml
Normal file
39
ui/imports/shared/UserSearchFilterContainer.qml
Normal file
@ -0,0 +1,39 @@
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
AnyOf {
|
||||
id: root
|
||||
|
||||
property string searchString
|
||||
|
||||
function searchPredicate(ensName, displayName, aliasName) {
|
||||
const lowerCaseSearchString = root.searchString.toLowerCase()
|
||||
const secondaryName = ProfileUtils.displayName("", ensName, displayName, aliasName)
|
||||
|
||||
return secondaryName.toLowerCase().includes(lowerCaseSearchString)
|
||||
}
|
||||
|
||||
enabled: root.searchString !== ""
|
||||
|
||||
// substring search for either nickname or the other primary/secondary display name
|
||||
SearchFilter {
|
||||
roleName: "localNickname"
|
||||
searchPhrase: root.searchString
|
||||
}
|
||||
FastExpressionFilter {
|
||||
expression: {
|
||||
root.searchString
|
||||
return root.searchPredicate(model.ensName, model.displayName, model.alias)
|
||||
}
|
||||
expectedRoles: ["ensName", "displayName", "alias"]
|
||||
}
|
||||
// exact search for the full key
|
||||
ValueFilter {
|
||||
roleName: "compressedPubKey"
|
||||
value: root.searchString
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ StatusMemberListItem {
|
||||
pubKey: model.isEnsVerified ? "" : model.compressedPubKey
|
||||
nickName: model.localNickname
|
||||
userName: ProfileUtils.displayName("", model.ensName, model.displayName, model.alias)
|
||||
isVerified: model.isVerified
|
||||
isUntrustworthy: model.isUntrustworthy
|
||||
isBlocked: model.isBlocked
|
||||
isVerified: model.isVerified || model.trustStatus === Constants.trustStatus.trusted
|
||||
isUntrustworthy: model.isUntrustworthy || model.trustStatus === Constants.trustStatus.untrustworthy
|
||||
isContact: model.isContact
|
||||
icon.name: model.icon
|
||||
icon.name: model.thumbnailImage || model.icon
|
||||
icon.color: Utils.colorForColorId(model.colorId)
|
||||
status: model.onlineStatus
|
||||
ringSettings.ringSpecModel: model.colorHash
|
||||
|
@ -3,3 +3,4 @@ module shared
|
||||
DelegateModelGeneralized 1.0 DelegateModelGeneralized.qml
|
||||
LoadingAnimation 1.0 LoadingAnimation.qml
|
||||
MacTrafficLights 1.0 MacTrafficLights.qml
|
||||
UserSearchFilterContainer 1.0 UserSearchFilterContainer.qml
|
||||
|
@ -1,31 +1,31 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
|
||||
import "../popups"
|
||||
import utils 1.0
|
||||
import shared.popups 1.0
|
||||
|
||||
Item {
|
||||
id: noContactsRect
|
||||
id: root
|
||||
implicitWidth: 260
|
||||
implicitHeight: visible ? 120 : 0
|
||||
|
||||
property string text: qsTr("You don’t have any contacts yet. Invite your friends to start chatting.")
|
||||
property string text: inviteButtonVisible ? qsTr("You don’t have any contacts yet. Invite your friends to start chatting.")
|
||||
: qsTr("No users match your search")
|
||||
property alias textColor: noContacts.color
|
||||
property bool inviteButtonVisible: true
|
||||
|
||||
StatusBaseText {
|
||||
id: noContacts
|
||||
text: noContactsRect.text
|
||||
text: root.text
|
||||
color: Theme.palette.baseColor1
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Theme.padding
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: 15
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StatusButton {
|
||||
@ -33,7 +33,8 @@ Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: noContacts.bottom
|
||||
anchors.topMargin: Theme.padding
|
||||
onClicked: Global.openPopup(inviteFriendsPopup);
|
||||
visible: root.inviteButtonVisible
|
||||
onClicked: inviteFriendsPopup.createObject(root).open()
|
||||
}
|
||||
|
||||
Component {
|
||||
|
@ -498,12 +498,8 @@ QtObject {
|
||||
readonly property QtObject contactsPanelUsage: QtObject {
|
||||
readonly property int unknownPosition: -1
|
||||
readonly property int mutualContacts: 0
|
||||
readonly property int verifiedMutualContacts: 1
|
||||
readonly property int sentContactRequest: 2
|
||||
readonly property int receivedContactRequest: 3
|
||||
readonly property int rejectedSentContactRequest: 4
|
||||
readonly property int rejectedReceivedContactRequest: 5
|
||||
readonly property int blockedContacts: 6
|
||||
readonly property int pendingContacts: 1
|
||||
readonly property int blockedContacts: 2
|
||||
}
|
||||
|
||||
readonly property QtObject keypair: QtObject {
|
||||
|
Loading…
x
Reference in New Issue
Block a user