mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-09 22:06:25 +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.Chat.stores 1.0 as ChatStores
|
||||||
import AppLayouts.Profile.stores 1.0 as ProfileStores
|
import AppLayouts.Profile.stores 1.0 as ProfileStores
|
||||||
|
|
||||||
|
import shared.stores 1.0
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
import Models 1.0
|
import Models 1.0
|
||||||
@ -15,8 +16,6 @@ import SortFilterProxyModel 0.2
|
|||||||
import Storybook 1.0
|
import Storybook 1.0
|
||||||
|
|
||||||
import StatusQ 0.1
|
import StatusQ 0.1
|
||||||
import StatusQ.Core.Utils 0.1 as SQUtils
|
|
||||||
|
|
||||||
|
|
||||||
SplitView {
|
SplitView {
|
||||||
id: root
|
id: root
|
||||||
@ -24,46 +23,27 @@ SplitView {
|
|||||||
orientation: Qt.Vertical
|
orientation: Qt.Vertical
|
||||||
Logs { id: logs }
|
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 {
|
MembersTabPanel {
|
||||||
id: membersTabPanelPage
|
id: membersTabPanelPage
|
||||||
SplitView.fillWidth: true
|
SplitView.fillWidth: true
|
||||||
SplitView.fillHeight: true
|
SplitView.fillHeight: true
|
||||||
placeholderText: "Search users"
|
|
||||||
model: usersModelWithMembershipState
|
model: usersModelWithMembershipState
|
||||||
panelType: viewStateSelector.currentValue
|
panelType: viewStateSelector.currentValue
|
||||||
|
searchString: ctrlSearch.text
|
||||||
|
|
||||||
rootStore: ChatStores.RootStore {
|
rootStore: ChatStores.RootStore {
|
||||||
contactsStore: ProfileStores.ContactsStore {
|
contactsStore: ProfileStores.ContactsStore {
|
||||||
readonly property string myPublicKey: "0x000"
|
readonly property string myPublicKey: "0x000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
utilsStore: UtilsStore {
|
||||||
|
function getEmojiHash(publicKey) {
|
||||||
|
if (publicKey === "")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
return JSON.stringify(["👨🏻🍼", "🏃🏿♂️", "🌇", "🤶🏿", "🏮","🤷🏻♂️", "🤦🏻", "📣", "🤎", "👷🏽", "😺", "🥞", "🔃", "🧝🏽♂️"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onKickUserClicked: {
|
onKickUserClicked: {
|
||||||
logs.logEvent("MembersTabPanel::onKickUserClicked", ["id", "name"], arguments)
|
logs.logEvent("MembersTabPanel::onKickUserClicked", ["id", "name"], arguments)
|
||||||
@ -132,7 +112,7 @@ SplitView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LogsAndControlsPanel {
|
LogsAndControlsPanel {
|
||||||
SplitView.minimumHeight: 100
|
SplitView.minimumHeight: 200
|
||||||
SplitView.preferredHeight: 320
|
SplitView.preferredHeight: 320
|
||||||
|
|
||||||
logsView.logText: logs.logText
|
logsView.logText: logs.logText
|
||||||
@ -144,6 +124,7 @@ SplitView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
|
Layout.preferredWidth: 300
|
||||||
id: viewStateSelector
|
id: viewStateSelector
|
||||||
textRole: "text"
|
textRole: "text"
|
||||||
valueRole: "value"
|
valueRole: "value"
|
||||||
@ -155,6 +136,13 @@ SplitView {
|
|||||||
ListElement { text: "Declined Members"; value: MembersTabPanel.TabType.DeclinedRequests }
|
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
|
// 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 {
|
StatusTabButton {
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
enabled: false
|
enabled: false
|
||||||
text: qsTr("Blocked & disabled")
|
text: "Blocked & disabled"
|
||||||
}
|
}
|
||||||
StatusTabButton {
|
StatusTabButton {
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
|
@ -9,9 +9,10 @@ ListModel {
|
|||||||
compressedPubKey: "zQ3shQBu4PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsBDF4",
|
compressedPubKey: "zQ3shQBu4PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsBDF4",
|
||||||
onlineStatus: Constants.onlineStatus.online,
|
onlineStatus: Constants.onlineStatus.online,
|
||||||
isContact: true,
|
isContact: true,
|
||||||
|
isBlocked: false,
|
||||||
isVerified: false,
|
isVerified: false,
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
isUntrustworthy: true,
|
isUntrustworthy: false,
|
||||||
displayName: "Mike has a very long name that should elide " +
|
displayName: "Mike has a very long name that should elide " +
|
||||||
"eventually and result in a tooltip displayed instead",
|
"eventually and result in a tooltip displayed instead",
|
||||||
alias: "",
|
alias: "",
|
||||||
@ -26,13 +27,15 @@ ListModel {
|
|||||||
],
|
],
|
||||||
isAwaitingAddress: false,
|
isAwaitingAddress: false,
|
||||||
memberRole: Constants.memberRole.none,
|
memberRole: Constants.memberRole.none,
|
||||||
trustStatus: Constants.trustStatus.untrustworthy
|
trustStatus: Constants.trustStatus.unknown
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pubKey: "0x04df12f12f12f12f1234",
|
pubKey: "0x04df12f12f12f12f1234",
|
||||||
compressedPubKey: "zQ3shQBAAPRDX17vewYyvSczbTj344viTXxcMNvQLeyQsBDF4",
|
compressedPubKey: "zQ3shQBAAPRDX17vewYyvSczbTj344viTXxcMNvQLeyQsBDF4",
|
||||||
onlineStatus: Constants.onlineStatus.inactive,
|
onlineStatus: Constants.onlineStatus.inactive,
|
||||||
isContact: false,
|
isContact: false,
|
||||||
|
contactRequest: Constants.ContactRequestState.Sent,
|
||||||
|
isBlocked: false,
|
||||||
isVerified: false,
|
isVerified: false,
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
isUntrustworthy: false,
|
isUntrustworthy: false,
|
||||||
@ -49,13 +52,14 @@ ListModel {
|
|||||||
],
|
],
|
||||||
isAwaitingAddress: false,
|
isAwaitingAddress: false,
|
||||||
memberRole: Constants.memberRole.owner,
|
memberRole: Constants.memberRole.owner,
|
||||||
trustStatus: Constants.trustStatus.trusted
|
trustStatus: Constants.trustStatus.unknown
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pubKey: "0x04d1b7cc0ef3f470f1238",
|
pubKey: "0x04d1b7cc0ef3f470f1238",
|
||||||
compressedPubKey: "zQ3shQ7u3PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsCDF4",
|
compressedPubKey: "zQ3shQ7u3PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsCDF4",
|
||||||
onlineStatus: Constants.onlineStatus.inactive,
|
onlineStatus: Constants.onlineStatus.inactive,
|
||||||
isContact: false,
|
isContact: false,
|
||||||
|
isBlocked: true,
|
||||||
isVerified: false,
|
isVerified: false,
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
isUntrustworthy: true,
|
isUntrustworthy: true,
|
||||||
@ -66,6 +70,10 @@ ListModel {
|
|||||||
icon: ModelsData.icons.dragonereum,
|
icon: ModelsData.icons.dragonereum,
|
||||||
colorId: 4,
|
colorId: 4,
|
||||||
isEnsVerified: false,
|
isEnsVerified: false,
|
||||||
|
colorHash: [
|
||||||
|
{ colorId: 7, segmentLength: 3 },
|
||||||
|
{ colorId: 12, segmentLength: 1 }
|
||||||
|
],
|
||||||
isAwaitingAddress: false,
|
isAwaitingAddress: false,
|
||||||
memberRole: Constants.memberRole.none,
|
memberRole: Constants.memberRole.none,
|
||||||
trustStatus: Constants.trustStatus.untrustworthy
|
trustStatus: Constants.trustStatus.untrustworthy
|
||||||
@ -75,16 +83,17 @@ ListModel {
|
|||||||
compressedPubKey: "zQ3shQAL4PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsBDF4",
|
compressedPubKey: "zQ3shQAL4PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsBDF4",
|
||||||
onlineStatus: Constants.onlineStatus.online,
|
onlineStatus: Constants.onlineStatus.online,
|
||||||
isContact: true,
|
isContact: true,
|
||||||
isVerified: true,
|
isBlocked: false,
|
||||||
|
isVerified: false,
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
isUntrustworthy: true,
|
isUntrustworthy: true,
|
||||||
displayName: "Maria",
|
displayName: "Maria",
|
||||||
alias: "meth",
|
alias: "meth",
|
||||||
localNickname: "86.eth",
|
localNickname: "",
|
||||||
ensName: "8⃣_6⃣.eth",
|
ensName: "",
|
||||||
icon: "",
|
icon: "",
|
||||||
colorId: 5,
|
colorId: 5,
|
||||||
isEnsVerified: true,
|
isEnsVerified: false,
|
||||||
isAwaitingAddress: false,
|
isAwaitingAddress: false,
|
||||||
memberRole: Constants.memberRole.none,
|
memberRole: Constants.memberRole.none,
|
||||||
trustStatus: Constants.trustStatus.untrustworthy
|
trustStatus: Constants.trustStatus.untrustworthy
|
||||||
@ -93,8 +102,10 @@ ListModel {
|
|||||||
pubKey: "0x04d1bed192343f470f1255",
|
pubKey: "0x04d1bed192343f470f1255",
|
||||||
compressedPubKey: "zQ3shQBu4PGDX17vewYyvSczbTj344viTXxcMNvQLeyQsBD1A",
|
compressedPubKey: "zQ3shQBu4PGDX17vewYyvSczbTj344viTXxcMNvQLeyQsBD1A",
|
||||||
onlineStatus: Constants.onlineStatus.online,
|
onlineStatus: Constants.onlineStatus.online,
|
||||||
isContact: true,
|
isContact: false,
|
||||||
isVerified: true,
|
contactRequest: Constants.ContactRequestState.Received,
|
||||||
|
isBlocked: false,
|
||||||
|
isVerified: false,
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
isUntrustworthy: true,
|
isUntrustworthy: true,
|
||||||
displayName: "",
|
displayName: "",
|
||||||
@ -113,7 +124,8 @@ ListModel {
|
|||||||
compressedPubKey: "zQ3shQBk4PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsB994",
|
compressedPubKey: "zQ3shQBk4PRDX17vewYyvSczbTj344viTXxcMNvQLeyQsB994",
|
||||||
onlineStatus: Constants.onlineStatus.inactive,
|
onlineStatus: Constants.onlineStatus.inactive,
|
||||||
isContact: true,
|
isContact: true,
|
||||||
isVerified: false,
|
isBlocked: false,
|
||||||
|
isVerified: true,
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
isUntrustworthy: false,
|
isUntrustworthy: false,
|
||||||
displayName: "",
|
displayName: "",
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
|
||||||
|
|
||||||
import StatusQ.Core.Utils 0.1
|
|
||||||
|
|
||||||
import utils 1.0
|
|
||||||
|
|
||||||
Flow {
|
Flow {
|
||||||
id: root
|
id: root
|
||||||
|
@ -52,7 +52,7 @@ settingsContentBaseScrollView_ContactListPanel = {"container": mainWindow_Contac
|
|||||||
settingsContentBaseScrollView_Item = {"container": mainWindow_ContactsView, "type": "Item", "unnamed": 1, "visible": True}
|
settingsContentBaseScrollView_Item = {"container": mainWindow_ContactsView, "type": "Item", "unnamed": 1, "visible": True}
|
||||||
settingsContentBaseScrollView_sentRequests_ContactsListPanel = {"container": mainWindow_ContactsView, "objectName": "sentRequests_ContactsListPanel", "type": "ContactsListPanel", "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}
|
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_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_Invite_friends_StatusButton = {"container": mainWindow_ContactsView, "type": "StatusButton", "unnamed": 1, "visible": True}
|
||||||
settingsContentBaseScrollView_NoFriendsRectangle = {"container": mainWindow_ContactsView, "type": "NoFriendsRectangle", "unnamed": 1, "visible": True}
|
settingsContentBaseScrollView_NoFriendsRectangle = {"container": mainWindow_ContactsView, "type": "NoFriendsRectangle", "unnamed": 1, "visible": True}
|
||||||
|
@ -71,7 +71,7 @@ Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
visible: root.isContact || (root.trustIndicator !== StatusContactVerificationIcons.TrustedType.None)
|
visible: root.isContact || root.isBlocked || (root.trustIndicator !== StatusContactVerificationIcons.TrustedType.None)
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hoverHandler
|
id: hoverHandler
|
||||||
@ -104,7 +104,8 @@ Row {
|
|||||||
|
|
||||||
// (un)trusted
|
// (un)trusted
|
||||||
StatusRoundIcon {
|
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
|
asset: root.trustContactIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,22 +58,27 @@ ItemDelegate {
|
|||||||
*/
|
*/
|
||||||
property string pubKey: ""
|
property string pubKey: ""
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string StatusMemberListItem::isContact
|
\qmlproperty bool StatusMemberListItem::isContact
|
||||||
This property holds if the member represented is contact.
|
This property holds if the member represented is contact.
|
||||||
*/
|
*/
|
||||||
property bool isContact: false
|
property bool isContact: false
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string StatusMemberListItem::isVerified
|
\qmlproperty bool StatusMemberListItem::isVerified
|
||||||
This property holds if the member represented is verified contact.
|
This property holds if the member represented is verified contact.
|
||||||
*/
|
*/
|
||||||
property bool isVerified: false
|
property bool isVerified: false
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string StatusMemberListItem::isUntrustworthy
|
\qmlproperty bool StatusMemberListItem::isUntrustworthy
|
||||||
This property holds if the member represented is untrustworthy.
|
This property holds if the member represented is untrustworthy.
|
||||||
*/
|
*/
|
||||||
property bool isUntrustworthy: false
|
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.
|
This property holds the connectivity status of the member represented.
|
||||||
|
|
||||||
int unknown: -1
|
int unknown: -1
|
||||||
@ -84,7 +89,7 @@ ItemDelegate {
|
|||||||
// FIXME: move Constants.onlineStatus from status-desktop
|
// FIXME: move Constants.onlineStatus from status-desktop
|
||||||
property int status: 0
|
property int status: 0
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string StatusMemberListItem::isAdmin
|
\qmlproperty bool StatusMemberListItem::isAdmin
|
||||||
This property holds the admin status of the member represented.
|
This property holds the admin status of the member represented.
|
||||||
*/
|
*/
|
||||||
property bool isAdmin: false
|
property bool isAdmin: false
|
||||||
@ -126,7 +131,7 @@ ItemDelegate {
|
|||||||
property alias badge: identicon.badge
|
property alias badge: identicon.badge
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmlsignal
|
\qmlsignal clicked
|
||||||
This signal is emitted when the StatusMemberListItem is clicked.
|
This signal is emitted when the StatusMemberListItem is clicked.
|
||||||
*/
|
*/
|
||||||
signal clicked(var mouse)
|
signal clicked(var mouse)
|
||||||
@ -158,9 +163,9 @@ ItemDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
horizontalPadding: 8
|
horizontalPadding: Theme.halfPadding
|
||||||
verticalPadding: 12
|
verticalPadding: 12
|
||||||
spacing: 8
|
spacing: Theme.halfPadding
|
||||||
|
|
||||||
icon.width: 32
|
icon.width: 32
|
||||||
icon.height: 32
|
icon.height: 32
|
||||||
@ -170,7 +175,7 @@ ItemDelegate {
|
|||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: root.color
|
color: root.color
|
||||||
radius: 8
|
radius: Theme.radius
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -200,9 +205,8 @@ ItemDelegate {
|
|||||||
// badge
|
// badge
|
||||||
badge.visible: true
|
badge.visible: true
|
||||||
badge.color: root.status === 1 ? Theme.palette.successColor1 : Theme.palette.baseColor1 // FIXME, see root.status
|
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.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.implicitHeight: 12 // 8 px + 2 px * 2 borders
|
||||||
badge.implicitWidth: 12 // 8 px + 2 px * 2 borders
|
badge.implicitWidth: 12 // 8 px + 2 px * 2 borders
|
||||||
}
|
}
|
||||||
@ -243,7 +247,7 @@ ItemDelegate {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
text: d.composeSubtitle()
|
text: d.composeSubtitle()
|
||||||
font.pixelSize: 10
|
font.pixelSize: Theme.asideTextFontSize
|
||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
visible: !!text
|
visible: !!text
|
||||||
|
|
||||||
@ -280,6 +284,7 @@ ItemDelegate {
|
|||||||
id: statusContactVerificationIcons
|
id: statusContactVerificationIcons
|
||||||
StatusContactVerificationIcons {
|
StatusContactVerificationIcons {
|
||||||
isContact: root.isContact
|
isContact: root.isContact
|
||||||
|
isBlocked: root.isBlocked
|
||||||
trustIndicator: {
|
trustIndicator: {
|
||||||
if (root.isVerified)
|
if (root.isVerified)
|
||||||
return StatusContactVerificationIcons.TrustedType.Verified
|
return StatusContactVerificationIcons.TrustedType.Verified
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import QtQuick 2.14
|
import QtQuick 2.15
|
||||||
|
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
@ -12,4 +12,5 @@ StatusFlatRoundButton {
|
|||||||
implicitHeight: 24
|
implicitHeight: 24
|
||||||
icon.color: Theme.palette.directColor9
|
icon.color: Theme.palette.directColor9
|
||||||
backgroundHoverColor: "transparent"
|
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 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: statusFlatRoundButton
|
id: statusFlatRoundButton
|
||||||
|
|
||||||
property StatusAssetSettings icon: StatusAssetSettings {
|
property StatusAssetSettings icon: StatusAssetSettings {
|
||||||
width: 23
|
width: 24
|
||||||
height: 23
|
height: 24
|
||||||
rotation: 0
|
rotation: 0
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import QtQuick 2.14
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.14
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.14
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
@ -17,8 +17,10 @@ Control {
|
|||||||
|
|
||||||
signal toggled
|
signal toggled
|
||||||
|
|
||||||
|
padding: 4
|
||||||
|
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
spacing: 16
|
spacing: Theme.padding
|
||||||
|
|
||||||
StatusRoundIcon {
|
StatusRoundIcon {
|
||||||
asset.name: root.icon
|
asset.name: root.icon
|
||||||
@ -26,22 +28,21 @@ Control {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
StatusBaseText {
|
|
||||||
text: root.title
|
StatusBaseText {
|
||||||
color: Theme.palette.directColor1
|
Layout.fillWidth: true
|
||||||
font.pixelSize: 15
|
text: root.title
|
||||||
}
|
visible: !!text
|
||||||
|
color: Theme.palette.directColor1
|
||||||
Item { Layout.fillWidth: true }
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
|
||||||
text: root.subTitle
|
text: root.subTitle
|
||||||
visible: !!text
|
visible: !!text
|
||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
font.pixelSize: 15
|
|
||||||
lineHeight: 1.2
|
lineHeight: 1.2
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
@ -51,6 +52,7 @@ Control {
|
|||||||
StatusSwitch {
|
StatusSwitch {
|
||||||
id: switchItem
|
id: switchItem
|
||||||
objectName: "switchItem"
|
objectName: "switchItem"
|
||||||
|
padding: 0
|
||||||
|
|
||||||
onToggled: root.toggled()
|
onToggled: root.toggled()
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
import AppLayouts.Communities.controls 1.0
|
import AppLayouts.Communities.controls 1.0
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
leftPadding: 64
|
leftPadding: Theme.xlPadding*2
|
||||||
topPadding: 16
|
topPadding: Theme.padding
|
||||||
|
|
||||||
|
readonly property int preferredContentWidth: 560
|
||||||
|
|
||||||
property alias buttons: pageHeader.buttons
|
property alias buttons: pageHeader.buttons
|
||||||
property alias subtitle: pageHeader.subtitle
|
property alias subtitle: pageHeader.subtitle
|
||||||
@ -18,8 +22,8 @@ Page {
|
|||||||
id: pageHeader
|
id: pageHeader
|
||||||
|
|
||||||
height: 44
|
height: 44
|
||||||
leftPadding: 64
|
leftPadding: root.leftPadding
|
||||||
rightPadding: width - 560 - leftPadding
|
rightPadding: width - root.preferredContentWidth - leftPadding
|
||||||
|
|
||||||
title: root.title
|
title: root.title
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@ import QtQuick.Layouts 1.15
|
|||||||
|
|
||||||
import StatusQ 0.1
|
import StatusQ 0.1
|
||||||
import StatusQ.Controls 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 shared.stores 1.0 as SharedStores
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
@ -72,14 +74,15 @@ SettingsPage {
|
|||||||
membersTabBar.currentIndex = tabButton.TabBar.index
|
membersTabBar.currentIndex = tabButton.TabBar.index
|
||||||
}
|
}
|
||||||
|
|
||||||
spacing: 19
|
spacing: Theme.padding
|
||||||
|
|
||||||
StatusTabBar {
|
StatusTabBar {
|
||||||
id: membersTabBar
|
id: membersTabBar
|
||||||
Layout.fillWidth: true
|
Layout.preferredWidth: root.preferredContentWidth
|
||||||
Layout.topMargin: 5
|
|
||||||
|
|
||||||
StatusTabButton {
|
StatusTabButton {
|
||||||
|
readonly property int subSection: MembersTabPanel.TabType.AllMembers
|
||||||
|
|
||||||
id: allMembersBtn
|
id: allMembersBtn
|
||||||
objectName: "allMembersButton"
|
objectName: "allMembersButton"
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
@ -87,6 +90,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StatusTabButton {
|
StatusTabButton {
|
||||||
|
readonly property int subSection: MembersTabPanel.TabType.PendingRequests
|
||||||
|
|
||||||
id: pendingRequestsBtn
|
id: pendingRequestsBtn
|
||||||
objectName: "pendingRequestsButton"
|
objectName: "pendingRequestsButton"
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
@ -95,6 +100,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StatusTabButton {
|
StatusTabButton {
|
||||||
|
readonly property int subSection: MembersTabPanel.TabType.DeclinedRequests
|
||||||
|
|
||||||
id: declinedRequestsBtn
|
id: declinedRequestsBtn
|
||||||
objectName: "declinedRequestsButton"
|
objectName: "declinedRequestsButton"
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
@ -103,6 +110,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StatusTabButton {
|
StatusTabButton {
|
||||||
|
readonly property int subSection: MembersTabPanel.TabType.BannedMembers
|
||||||
|
|
||||||
id: bannedBtn
|
id: bannedBtn
|
||||||
objectName: "bannedButton"
|
objectName: "bannedButton"
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
@ -111,79 +120,53 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StackLayout {
|
SearchBox {
|
||||||
id: stackLayout
|
id: memberSearch
|
||||||
Layout.fillWidth: true
|
Layout.preferredWidth: root.preferredContentWidth
|
||||||
|
placeholderText: qsTr("Search by name or chat key")
|
||||||
|
enabled: membersTabBar.currentItem.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
MembersTabPanel {
|
||||||
|
Layout.preferredWidth: root.preferredContentWidth
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
currentIndex: membersTabBar.currentIndex
|
|
||||||
|
|
||||||
MembersTabPanel {
|
panelType: membersTabBar.currentItem.subSection
|
||||||
model: root.membersModel
|
model: {
|
||||||
rootStore: root.rootStore
|
switch (panelType) {
|
||||||
utilsStore: root.utilsStore
|
case MembersTabPanel.TabType.PendingRequests:
|
||||||
memberRole: root.memberRole
|
return root.pendingMembersModel
|
||||||
panelType: MembersTabPanel.TabType.AllMembers
|
case MembersTabPanel.TabType.DeclinedRequests:
|
||||||
|
return root.declinedMembersModel
|
||||||
Layout.fillWidth: true
|
case MembersTabPanel.TabType.BannedMembers:
|
||||||
Layout.fillHeight: true
|
return root.bannedMembersModel
|
||||||
|
case MembersTabPanel.TabType.AllMembers:
|
||||||
onKickUserClicked: {
|
default:
|
||||||
kickBanPopup.mode = KickBanPopup.Mode.Kick
|
return root.membersModel
|
||||||
kickBanPopup.username = name
|
|
||||||
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 {
|
searchString: memberSearch.text
|
||||||
model: root.pendingMembersModel
|
rootStore: root.rootStore
|
||||||
rootStore: root.rootStore
|
utilsStore: root.utilsStore
|
||||||
utilsStore: root.utilsStore
|
memberRole: root.memberRole
|
||||||
memberRole: root.memberRole
|
|
||||||
panelType: MembersTabPanel.TabType.PendingRequests
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
onKickUserClicked: {
|
||||||
Layout.fillHeight: true
|
kickBanPopup.mode = KickBanPopup.Mode.Kick
|
||||||
|
kickBanPopup.username = name
|
||||||
onAcceptRequestToJoin: root.acceptRequestToJoin(id)
|
kickBanPopup.userId = id
|
||||||
onDeclineRequestToJoin: root.declineRequestToJoin(id)
|
kickBanPopup.open()
|
||||||
}
|
}
|
||||||
|
onBanUserClicked: {
|
||||||
MembersTabPanel {
|
kickBanPopup.mode = KickBanPopup.Mode.Ban
|
||||||
model: root.declinedMembersModel
|
kickBanPopup.username = name
|
||||||
rootStore: root.rootStore
|
kickBanPopup.userId = id
|
||||||
utilsStore: root.utilsStore
|
kickBanPopup.open()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
onUnbanUserClicked: root.unbanUserClicked(id)
|
||||||
|
onAcceptRequestToJoin: root.acceptRequestToJoin(id)
|
||||||
|
onDeclineRequestToJoin: root.declineRequestToJoin(id)
|
||||||
|
onViewMemberMessagesClicked: root.viewMemberMessagesClicked(pubKey, displayName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,25 +10,27 @@ import StatusQ.Controls 0.1
|
|||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
import StatusQ.Popups 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.chat 1.0
|
||||||
|
import shared.controls.delegates 1.0
|
||||||
import shared.stores 1.0 as SharedStores
|
import shared.stores 1.0 as SharedStores
|
||||||
import shared.views.chat 1.0
|
import shared.views.chat 1.0
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
import AppLayouts.Chat.stores 1.0
|
import AppLayouts.Chat.stores 1.0
|
||||||
import AppLayouts.Communities.layouts 1.0
|
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string placeholderText: qsTr("Search by member name or chat key")
|
required property var model
|
||||||
property var model
|
|
||||||
|
property string searchString
|
||||||
property RootStore rootStore
|
property RootStore rootStore
|
||||||
property SharedStores.UtilsStore utilsStore
|
property SharedStores.UtilsStore utilsStore
|
||||||
|
|
||||||
|
property int panelType: MembersTabPanel.TabType.AllMembers
|
||||||
property int memberRole: Constants.memberRole.none
|
property int memberRole: Constants.memberRole.none
|
||||||
|
|
||||||
readonly property bool isOwner: memberRole === Constants.memberRole.owner
|
readonly property bool isOwner: memberRole === Constants.memberRole.owner
|
||||||
@ -49,332 +51,279 @@ Item {
|
|||||||
DeclinedRequests
|
DeclinedRequests
|
||||||
}
|
}
|
||||||
|
|
||||||
property int panelType: MembersTabPanel.TabType.AllMembers
|
StatusListView {
|
||||||
|
objectName: "CommunityMembersTabPanel_MembersListViews"
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 30
|
|
||||||
|
|
||||||
SearchBox {
|
model: SortFilterProxyModel {
|
||||||
id: memberSearch
|
sourceModel: root.model
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.leftMargin: 12
|
sorters: [
|
||||||
placeholderText: root.placeholderText
|
StringSorter {
|
||||||
enabled: !!root.model && !root.model.ModelCount.empty
|
roleName: "preferredDisplayName"
|
||||||
|
caseSensitivity: Qt.CaseInsensitive
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
filters: [
|
||||||
|
UserSearchFilterContainer {
|
||||||
|
searchString: root.searchString
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusListView {
|
spacing: 0
|
||||||
id: membersList
|
|
||||||
objectName: "CommunityMembersTabPanel_MembersListViews"
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
delegate: ContactListItemDelegate {
|
||||||
Layout.fillHeight: true
|
id: memberItem
|
||||||
|
|
||||||
model: SortFilterProxyModel {
|
// Buttons visibility conditions:
|
||||||
id: filteredModel
|
// 1. Tab based buttons - only visible when the tab is selected
|
||||||
sourceModel: root.model
|
// a. All members tab
|
||||||
|
// - Kick; - Kick pending
|
||||||
|
// - Ban; - Ban pending
|
||||||
|
// b. Pending requests tab
|
||||||
|
// - Accept; - Accept pending
|
||||||
|
// - Reject; - Reject pending
|
||||||
|
// c. Rejected members tab
|
||||||
|
// - Accept; - Accept pending
|
||||||
|
// d. Banned members tab
|
||||||
|
// - Unban
|
||||||
|
// 2. Pending states - buttons in pending states are always visible in their specific tab. Other buttons are disabled if the request is in pending state
|
||||||
|
// - Accept button is visible when the user is hovered or when the request is in accepted pending state. This condition can be overriden by the ctaAllowed property
|
||||||
|
// - Reject button is visible when the user is hovered or when the request is in rejected pending state. This condition can be overriden by the ctaAllowed property
|
||||||
|
// - Kick and ban buttons are visible when the user is hovered or when the request is in kick or ban pending state. This condition can be overriden by the ctaAllowed property
|
||||||
|
// 3. Other conditions - buttons are visible when the user is hovered and is not himself or other privileged user
|
||||||
|
// 4. All members tab, member in AwaitingAddress state - buttons is not visible, sandwatch icon is shown
|
||||||
|
|
||||||
function searchPredicate(ensName, displayName, aliasName) {
|
/// Helpers ///
|
||||||
const lowerCaseSearchString = memberSearch.text.toLowerCase()
|
|
||||||
const secondaryName = ProfileUtils.displayName("", ensName, displayName, aliasName)
|
|
||||||
|
|
||||||
return secondaryName.toLowerCase().includes(lowerCaseSearchString)
|
// Tab based buttons
|
||||||
}
|
readonly property bool tabIsShowingKickBanButtons: root.panelType === MembersTabPanel.TabType.AllMembers
|
||||||
|
readonly property bool tabIsShowingUnbanButton: root.panelType === MembersTabPanel.TabType.BannedMembers
|
||||||
|
readonly property bool tabIsShowingRejectButton: root.panelType === MembersTabPanel.TabType.PendingRequests
|
||||||
|
readonly property bool tabIsShowingAcceptButton: root.panelType === MembersTabPanel.TabType.PendingRequests ||
|
||||||
|
root.panelType === MembersTabPanel.TabType.DeclinedRequests
|
||||||
|
readonly property bool tabIsShowingViewMessagesButton: model.membershipRequestState !== Constants.CommunityMembershipRequestState.BannedWithAllMessagesDelete &&
|
||||||
|
(root.panelType === MembersTabPanel.TabType.AllMembers ||
|
||||||
|
root.panelType === MembersTabPanel.TabType.BannedMembers)
|
||||||
|
|
||||||
sorters : [
|
|
||||||
StringSorter {
|
|
||||||
roleName: "preferredDisplayName"
|
|
||||||
caseSensitivity: Qt.CaseInsensitive
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
filters: AnyOf {
|
// Request states
|
||||||
enabled: memberSearch.text !== ""
|
readonly property bool isPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.Pending
|
||||||
// substring search for either nickname or the other primary/secondary display name
|
readonly property bool isAccepted: model.membershipRequestState === Constants.CommunityMembershipRequestState.Accepted
|
||||||
SearchFilter {
|
readonly property bool isRejected: model.membershipRequestState === Constants.CommunityMembershipRequestState.Rejected
|
||||||
roleName: "localNickname"
|
readonly property bool isRejectedPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.RejectedPending
|
||||||
searchPhrase: memberSearch.text
|
readonly property bool isAcceptedPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.AcceptedPending
|
||||||
}
|
readonly property bool isBanPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.BannedPending
|
||||||
FastExpressionFilter {
|
readonly property bool isUnbanPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.UnbannedPending
|
||||||
expression: {
|
readonly property bool isKickPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.KickedPending
|
||||||
memberSearch.text
|
readonly property bool isBanned: model.membershipRequestState === Constants.CommunityMembershipRequestState.Banned ||
|
||||||
return filteredModel.searchPredicate(model.ensName, model.displayName, model.alias)
|
model.membershipRequestState === Constants.CommunityMembershipRequestState.BannedWithAllMessagesDelete
|
||||||
}
|
readonly property bool isKicked: model.membershipRequestState === Constants.CommunityMembershipRequestState.Kicked
|
||||||
expectedRoles: ["ensName", "displayName", "alias"]
|
|
||||||
}
|
// TODO: Connect to backend when available
|
||||||
// exact search for the full key
|
// The admin that initited the pending state can change the state. Actions are not visible for other admins
|
||||||
ValueFilter {
|
readonly property bool ctaAllowed: !isRejectedPending && !isAcceptedPending && !isBanPending && !isUnbanPending && !isKickPending
|
||||||
roleName: "compressedPubKey"
|
|
||||||
value: memberSearch.text
|
readonly property bool canBeBanned: {
|
||||||
}
|
if (model.isCurrentUser)
|
||||||
|
return false
|
||||||
|
|
||||||
|
switch (model.memberRole) {
|
||||||
|
// Owner can't be banned
|
||||||
|
case Constants.memberRole.owner: return false
|
||||||
|
// TokenMaster can only be banned by owner
|
||||||
|
case Constants.memberRole.tokenMaster: return root.isOwner
|
||||||
|
// Admin can only be banned by owner and tokenMaster
|
||||||
|
case Constants.memberRole.admin: return root.isOwner || root.isTokenMaster
|
||||||
|
// All normal members can be banned by all privileged users
|
||||||
|
default: return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spacing: 0
|
readonly property bool showOnHover: hovered && ctaAllowed
|
||||||
|
readonly property bool canDeleteMessages: model.isCurrentUser || model.memberRole !== Constants.memberRole.owner
|
||||||
|
|
||||||
delegate: StatusMemberListItem {
|
/// Button visibility ///
|
||||||
id: memberItem
|
readonly property bool acceptButtonVisible: tabIsShowingAcceptButton && (isPending || isRejected || isRejectedPending || isAcceptedPending) && showOnHover
|
||||||
|
readonly property bool rejectButtonVisible: tabIsShowingRejectButton && (isPending || isRejectedPending || isAcceptedPending) && showOnHover
|
||||||
|
readonly property bool acceptPendingButtonVisible: tabIsShowingAcceptButton && isAcceptedPending
|
||||||
|
readonly property bool rejectPendingButtonVisible: tabIsShowingRejectButton && isRejectedPending
|
||||||
|
readonly property bool kickButtonVisible: tabIsShowingKickBanButtons && isAccepted && showOnHover && canBeBanned
|
||||||
|
readonly property bool banButtonVisible: tabIsShowingKickBanButtons && isAccepted && showOnHover && canBeBanned
|
||||||
|
readonly property bool kickPendingButtonVisible: tabIsShowingKickBanButtons && isKickPending
|
||||||
|
readonly property bool banPendingButtonVisible: tabIsShowingKickBanButtons && isBanPending
|
||||||
|
readonly property bool unbanButtonVisible: tabIsShowingUnbanButton && isBanned && showOnHover
|
||||||
|
readonly property bool viewMessagesButtonVisible: tabIsShowingViewMessagesButton && showOnHover
|
||||||
|
readonly property bool messagesDeletedTextVisible: showOnHover &&
|
||||||
|
model.membershipRequestState === Constants.CommunityMembershipRequestState.BannedWithAllMessagesDelete
|
||||||
|
|
||||||
// Buttons visibility conditions:
|
/// Pending states ///
|
||||||
// 1. Tab based buttons - only visible when the tab is selected
|
readonly property bool isPendingState: isAcceptedPending || isRejectedPending || isBanPending || isUnbanPending || isKickPending
|
||||||
// a. All members tab
|
readonly property string pendingStateText: isAcceptedPending ? qsTr("Accept pending...") :
|
||||||
// - Kick; - Kick pending
|
isRejectedPending ? qsTr("Reject pending...") :
|
||||||
// - Ban; - Ban pending
|
isBanPending ? qsTr("Ban pending...") :
|
||||||
// b. Pending requests tab
|
isUnbanPending ? qsTr("Unban pending...") :
|
||||||
// - Accept; - Accept pending
|
isKickPending ? qsTr("Kick pending...") : ""
|
||||||
// - Reject; - Reject pending
|
|
||||||
// c. Rejected members tab
|
|
||||||
// - Accept; - Accept pending
|
|
||||||
// d. Banned members tab
|
|
||||||
// - Unban
|
|
||||||
// 2. Pending states - buttons in pending states are always visible in their specific tab. Other buttons are disabled if the request is in pending state
|
|
||||||
// - Accept button is visible when the user is hovered or when the request is in accepted pending state. This condition can be overriden by the ctaAllowed property
|
|
||||||
// - Reject button is visible when the user is hovered or when the request is in rejected pending state. This condition can be overriden by the ctaAllowed property
|
|
||||||
// - Kick and ban buttons are visible when the user is hovered or when the request is in kick or ban pending state. This condition can be overriden by the ctaAllowed property
|
|
||||||
// 3. Other conditions - buttons are visible when the user is hovered and is not himself or other privileged user
|
|
||||||
// 4. All members tab, member in AwaitingAddress state - buttons is not visible, sandwatch icon is shown
|
|
||||||
|
|
||||||
/// Helpers ///
|
isAwaitingAddress: model.membershipRequestState === Constants.CommunityMembershipRequestState.AwaitingAddress
|
||||||
|
|
||||||
// Tab based buttons
|
components: [
|
||||||
readonly property bool tabIsShowingKickBanButtons: root.panelType === MembersTabPanel.TabType.AllMembers
|
StatusBaseText {
|
||||||
readonly property bool tabIsShowingUnbanButton: root.panelType === MembersTabPanel.TabType.BannedMembers
|
id: pendingText
|
||||||
readonly property bool tabIsShowingRejectButton: root.panelType === MembersTabPanel.TabType.PendingRequests
|
width: Math.max(implicitWidth, d.pendingTextMaxWidth)
|
||||||
readonly property bool tabIsShowingAcceptButton: root.panelType === MembersTabPanel.TabType.PendingRequests ||
|
onImplicitWidthChanged: {
|
||||||
root.panelType === MembersTabPanel.TabType.DeclinedRequests
|
d.pendingTextMaxWidth = Math.max(implicitWidth, d.pendingTextMaxWidth)
|
||||||
readonly property bool tabIsShowingViewMessagesButton: model.membershipRequestState !== Constants.CommunityMembershipRequestState.BannedWithAllMessagesDelete &&
|
|
||||||
(root.panelType === MembersTabPanel.TabType.AllMembers ||
|
|
||||||
root.panelType === MembersTabPanel.TabType.BannedMembers)
|
|
||||||
|
|
||||||
|
|
||||||
// Request states
|
|
||||||
readonly property bool isPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.Pending
|
|
||||||
readonly property bool isAccepted: model.membershipRequestState === Constants.CommunityMembershipRequestState.Accepted
|
|
||||||
readonly property bool isRejected: model.membershipRequestState === Constants.CommunityMembershipRequestState.Rejected
|
|
||||||
readonly property bool isRejectedPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.RejectedPending
|
|
||||||
readonly property bool isAcceptedPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.AcceptedPending
|
|
||||||
readonly property bool isBanPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.BannedPending
|
|
||||||
readonly property bool isUnbanPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.UnbannedPending
|
|
||||||
readonly property bool isKickPending: model.membershipRequestState === Constants.CommunityMembershipRequestState.KickedPending
|
|
||||||
readonly property bool isBanned: model.membershipRequestState === Constants.CommunityMembershipRequestState.Banned ||
|
|
||||||
model.membershipRequestState === Constants.CommunityMembershipRequestState.BannedWithAllMessagesDelete
|
|
||||||
readonly property bool isKicked: model.membershipRequestState === Constants.CommunityMembershipRequestState.Kicked
|
|
||||||
|
|
||||||
// TODO: Connect to backend when available
|
|
||||||
// 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
|
|
||||||
|
|
||||||
switch (model.memberRole) {
|
|
||||||
// Owner can't be banned
|
|
||||||
case Constants.memberRole.owner: return false
|
|
||||||
// TokenMaster can only be banned by owner
|
|
||||||
case Constants.memberRole.tokenMaster: return root.isOwner
|
|
||||||
// Admin can only be banned by owner and tokenMaster
|
|
||||||
case Constants.memberRole.admin: return root.isOwner || root.isTokenMaster
|
|
||||||
// All normal members can be banned by all privileged users
|
|
||||||
default: return true
|
|
||||||
}
|
}
|
||||||
|
visible: !!text && isPendingState
|
||||||
|
rightPadding: isKickPending || isBanPending || isUnbanPending ? 0 : Theme.bigPadding
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: pendingStateText
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
StatusToolTip {
|
||||||
|
text: qsTr("Waiting for owner node to come online")
|
||||||
|
visible: hoverHandler.hovered
|
||||||
|
}
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
enabled: pendingText.visible
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
text: qsTr("Messages deleted")
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: messagesDeletedTextVisible
|
||||||
|
},
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
id: viewMessages
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
objectName: "MemberListItem_ViewMessages"
|
||||||
|
text: qsTr("View Messages")
|
||||||
|
visible: viewMessagesButtonVisible
|
||||||
|
size: StatusBaseButton.Size.Small
|
||||||
|
onClicked: root.viewMemberMessagesClicked(model.pubKey, memberItem.title)
|
||||||
|
},
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
objectName: "MemberListItem_KickButton"
|
||||||
|
text: qsTr("Kick")
|
||||||
|
visible: kickButtonVisible
|
||||||
|
type: StatusBaseButton.Type.Danger
|
||||||
|
size: StatusBaseButton.Size.Small
|
||||||
|
onClicked: root.kickUserClicked(model.pubKey, memberItem.title)
|
||||||
|
},
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
objectName: "MemberListItem_BanButton"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: banButtonVisible
|
||||||
|
text: qsTr("Ban")
|
||||||
|
type: StatusBaseButton.Type.Danger
|
||||||
|
size: StatusBaseButton.Size.Small
|
||||||
|
onClicked: root.banUserClicked(model.pubKey, memberItem.title)
|
||||||
|
},
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
objectName: "MemberListItem_UnbanButton"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: unbanButtonVisible
|
||||||
|
text: qsTr("Unban")
|
||||||
|
type: StatusBaseButton.Type.Danger
|
||||||
|
size: StatusBaseButton.Size.Small
|
||||||
|
onClicked: root.unbanUserClicked(model.pubKey)
|
||||||
|
},
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
id: acceptButton
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
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
|
||||||
|
enabled: !acceptPendingButtonVisible
|
||||||
|
onClicked: root.acceptRequestToJoin(model.requestToJoinId)
|
||||||
|
},
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
id: rejectButton
|
||||||
|
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
|
||||||
|
onClicked: root.declineRequestToJoin(model.requestToJoinId)
|
||||||
}
|
}
|
||||||
readonly property bool showOnHover: isHovered && ctaAllowed
|
]
|
||||||
readonly property bool canDeleteMessages: model.isCurrentUser || model.memberRole !== Constants.memberRole.owner
|
|
||||||
|
|
||||||
/// Button visibility ///
|
readonly property string title: model.preferredDisplayName
|
||||||
readonly property bool acceptButtonVisible: tabIsShowingAcceptButton && (isPending || isRejected || isRejectedPending || isAcceptedPending) && showOnHover
|
|
||||||
readonly property bool rejectButtonVisible: tabIsShowingRejectButton && (isPending || isRejectedPending || isAcceptedPending) && showOnHover
|
|
||||||
readonly property bool acceptPendingButtonVisible: tabIsShowingAcceptButton && isAcceptedPending
|
|
||||||
readonly property bool rejectPendingButtonVisible: tabIsShowingRejectButton && isRejectedPending
|
|
||||||
readonly property bool kickButtonVisible: tabIsShowingKickBanButtons && isAccepted && showOnHover && canBeBanned
|
|
||||||
readonly property bool banButtonVisible: tabIsShowingKickBanButtons && isAccepted && showOnHover && canBeBanned
|
|
||||||
readonly property bool kickPendingButtonVisible: tabIsShowingKickBanButtons && isKickPending
|
|
||||||
readonly property bool banPendingButtonVisible: tabIsShowingKickBanButtons && isBanPending
|
|
||||||
readonly property bool unbanButtonVisible: tabIsShowingUnbanButton && isBanned && showOnHover
|
|
||||||
readonly property bool viewMessagesButtonVisible: tabIsShowingViewMessagesButton && showOnHover
|
|
||||||
readonly property bool messagesDeletedTextVisible: showOnHover &&
|
|
||||||
model.membershipRequestState === Constants.CommunityMembershipRequestState.BannedWithAllMessagesDelete
|
|
||||||
|
|
||||||
/// Pending states ///
|
width: ListView.view.width
|
||||||
readonly property bool isPendingState: isAcceptedPending || isRejectedPending || isBanPending || isUnbanPending || isKickPending
|
|
||||||
readonly property string pendingStateText: isAcceptedPending ? qsTr("Accept pending...") :
|
|
||||||
isRejectedPending ? qsTr("Reject pending...") :
|
|
||||||
isBanPending ? qsTr("Ban pending...") :
|
|
||||||
isUnbanPending ? qsTr("Unban pending...") :
|
|
||||||
isKickPending ? qsTr("Kick pending...") : ""
|
|
||||||
|
|
||||||
isAwaitingAddress: model.membershipRequestState === Constants.CommunityMembershipRequestState.AwaitingAddress
|
icon.width: 40
|
||||||
|
icon.height: 40
|
||||||
|
|
||||||
rightPadding: 75
|
onClicked: {
|
||||||
leftPadding: 12
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
const profileType = Utils.getProfileType(model.isCurrentUser, false, model.isBlocked)
|
||||||
|
const contactType = Utils.getContactType(model.contactRequest, model.isContact)
|
||||||
|
|
||||||
components: [
|
const params = {
|
||||||
StatusBaseText {
|
profileType, contactType,
|
||||||
id: pendingText
|
pubKey: model.pubKey,
|
||||||
width: Math.max(implicitWidth, d.pendingTextMaxWidth)
|
compressedPubKey: model.compressedPubKey,
|
||||||
onImplicitWidthChanged: {
|
emojiHash: root.utilsStore.getEmojiHash(model.pubKey),
|
||||||
d.pendingTextMaxWidth = Math.max(implicitWidth, d.pendingTextMaxWidth)
|
colorHash: model.colorHash,
|
||||||
}
|
colorId: model.colorId,
|
||||||
visible: !!text && isPendingState
|
displayName: memberItem.title || model.displayName,
|
||||||
rightPadding: isKickPending || isBanPending || isUnbanPending ? 0 : Theme.bigPadding
|
userIcon: model.icon,
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
trustStatus: model.trustStatus,
|
||||||
text: pendingStateText
|
onlineStatus: model.onlineStatus,
|
||||||
color: Theme.palette.baseColor1
|
ensVerified: model.isEnsVerified,
|
||||||
StatusToolTip {
|
hasLocalNickname: !!model.localNickname
|
||||||
text: qsTr("Waiting for owner node to come online")
|
|
||||||
visible: hoverHandler.hovered
|
|
||||||
}
|
|
||||||
HoverHandler {
|
|
||||||
id: hoverHandler
|
|
||||||
enabled: pendingText.visible
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
StatusBaseText {
|
|
||||||
text: qsTr("Messages deleted")
|
|
||||||
color: Theme.palette.baseColor1
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: messagesDeletedTextVisible
|
|
||||||
},
|
|
||||||
|
|
||||||
StatusButton {
|
|
||||||
id: viewMessages
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
objectName: "MemberListItem_ViewMessages"
|
|
||||||
text: qsTr("View Messages")
|
|
||||||
visible: viewMessagesButtonVisible
|
|
||||||
size: StatusBaseButton.Size.Small
|
|
||||||
onClicked: root.viewMemberMessagesClicked(model.pubKey, memberItem.title)
|
|
||||||
},
|
|
||||||
|
|
||||||
StatusButton {
|
|
||||||
id: kickButton
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
objectName: "MemberListItem_KickButton"
|
|
||||||
text: qsTr("Kick")
|
|
||||||
visible: kickButtonVisible
|
|
||||||
type: StatusBaseButton.Type.Danger
|
|
||||||
size: StatusBaseButton.Size.Small
|
|
||||||
onClicked: root.kickUserClicked(model.pubKey, memberItem.title)
|
|
||||||
},
|
|
||||||
|
|
||||||
StatusButton {
|
|
||||||
id: banButton
|
|
||||||
objectName: "MemberListItem_BanButton"
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: banButtonVisible
|
|
||||||
text: qsTr("Ban")
|
|
||||||
type: StatusBaseButton.Type.Danger
|
|
||||||
size: StatusBaseButton.Size.Small
|
|
||||||
onClicked: root.banUserClicked(model.pubKey, memberItem.title)
|
|
||||||
},
|
|
||||||
|
|
||||||
StatusButton {
|
|
||||||
objectName: "MemberListItem_UnbanButton"
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: unbanButtonVisible
|
|
||||||
text: qsTr("Unban")
|
|
||||||
type: StatusBaseButton.Type.Danger
|
|
||||||
size: StatusBaseButton.Size.Small
|
|
||||||
onClicked: root.unbanUserClicked(model.pubKey)
|
|
||||||
},
|
|
||||||
|
|
||||||
StatusButton {
|
|
||||||
id: acceptButton
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
opacity: acceptButtonVisible
|
|
||||||
text: qsTr("Accept")
|
|
||||||
type: StatusBaseButton.Type.Success
|
|
||||||
icon.name: "checkmark-circle"
|
|
||||||
icon.color: enabled ? Theme.palette.successColor1 : disabledTextColor
|
|
||||||
loading: model.requestToJoinLoading
|
|
||||||
enabled: !acceptPendingButtonVisible
|
|
||||||
onClicked: root.acceptRequestToJoin(model.requestToJoinId)
|
|
||||||
},
|
|
||||||
|
|
||||||
StatusButton {
|
|
||||||
id: rejectButton
|
|
||||||
opacity: rejectButtonVisible
|
|
||||||
text: qsTr("Reject")
|
|
||||||
type: StatusBaseButton.Type.Danger
|
|
||||||
icon.name: "close-circle"
|
|
||||||
icon.color: enabled ? Theme.palette.dangerColor1 : disabledTextColor
|
|
||||||
enabled: !rejectPendingButtonVisible
|
|
||||||
onClicked: root.declineRequestToJoin(model.requestToJoinId)
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
|
|
||||||
readonly property string title: model.preferredDisplayName
|
memberContextMenuComponent.createObject(root, params).popup(this)
|
||||||
|
} else if (mouse.button === Qt.LeftButton) {
|
||||||
width: membersList.width
|
Global.openProfilePopup(model.pubKey)
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
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) {
|
|
||||||
const profileType = Utils.getProfileType(model.isCurrentUser, false, model.isBlocked)
|
|
||||||
const contactType = Utils.getContactType(model.contactRequest, model.isContact)
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
profileType, contactType,
|
|
||||||
pubKey: model.pubKey,
|
|
||||||
compressedPubKey: model.compressedPubKey,
|
|
||||||
emojiHash: root.utilsStore.getEmojiHash(model.pubKey),
|
|
||||||
colorHash: model.colorHash,
|
|
||||||
colorId: model.colorId,
|
|
||||||
displayName: memberItem.title || model.displayName,
|
|
||||||
userIcon: model.icon,
|
|
||||||
trustStatus: model.trustStatus,
|
|
||||||
onlineStatus: model.onlineStatus,
|
|
||||||
ensVerified: model.isEnsVerified,
|
|
||||||
hasLocalNickname: !!model.localNickname
|
|
||||||
}
|
|
||||||
|
|
||||||
Global.openMenu(memberContextMenuComponent, this, params)
|
|
||||||
} else if (mouse.button === Qt.LeftButton) {
|
|
||||||
Global.openProfilePopup(model.pubKey)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: memberContextMenuComponent
|
id: memberContextMenuComponent
|
||||||
|
|
||||||
ProfileContextMenu {
|
ProfileContextMenu {
|
||||||
id: memberContextMenuView
|
id: memberContextMenuView
|
||||||
|
|
||||||
required property string pubKey
|
required property string pubKey
|
||||||
|
|
||||||
onOpenProfileClicked: Global.openProfilePopup(pubKey, null)
|
onOpenProfileClicked: Global.openProfilePopup(pubKey, null)
|
||||||
onCreateOneToOneChat: {
|
onCreateOneToOneChat: {
|
||||||
Global.changeAppSectionBySectionType(Constants.appSection.chat)
|
Global.changeAppSectionBySectionType(Constants.appSection.chat)
|
||||||
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", pubKey, "")
|
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", pubKey, "")
|
||||||
|
}
|
||||||
|
onReviewContactRequest: Global.openReviewContactRequestPopup(pubKey, null)
|
||||||
|
onSendContactRequest: Global.openContactRequestPopup(pubKey, null)
|
||||||
|
onEditNickname: Global.openNicknamePopupRequested(pubKey, null)
|
||||||
|
onRemoveNickname: root.rootStore.contactsStore.changeContactNickname(pubKey, "", displayName, true)
|
||||||
|
onUnblockContact: Global.unblockContactRequested(pubKey)
|
||||||
|
onMarkAsUntrusted: Global.markAsUntrustedRequested(pubKey)
|
||||||
|
onRemoveTrustStatus: root.rootStore.contactsStore.removeTrustStatus(pubKey)
|
||||||
|
onRemoveContact: Global.removeContactRequested(pubKey)
|
||||||
|
onBlockContact: Global.blockContactRequested(pubKey)
|
||||||
|
onMarkAsTrusted: Global.openMarkAsIDVerifiedPopup(pubKey, null)
|
||||||
|
onRemoveTrustedMark: Global.openRemoveIDVerificationDialog(pubKey, null)
|
||||||
|
onClosed: destroy()
|
||||||
}
|
}
|
||||||
onReviewContactRequest: Global.openReviewContactRequestPopup(pubKey, null)
|
|
||||||
onSendContactRequest: Global.openContactRequestPopup(pubKey, null)
|
|
||||||
onEditNickname: Global.openNicknamePopupRequested(pubKey, null)
|
|
||||||
onRemoveNickname: root.rootStore.contactsStore.changeContactNickname(pubKey, "", displayName, true)
|
|
||||||
onUnblockContact: Global.unblockContactRequested(pubKey)
|
|
||||||
onMarkAsUntrusted: Global.markAsUntrustedRequested(pubKey)
|
|
||||||
onRemoveTrustStatus: root.rootStore.contactsStore.removeTrustStatus(pubKey)
|
|
||||||
onRemoveContact: Global.removeContactRequested(pubKey)
|
|
||||||
onBlockContact: Global.blockContactRequested(pubKey)
|
|
||||||
onMarkAsTrusted: Global.openMarkAsIDVerifiedPopup(pubKey, null)
|
|
||||||
onRemoveTrustedMark: Global.openRemoveIDVerificationDialog(pubKey, null)
|
|
||||||
onClosed: destroy()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,5 +333,6 @@ Item {
|
|||||||
// so that the text aligned on all rows (the text might be different on each row)
|
// so that the text aligned on all rows (the text might be different on each row)
|
||||||
property real pendingTextMaxWidth: 0
|
property real pendingTextMaxWidth: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
onPanelTypeChanged: { d.pendingTextMaxWidth = 0 }
|
onPanelTypeChanged: { d.pendingTextMaxWidth = 0 }
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import QtQuick 2.15
|
|||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import QtQml.Models 2.15
|
import QtQml.Models 2.15
|
||||||
import QtGraphicalEffects 1.15
|
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
|
@ -25,46 +25,32 @@ StatusDialog {
|
|||||||
Kick, Ban
|
Kick, Ban
|
||||||
}
|
}
|
||||||
|
|
||||||
width: 400
|
width: 480
|
||||||
|
|
||||||
title: root.mode === KickBanPopup.Mode.Kick
|
title: root.mode === KickBanPopup.Mode.Kick
|
||||||
? qsTr("Kick %1").arg(root.username)
|
? qsTr("Kick %1").arg(root.username)
|
||||||
: qsTr("Ban %1").arg(root.username)
|
: qsTr("Ban %1").arg(root.username)
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
font.pixelSize: Theme.primaryTextFontSize
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
text: root.mode === KickBanPopup.Mode.Kick
|
text: root.mode === KickBanPopup.Mode.Kick
|
||||||
? qsTr("Are you sure you want to kick <b>%1</b> from %2?")
|
? qsTr("Are you sure you want to kick <b>%1</b> from %2?").arg(root.username).arg(root.communityName)
|
||||||
.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)
|
||||||
: 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 {
|
StatusSwitch {
|
||||||
visible: root.mode === KickBanPopup.Mode.Ban
|
Layout.fillWidth: true
|
||||||
|
id: deleteAllMessagesSwitch
|
||||||
StatusBaseText {
|
visible: root.mode === KickBanPopup.Mode.Ban
|
||||||
Layout.fillWidth: true
|
leftSide: false
|
||||||
|
text: qsTr("Delete all messages posted by the user")
|
||||||
text: qsTr("Delete all messages posted by the user")
|
|
||||||
font.pixelSize: Theme.primaryTextFontSize
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusSwitch {
|
|
||||||
id: deleteAllMessagesSwitch
|
|
||||||
|
|
||||||
checked: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
footer: StatusDialogFooter {
|
footer: StatusDialogFooter {
|
||||||
rightButtons: ObjectModel {
|
rightButtons: ObjectModel {
|
||||||
@ -74,8 +60,6 @@ StatusDialog {
|
|||||||
onClicked: root.close()
|
onClicked: root.close()
|
||||||
}
|
}
|
||||||
StatusButton {
|
StatusButton {
|
||||||
id: banButton
|
|
||||||
|
|
||||||
objectName: root.mode === KickBanPopup.Mode.Kick
|
objectName: root.mode === KickBanPopup.Mode.Kick
|
||||||
? "CommunityMembers_KickModal_KickButton"
|
? "CommunityMembers_KickModal_KickButton"
|
||||||
: "CommunityMembers_BanModal_BanButton"
|
: "CommunityMembers_BanModal_BanButton"
|
||||||
|
@ -55,8 +55,8 @@ StatusSectionLayout {
|
|||||||
|
|
||||||
property var mutualContactsModel
|
property var mutualContactsModel
|
||||||
property var blockedContactsModel
|
property var blockedContactsModel
|
||||||
property var pendingReceivedRequestContactsModel
|
property var pendingContactsModel
|
||||||
property var pendingSentRequestContactsModel
|
property int pendingReceivedContactsCount
|
||||||
|
|
||||||
required property bool isCentralizedMetricsEnabled
|
required property bool isCentralizedMetricsEnabled
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ StatusSectionLayout {
|
|||||||
|
|
||||||
syncingBadgeCount: root.store.devicesStore.devicesModel.count -
|
syncingBadgeCount: root.store.devicesStore.devicesModel.count -
|
||||||
root.store.devicesStore.devicesModel.pairedCount
|
root.store.devicesStore.devicesModel.pairedCount
|
||||||
messagingBadgeCount: root.pendingReceivedRequestContactsModel.ModelCount.count
|
messagingBadgeCount: root.pendingReceivedContactsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
headerBackground: AccountHeaderGradient {
|
headerBackground: AccountHeaderGradient {
|
||||||
@ -244,8 +244,8 @@ StatusSectionLayout {
|
|||||||
|
|
||||||
mutualContactsModel: root.mutualContactsModel
|
mutualContactsModel: root.mutualContactsModel
|
||||||
blockedContactsModel: root.blockedContactsModel
|
blockedContactsModel: root.blockedContactsModel
|
||||||
pendingReceivedRequestContactsModel: root.pendingReceivedRequestContactsModel
|
pendingContactsModel: root.pendingContactsModel
|
||||||
pendingSentRequestContactsModel: root.pendingSentRequestContactsModel
|
pendingReceivedContactsCount: root.pendingReceivedContactsCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +280,7 @@ StatusSectionLayout {
|
|||||||
contentWidth: d.contentWidth
|
contentWidth: d.contentWidth
|
||||||
|
|
||||||
sectionTitle: settingsEntriesModel.getNameForSubsection(Constants.settingsSubsection.messaging)
|
sectionTitle: settingsEntriesModel.getNameForSubsection(Constants.settingsSubsection.messaging)
|
||||||
requestsCount: root.pendingReceivedRequestContactsModel.ModelCount.count
|
requestsCount: root.pendingReceivedContactsCount
|
||||||
messagingStore: root.store.messagingStore
|
messagingStore: root.store.messagingStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,26 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
import StatusQ.Components 0.1
|
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
import utils 1.0
|
import shared.controls.delegates 1.0
|
||||||
|
|
||||||
StatusListItem {
|
ContactListItemDelegate {
|
||||||
id: root
|
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 showSendMessageButton: false
|
||||||
property bool showRejectContactRequestButton: false
|
property bool showRejectContactRequestButton: false
|
||||||
property bool showAcceptContactRequestButton: false
|
property bool showAcceptContactRequestButton: false
|
||||||
property bool showRemoveRejectionButton: false
|
|
||||||
property string contactText: ""
|
property string contactText: ""
|
||||||
|
|
||||||
signal contextMenuRequested
|
signal contextMenuRequested
|
||||||
signal sendMessageRequested
|
signal sendMessageRequested
|
||||||
signal showVerificationRequestRequested
|
|
||||||
signal acceptContactRequested
|
signal acceptContactRequested
|
||||||
signal rejectRequestRequested
|
signal rejectRequestRequested
|
||||||
signal removeRejectionRequested
|
|
||||||
|
|
||||||
asset.width: 40
|
icon.width: 40
|
||||||
asset.height: 40
|
icon.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)
|
|
||||||
}
|
|
||||||
|
|
||||||
components: [
|
components: [
|
||||||
StatusFlatRoundButton {
|
StatusFlatRoundButton {
|
||||||
@ -53,6 +30,7 @@ StatusListItem {
|
|||||||
height: visible ? 32 : 0
|
height: visible ? 32 : 0
|
||||||
icon.name: "chat"
|
icon.name: "chat"
|
||||||
icon.color: Theme.palette.directColor1
|
icon.color: Theme.palette.directColor1
|
||||||
|
tooltip.text: qsTr("Send message")
|
||||||
onClicked: root.sendMessageRequested()
|
onClicked: root.sendMessageRequested()
|
||||||
},
|
},
|
||||||
StatusFlatRoundButton {
|
StatusFlatRoundButton {
|
||||||
@ -62,6 +40,7 @@ StatusListItem {
|
|||||||
height: visible ? 32 : 0
|
height: visible ? 32 : 0
|
||||||
icon.name: "close-circle"
|
icon.name: "close-circle"
|
||||||
icon.color: Theme.palette.dangerColor1
|
icon.color: Theme.palette.dangerColor1
|
||||||
|
tooltip.text: qsTr("Reject")
|
||||||
onClicked: root.rejectRequestRequested()
|
onClicked: root.rejectRequestRequested()
|
||||||
},
|
},
|
||||||
StatusFlatRoundButton {
|
StatusFlatRoundButton {
|
||||||
@ -71,26 +50,16 @@ StatusListItem {
|
|||||||
height: visible ? 32 : 0
|
height: visible ? 32 : 0
|
||||||
icon.name: "checkmark-circle"
|
icon.name: "checkmark-circle"
|
||||||
icon.color: Theme.palette.successColor1
|
icon.color: Theme.palette.successColor1
|
||||||
|
tooltip.text: qsTr("Accept")
|
||||||
onClicked: root.acceptContactRequested()
|
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 {
|
StatusBaseText {
|
||||||
text: root.contactText
|
text: root.contactText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
},
|
},
|
||||||
StatusFlatRoundButton {
|
StatusFlatRoundButton {
|
||||||
objectName: "moreBtn"
|
objectName: "moreBtn"
|
||||||
id: menuButton
|
|
||||||
width: 32
|
width: 32
|
||||||
height: 32
|
height: 32
|
||||||
icon.name: "more"
|
icon.name: "more"
|
||||||
|
@ -1,152 +1,71 @@
|
|||||||
import QtQuick 2.15
|
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 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
|
||||||
|
|
||||||
import shared 1.0
|
import shared 1.0
|
||||||
import shared.panels 1.0
|
|
||||||
import shared.popups 1.0
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
Item {
|
StatusListView {
|
||||||
id: root
|
id: root
|
||||||
implicitHeight: (title.height + contactsList.height)
|
|
||||||
|
|
||||||
property var contactsModel
|
|
||||||
|
|
||||||
|
required property var contactsModel
|
||||||
property int panelUsage: Constants.contactsPanelUsage.unknownPosition
|
property int panelUsage: Constants.contactsPanelUsage.unknownPosition
|
||||||
|
|
||||||
property string title: ""
|
|
||||||
property string searchString: ""
|
property string searchString: ""
|
||||||
readonly property int count: contactsList.count
|
|
||||||
|
|
||||||
signal openContactContextMenu(string publicKey)
|
signal openContactContextMenu(string publicKey)
|
||||||
signal sendMessageActionTriggered(string publicKey)
|
signal sendMessageActionTriggered(string publicKey)
|
||||||
signal showVerificationRequest(string publicKey)
|
|
||||||
signal contactRequestAccepted(string publicKey)
|
signal contactRequestAccepted(string publicKey)
|
||||||
signal contactRequestRejected(string publicKey)
|
signal contactRequestRejected(string publicKey)
|
||||||
signal rejectionRemoved(string publicKey)
|
|
||||||
|
|
||||||
StyledText {
|
objectName: "ContactListPanel_ListView"
|
||||||
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 {
|
model: SortFilterProxyModel {
|
||||||
id: contactsList
|
id: filteredModel
|
||||||
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
|
sourceModel: root.contactsModel
|
||||||
|
|
||||||
function panelUsagePredicate(isVerified) {
|
filters: [
|
||||||
if (panelUsage === Constants.contactsPanelUsage.verifiedMutualContacts)
|
UserSearchFilterContainer {
|
||||||
return isVerified
|
searchString: root.searchString
|
||||||
if (panelUsage === Constants.contactsPanelUsage.mutualContacts)
|
|
||||||
return !isVerified
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
|
||||||
function searchPredicate(name, pubkey, compressedPubKey) {
|
sorters: [
|
||||||
const lowerCaseSearchString = root.searchString.toLowerCase()
|
FilterSorter { // Trusted contacts first
|
||||||
|
enabled: root.panelUsage === Constants.contactsPanelUsage.mutualContacts
|
||||||
return name.toLowerCase().includes(lowerCaseSearchString) ||
|
ValueFilter { roleName: "isVerified"; value: true }
|
||||||
pubkey.toLowerCase().includes(lowerCaseSearchString) ||
|
},
|
||||||
compressedPubKey.toLowerCase().includes(lowerCaseSearchString)
|
FilterSorter { // Received CRs first
|
||||||
}
|
id: pendingFilter
|
||||||
|
readonly property int received: Constants.ContactRequestState.Received
|
||||||
filters: [
|
enabled: root.panelUsage === Constants.contactsPanelUsage.pendingContacts
|
||||||
FastExpressionFilter {
|
ValueFilter { roleName: "contactRequest"; value: pendingFilter.received }
|
||||||
expression: filteredModel.panelUsagePredicate(model.isVerified)
|
},
|
||||||
expectedRoles: ["isVerified"]
|
StringSorter {
|
||||||
},
|
|
||||||
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"
|
roleName: "preferredDisplayName"
|
||||||
caseSensitivity: Qt.CaseInsensitive
|
caseSensitivity: Qt.CaseInsensitive
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
|
}
|
||||||
|
|
||||||
delegate: ContactPanel {
|
delegate: ContactPanel {
|
||||||
id: panelDelegate
|
width: ListView.view.width
|
||||||
|
|
||||||
width: ListView.view.width
|
showSendMessageButton: model.isContact && !model.isBlocked
|
||||||
name: model.preferredDisplayName
|
showRejectContactRequestButton: root.panelUsage === Constants.contactsPanelUsage.pendingContacts &&
|
||||||
iconSource: model.thumbnailImage
|
model.contactRequest === Constants.ContactRequestState.Received
|
||||||
|
showAcceptContactRequestButton: showRejectContactRequestButton
|
||||||
|
|
||||||
subTitle: model.ensVerified ? "" : Utils.getElidedCompressedPk(model.pubKey)
|
contactText: root.panelUsage === Constants.contactsPanelUsage.pendingContacts &&
|
||||||
pubKeyColor: Utils.colorForPubkey(model.pubKey)
|
model.contactRequest === Constants.ContactRequestState.Sent ? qsTr("Contact Request Sent")
|
||||||
colorHash: Utils.getColorHashAsJson(model.pubKey, model.ensVerified)
|
: ""
|
||||||
|
|
||||||
showSendMessageButton: model.isContact && !model.isBlocked
|
onClicked: Global.openProfilePopup(model.pubKey)
|
||||||
showRejectContactRequestButton: {
|
onContextMenuRequested: root.openContactContextMenu(model.pubKey)
|
||||||
if (root.panelUsage === Constants.contactsPanelUsage.receivedContactRequest
|
onSendMessageRequested: root.sendMessageActionTriggered(model.pubKey)
|
||||||
&& !model.verificationRequestStatus)
|
onAcceptContactRequested: root.contactRequestAccepted(model.pubKey)
|
||||||
return true
|
onRejectRequestRequested: root.contactRequestRejected(model.pubKey)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
ContactPanel 1.0 ContactPanel.qml
|
ContactPanel 1.0 ContactPanel.qml
|
||||||
|
ContactsListPanel 1.0 ContactsListPanel.qml
|
||||||
ProfileDescriptionPanel 1.0 ProfileDescriptionPanel.qml
|
ProfileDescriptionPanel 1.0 ProfileDescriptionPanel.qml
|
||||||
ProfileShowcaseAccountsPanel 1.0 ProfileShowcaseAccountsPanel.qml
|
ProfileShowcaseAccountsPanel 1.0 ProfileShowcaseAccountsPanel.qml
|
||||||
ProfileShowcaseAssetsPanel 1.0 ProfileShowcaseAssetsPanel.qml
|
ProfileShowcaseAssetsPanel 1.0 ProfileShowcaseAssetsPanel.qml
|
||||||
|
@ -12,7 +12,7 @@ import StatusQ.Core.Backpressure 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 "../stores"
|
import AppLayouts.Profile.stores 1.0
|
||||||
|
|
||||||
StatusModal {
|
StatusModal {
|
||||||
id: root
|
id: root
|
||||||
|
@ -10,3 +10,4 @@ TokenListPopup 1.0 TokenListPopup.qml
|
|||||||
WalletKeypairAccountMenu 1.0 WalletKeypairAccountMenu.qml
|
WalletKeypairAccountMenu 1.0 WalletKeypairAccountMenu.qml
|
||||||
WalletAddressMenu 1.0 WalletAddressMenu.qml
|
WalletAddressMenu 1.0 WalletAddressMenu.qml
|
||||||
ConfirmChangePasswordModal 1.0 ConfirmChangePasswordModal.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 1.0
|
||||||
import shared.views.chat 1.0
|
import shared.views.chat 1.0
|
||||||
|
|
||||||
import "../stores"
|
import AppLayouts.Profile.stores 1.0
|
||||||
import "../panels"
|
import AppLayouts.Profile.panels 1.0
|
||||||
import "../popups"
|
import AppLayouts.Profile.popups 1.0
|
||||||
|
|
||||||
SettingsContentBase {
|
SettingsContentBase {
|
||||||
id: root
|
id: root
|
||||||
@ -28,20 +28,17 @@ SettingsContentBase {
|
|||||||
property ContactsStore contactsStore
|
property ContactsStore contactsStore
|
||||||
property SharedStores.UtilsStore utilsStore
|
property SharedStores.UtilsStore utilsStore
|
||||||
|
|
||||||
property var mutualContactsModel
|
required property var mutualContactsModel
|
||||||
property var blockedContactsModel
|
required property var blockedContactsModel
|
||||||
property var pendingReceivedRequestContactsModel
|
required property var pendingContactsModel
|
||||||
property var pendingSentRequestContactsModel
|
required property int pendingReceivedContactsCount
|
||||||
|
|
||||||
property alias searchStr: searchBox.text
|
property alias searchStr: searchBox.text
|
||||||
property bool isPending: false
|
|
||||||
|
|
||||||
titleRowComponentLoader.sourceComponent: StatusButton {
|
titleRowComponentLoader.sourceComponent: StatusButton {
|
||||||
objectName: "ContactsView_ContactRequest_Button"
|
objectName: "ContactsView_ContactRequest_Button"
|
||||||
text: qsTr("Send contact request to chat key")
|
text: qsTr("Send contact request to chat key")
|
||||||
onClicked: {
|
onClicked: sendContactRequestComponent.createObject(root).open()
|
||||||
Global.openPopup(sendContactRequest);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function openContextMenu(model, pubKey) {
|
function openContextMenu(model, pubKey) {
|
||||||
@ -67,11 +64,108 @@ SettingsContentBase {
|
|||||||
Global.openMenu(contactContextMenuComponent, this, params)
|
Global.openMenu(contactContextMenuComponent, this, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
headerComponents: ColumnLayout {
|
||||||
id: contentItem
|
|
||||||
width: root.contentWidth
|
width: root.contentWidth
|
||||||
height: (searchBox.height + contactsTabBar.height
|
spacing: Theme.padding
|
||||||
+ stackLayout.height + (2 * Theme.bigPadding))
|
|
||||||
|
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 {
|
Component {
|
||||||
id: contactContextMenuComponent
|
id: contactContextMenuComponent
|
||||||
@ -98,191 +192,27 @@ SettingsContentBase {
|
|||||||
onClosed: destroy()
|
onClosed: destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SearchBox {
|
}
|
||||||
id: searchBox
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
placeholderText: qsTr("Search by a display name or chat key")
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusTabBar {
|
component SectionComponent: Rectangle {
|
||||||
id: contactsTabBar
|
required property string section
|
||||||
anchors.left: parent.left
|
property alias text: sectionText.text
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: searchBox.bottom
|
|
||||||
anchors.topMargin: Theme.padding
|
|
||||||
|
|
||||||
StatusTabButton {
|
width: ListView.view.width
|
||||||
id: contactsBtn
|
height: sectionText.implicitHeight
|
||||||
leftPadding: Theme.padding
|
color: Theme.palette.statusListItem.backgroundColor
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StackLayout {
|
StatusBaseText {
|
||||||
id: stackLayout
|
id: sectionText
|
||||||
anchors.left: parent.left
|
width: parent.width
|
||||||
anchors.right: parent.right
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.top: contactsTabBar.bottom
|
topPadding: Theme.halfPadding
|
||||||
currentIndex: contactsTabBar.currentIndex
|
bottomPadding: Theme.halfPadding
|
||||||
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
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
color: Theme.palette.baseColor1
|
||||||
title: qsTr("Trusted Contacts")
|
font.pixelSize: Theme.additionalTextSize
|
||||||
visible: !noFriendsItem.visible && count > 0
|
font.weight: Font.Medium
|
||||||
contactsModel: root.mutualContactsModel
|
elide: Text.ElideRight
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@ AboutView 1.0 AboutView.qml
|
|||||||
AppearanceView 1.0 AppearanceView.qml
|
AppearanceView 1.0 AppearanceView.qml
|
||||||
ChangePasswordView 1.0 ChangePasswordView.qml
|
ChangePasswordView 1.0 ChangePasswordView.qml
|
||||||
CommunitiesView 1.0 CommunitiesView.qml
|
CommunitiesView 1.0 CommunitiesView.qml
|
||||||
|
ContactsView 1.0 ContactsView.qml
|
||||||
CurrenciesModel 1.0 CurrenciesModel.qml
|
CurrenciesModel 1.0 CurrenciesModel.qml
|
||||||
LanguageView 1.0 LanguageView.qml
|
LanguageView 1.0 LanguageView.qml
|
||||||
NotificationsView 1.0 NotificationsView.qml
|
NotificationsView 1.0 NotificationsView.qml
|
||||||
PrivacyAndSecurityView 1.0 PrivacyAndSecurityView.qml
|
PrivacyAndSecurityView 1.0 PrivacyAndSecurityView.qml
|
||||||
SyncingView 1.0 SyncingView.qml
|
SyncingView 1.0 SyncingView.qml
|
||||||
|
SettingsContentBase 1.0 SettingsContentBase.qml
|
||||||
|
@ -541,7 +541,7 @@ Item {
|
|||||||
Global.displayToastMessage(toastTitle, toastSubtitle, toastIcon, toastLoading, toastType, toastLink)
|
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 = ""
|
var text = ""
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Constants.CommunityMembershipRequestState.Banned:
|
case Constants.CommunityMembershipRequestState.Banned:
|
||||||
@ -1746,8 +1746,8 @@ Item {
|
|||||||
|
|
||||||
mutualContactsModel: contactsModelAdaptor.mutualContacts
|
mutualContactsModel: contactsModelAdaptor.mutualContacts
|
||||||
blockedContactsModel: contactsModelAdaptor.blockedContacts
|
blockedContactsModel: contactsModelAdaptor.blockedContacts
|
||||||
pendingReceivedRequestContactsModel: contactsModelAdaptor.pendingReceivedRequestContacts
|
pendingContactsModel: contactsModelAdaptor.pendingContacts
|
||||||
pendingSentRequestContactsModel: contactsModelAdaptor.pendingSentRequestContacts
|
pendingReceivedContactsCount: contactsModelAdaptor.pendingReceivedRequestContacts.count
|
||||||
|
|
||||||
Binding on settingsSubsection {
|
Binding on settingsSubsection {
|
||||||
value: profileLoader.settingsSubsection
|
value: profileLoader.settingsSubsection
|
||||||
|
@ -19,7 +19,7 @@ QObject {
|
|||||||
localNickname [string] - local nickname set by the current user
|
localNickname [string] - local nickname set by the current user
|
||||||
alias [string] - generated 3 word name
|
alias [string] - generated 3 word name
|
||||||
icon [string] - thumbnail image of the user
|
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
|
colorHash [string] - generated color hash for the user's profile
|
||||||
onlineStatus [int] - the online status of the member
|
onlineStatus [int] - the online status of the member
|
||||||
isContact [bool] - whether the user is a mutual contact or not
|
isContact [bool] - whether the user is a mutual contact or not
|
||||||
@ -75,4 +75,21 @@ QObject {
|
|||||||
value: Constants.ContactRequestState.Sent
|
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
|
pubKey: model.isEnsVerified ? "" : model.compressedPubKey
|
||||||
nickName: model.localNickname
|
nickName: model.localNickname
|
||||||
userName: ProfileUtils.displayName("", model.ensName, model.displayName, model.alias)
|
userName: ProfileUtils.displayName("", model.ensName, model.displayName, model.alias)
|
||||||
isVerified: model.isVerified
|
isBlocked: model.isBlocked
|
||||||
isUntrustworthy: model.isUntrustworthy
|
isVerified: model.isVerified || model.trustStatus === Constants.trustStatus.trusted
|
||||||
|
isUntrustworthy: model.isUntrustworthy || model.trustStatus === Constants.trustStatus.untrustworthy
|
||||||
isContact: model.isContact
|
isContact: model.isContact
|
||||||
icon.name: model.icon
|
icon.name: model.thumbnailImage || model.icon
|
||||||
icon.color: Utils.colorForColorId(model.colorId)
|
icon.color: Utils.colorForColorId(model.colorId)
|
||||||
status: model.onlineStatus
|
status: model.onlineStatus
|
||||||
ringSettings.ringSpecModel: model.colorHash
|
ringSettings.ringSpecModel: model.colorHash
|
||||||
|
@ -3,3 +3,4 @@ module shared
|
|||||||
DelegateModelGeneralized 1.0 DelegateModelGeneralized.qml
|
DelegateModelGeneralized 1.0 DelegateModelGeneralized.qml
|
||||||
LoadingAnimation 1.0 LoadingAnimation.qml
|
LoadingAnimation 1.0 LoadingAnimation.qml
|
||||||
MacTrafficLights 1.0 MacTrafficLights.qml
|
MacTrafficLights 1.0 MacTrafficLights.qml
|
||||||
|
UserSearchFilterContainer 1.0 UserSearchFilterContainer.qml
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
|
||||||
import utils 1.0
|
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
|
||||||
import "../popups"
|
import utils 1.0
|
||||||
|
import shared.popups 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: noContactsRect
|
id: root
|
||||||
implicitWidth: 260
|
implicitWidth: 260
|
||||||
implicitHeight: visible ? 120 : 0
|
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 alias textColor: noContacts.color
|
||||||
|
property bool inviteButtonVisible: true
|
||||||
|
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
id: noContacts
|
id: noContacts
|
||||||
text: noContactsRect.text
|
text: root.text
|
||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: Theme.padding
|
anchors.topMargin: Theme.padding
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
font.pixelSize: 15
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
StatusButton {
|
StatusButton {
|
||||||
@ -33,7 +33,8 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.top: noContacts.bottom
|
anchors.top: noContacts.bottom
|
||||||
anchors.topMargin: Theme.padding
|
anchors.topMargin: Theme.padding
|
||||||
onClicked: Global.openPopup(inviteFriendsPopup);
|
visible: root.inviteButtonVisible
|
||||||
|
onClicked: inviteFriendsPopup.createObject(root).open()
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
@ -498,12 +498,8 @@ QtObject {
|
|||||||
readonly property QtObject contactsPanelUsage: QtObject {
|
readonly property QtObject contactsPanelUsage: QtObject {
|
||||||
readonly property int unknownPosition: -1
|
readonly property int unknownPosition: -1
|
||||||
readonly property int mutualContacts: 0
|
readonly property int mutualContacts: 0
|
||||||
readonly property int verifiedMutualContacts: 1
|
readonly property int pendingContacts: 1
|
||||||
readonly property int sentContactRequest: 2
|
readonly property int blockedContacts: 2
|
||||||
readonly property int receivedContactRequest: 3
|
|
||||||
readonly property int rejectedSentContactRequest: 4
|
|
||||||
readonly property int rejectedReceivedContactRequest: 5
|
|
||||||
readonly property int blockedContacts: 6
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property QtObject keypair: QtObject {
|
readonly property QtObject keypair: QtObject {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user