Skip to content

Commit 9596d61

Browse files
committed
feat: Improvements for community admins
- add ability to copy display name of a message author - add possibility to search for a member by a un/compressed chat key - adjust the search field placeholder text to "Search by member name or chat key" - update SB with some more variations - cleanup some the signal handling Fixes #16790
1 parent 5bf4ba2 commit 9596d61

File tree

11 files changed

+97
-103
lines changed

11 files changed

+97
-103
lines changed

storybook/pages/StatusMessagePage.qml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,17 @@ SplitView {
6666
trustIndicator: StatusContactVerificationIcons.TrustedType.None
6767
outgoingStatus: StatusMessage.OutgoingStatus.Delivered
6868
}
69+
ListElement {
70+
timestamp: 1667937930159
71+
senderId: "zqdeadbeef86"
72+
senderDisplayName: "8⃣6⃣.stateofus.eth"
73+
contentType: StatusMessage.ContentType.Text
74+
message: "Test message for a user with emoji + ENS name"
75+
isContact: false
76+
isAReply: false
77+
trustIndicator: StatusContactVerificationIcons.TrustedType.None
78+
outgoingStatus: StatusMessage.OutgoingStatus.Delivered
79+
}
6980
ListElement {
7081
timestamp: 1719769718000
7182
senderId: "zq123456790"
@@ -186,6 +197,7 @@ SplitView {
186197
SplitView.fillWidth: true
187198
SplitView.fillHeight: true
188199
color: Theme.palette.statusAppLayout.rightPanelBackgroundColor
200+
clip: true
189201

190202
ListView {
191203
anchors.margins: 16
@@ -236,7 +248,7 @@ SplitView {
236248
onReplyProfileClicked: logs.logEvent("StatusMessage::replyProfileClicked")
237249
onReplyMessageClicked: logs.logEvent("StatusMessage::replyMessageClicked")
238250
onResendClicked: logs.logEvent("StatusMessage::resendClicked")
239-
onLinkActivated: logs.logEvent("StatusMessage::linkActivated" + link)
251+
onLinkActivated: logs.logEvent("StatusMessage::linkActivated", ["link"], arguments)
240252
onImageClicked: logs.logEvent("StatusMessage::imageClicked")
241253
}
242254
}
@@ -254,3 +266,4 @@ SplitView {
254266
}
255267

256268
// category: Components
269+
// status: good

ui/StatusQ/src/StatusQ/Components/StatusContactVerificationIcons.qml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import QtQuick 2.14
1+
import QtQuick 2.15
22

33
import StatusQ.Core 0.1
44
import StatusQ.Controls 0.1

ui/StatusQ/src/StatusQ/Components/StatusMemberListItem.qml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,13 @@ ItemDelegate {
7575
/*!
7676
\qmlproperty string StatusMemberListItem::status
7777
This property holds the connectivity status of the member represented.
78-
0 - offline
79-
1 - online
80-
2 - doNotDisturb
81-
3 - idle
78+
79+
int unknown: -1
80+
int inactive: 0
81+
int online: 1
82+
8283
*/
83-
// FIXME: move Constants.userStatus from status-desktop
84+
// FIXME: move Constants.onlineStatus from status-desktop
8485
property int status: 0
8586
/*!
8687
\qmlproperty string StatusMemberListItem::isAdmin
@@ -228,7 +229,7 @@ ItemDelegate {
228229

229230
StatusToolTip {
230231
text: parent.text
231-
delay: 0
232+
delay: 50
232233
visible: parent.truncated && primaryTextHandler.hovered
233234
}
234235
}
@@ -252,7 +253,7 @@ ItemDelegate {
252253

253254
StatusToolTip {
254255
text: parent.text
255-
delay: 0
256+
delay: 50
256257
visible: parent.truncated && secondaryTextHandler.hovered
257258
}
258259
}

ui/StatusQ/src/StatusQ/Components/StatusMessage.qml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,8 @@ Control {
8181
property StatusMessageDetails messageDetails: StatusMessageDetails {}
8282
property StatusMessageDetails replyDetails: StatusMessageDetails {}
8383

84-
signal clicked(var sender, var mouse)
8584
signal profilePictureClicked(var sender, var mouse)
86-
signal senderNameClicked(var sender, var mouse)
85+
signal senderNameClicked(var sender)
8786
signal replyProfileClicked(var sender, var mouse)
8887
signal replyMessageClicked(var mouse)
8988

@@ -265,7 +264,7 @@ Control {
265264
amISender: root.messageDetails.amISender
266265
messageOriginInfo: root.messageDetails.messageOriginInfo
267266
resendError: root.messageDetails.amISender ? root.resendError : ""
268-
onClicked: root.senderNameClicked(sender, mouse)
267+
onClicked: (sender) => root.senderNameClicked(sender)
269268
onResendClicked: root.resendClicked()
270269
timestamp: root.timestamp
271270
showFullTimestamp: root.isInPinnedPopup

ui/StatusQ/src/StatusQ/Components/StatusMessageHeader.qml

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import QtQuick 2.14
2-
import QtQuick.Layouts 1.14
1+
import QtQuick 2.15
2+
import QtQuick.Layouts 1.15
33

44
import StatusQ.Core 0.1
55
import StatusQ.Core.Theme 0.1
@@ -28,7 +28,7 @@ Item {
2828
property int outgoingStatus: StatusMessage.OutgoingStatus.Unknown
2929
property bool showOutgointStatusLabel: false
3030

31-
signal clicked(var sender, var mouse)
31+
signal clicked(var sender)
3232
signal resendClicked()
3333

3434
implicitHeight: layout.implicitHeight
@@ -46,29 +46,39 @@ Item {
4646
spacing: 4
4747
width: parent.width
4848

49-
StatusBaseText {
49+
TextEdit {
5050
id: primaryDisplayName
5151
objectName: "StatusMessageHeader_DisplayName"
5252
verticalAlignment: Text.AlignVCenter
5353
Layout.fillWidth: true
5454
Layout.maximumWidth: Math.ceil(implicitWidth)
5555
Layout.bottomMargin: 2 // offset for the underline to stay vertically centered
5656
font.weight: Font.Medium
57-
font.underline: mouseArea.containsMouse
57+
font.underline: root.displayNameClickable && hhandler.hovered
58+
font.family: Theme.baseFont.name
5859
font.pixelSize: Theme.primaryTextFontSize
5960
wrapMode: Text.WordWrap
6061
color: Theme.palette.primaryColor1
62+
selectionColor: Theme.palette.primaryColor2
63+
selectedTextColor: Theme.palette.primaryColor3
6164
text: root.amISender ? qsTr("You") : Emoji.parse(root.sender.displayName)
62-
MouseArea {
63-
id: mouseArea
64-
anchors.fill: parent
65-
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
66-
acceptedButtons: Qt.LeftButton | Qt.RightButton
65+
readOnly: true
66+
selectByMouse: true
67+
textFormat: TextEdit.AutoText
68+
69+
// to make the text easier to select, but w/o inflating the spacing with other items in the parent RowLayout
70+
leftPadding: 4
71+
rightPadding: 4
72+
Layout.leftMargin: -4
73+
Layout.rightMargin: -4
74+
75+
HoverHandler {
76+
id: hhandler
77+
cursorShape: !!parent.selectedText ? Qt.IBeamCursor : root.displayNameClickable ? Qt.PointingHandCursor : undefined
78+
}
79+
TapHandler {
6780
enabled: root.displayNameClickable
68-
hoverEnabled: true
69-
onClicked: {
70-
root.clicked(this, mouse)
71-
}
81+
onSingleTapped: root.clicked(this)
7282
}
7383
}
7484

@@ -77,7 +87,6 @@ Item {
7787
active: root.messageOriginInfo
7888
asynchronous: true
7989
sourceComponent: StatusBaseText {
80-
id: messageOriginInfo
8190
verticalAlignment: Text.AlignVCenter
8291
color: Theme.palette.baseColor1
8392
font.pixelSize: Theme.asideTextFontSize
@@ -90,7 +99,6 @@ Item {
9099
active: !root.amISender
91100
asynchronous: true
92101
sourceComponent: StatusContactVerificationIcons {
93-
id: verificationIcons
94102
isContact: root.isContact
95103
trustIndicator: root.trustIndicator
96104
}
@@ -148,7 +156,6 @@ Item {
148156
Component {
149157
id: dotComponent
150158
StatusBaseText {
151-
id: dot
152159
verticalAlignment: Text.AlignVCenter
153160
font.pixelSize: Theme.asideTextFontSize
154161
color: Theme.palette.baseColor1

ui/StatusQ/src/StatusQ/Components/StatusMessageSenderDetails.qml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import QtQuick 2.0
1+
import QtQuick 2.15
2+
23
import StatusQ.Core 0.1
34

45
QtObject {

ui/StatusQ/src/StatusQ/Components/private/statusMessage/StatusMessageReply.qml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import QtQuick 2.14
2-
import QtQuick.Shapes 1.13
3-
import QtQuick.Layouts 1.14
1+
import QtQuick 2.15
2+
import QtQuick.Shapes 1.15
3+
import QtQuick.Layouts 1.15
44

55
import StatusQ.Core 0.1
66
import StatusQ.Core.Theme 0.1

ui/app/AppLayouts/Communities/panels/MembersSettingsPanel.qml

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ SettingsPage {
2626
property int memberRole
2727
property bool editable: true
2828

29-
signal membershipRequestsClicked()
3029
signal kickUserClicked(string id)
3130
signal banUserClicked(string id, bool deleteAllMessages)
3231
signal unbanUserClicked(string id)
@@ -123,12 +122,6 @@ SettingsPage {
123122
rootStore: root.rootStore
124123
utilsStore: root.utilsStore
125124
memberRole: root.memberRole
126-
placeholderText: {
127-
if (root.membersModel.ModelCount.count === 0)
128-
return qsTr("No members to search")
129-
130-
return qsTr("Search %1's %n member(s)", "", root.membersModel ? root.membersModel.ModelCount.count : 0).arg(root.communityName)
131-
}
132125
panelType: MembersTabPanel.TabType.AllMembers
133126

134127
Layout.fillWidth: true
@@ -156,12 +149,6 @@ SettingsPage {
156149
rootStore: root.rootStore
157150
utilsStore: root.utilsStore
158151
memberRole: root.memberRole
159-
placeholderText: {
160-
if (root.pendingMembersModel.ModelCount.count === 0)
161-
return qsTr("No pending requests to search")
162-
163-
return qsTr("Search %1's %n pending request(s)", "", root.pendingMembersModel.ModelCount.count).arg(root.communityName)
164-
}
165152
panelType: MembersTabPanel.TabType.PendingRequests
166153

167154
Layout.fillWidth: true
@@ -176,12 +163,6 @@ SettingsPage {
176163
rootStore: root.rootStore
177164
utilsStore: root.utilsStore
178165
memberRole: root.memberRole
179-
placeholderText: {
180-
if (root.declinedMembersModel.ModelCount.count === 0)
181-
return qsTr("No rejected members to search")
182-
183-
return qsTr("Search %1's %n rejected member(s)", "", root.declinedMembersModel.ModelCount.count).arg(root.communityName)
184-
}
185166
panelType: MembersTabPanel.TabType.DeclinedRequests
186167

187168
Layout.fillWidth: true
@@ -195,12 +176,6 @@ SettingsPage {
195176
rootStore: root.rootStore
196177
utilsStore: root.utilsStore
197178
memberRole: root.memberRole
198-
placeholderText: {
199-
if (root.bannedMembersModel.ModelCount.count === 0)
200-
return qsTr("No banned members to search")
201-
202-
return qsTr("Search %1's %n banned member(s)", "", root.bannedMembersModel.ModelCount.count).arg(root.communityName)
203-
}
204179
panelType: MembersTabPanel.TabType.BannedMembers
205180

206181
Layout.fillWidth: true

ui/app/AppLayouts/Communities/panels/MembersTabPanel.qml

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import SortFilterProxyModel 0.2
2424
Item {
2525
id: root
2626

27-
property string placeholderText
27+
property string placeholderText: qsTr("Search by member name or chat key")
2828
property var model
2929
property RootStore rootStore
3030
property SharedStores.UtilsStore utilsStore
@@ -60,7 +60,7 @@ Item {
6060
Layout.preferredWidth: 400
6161
Layout.leftMargin: 12
6262
placeholderText: root.placeholderText
63-
enabled: !!model && model.count > 0
63+
enabled: !!root.model && !root.model.ModelCount.empty
6464
}
6565

6666
StatusListView {
@@ -71,8 +71,16 @@ Item {
7171
Layout.fillHeight: true
7272

7373
model: SortFilterProxyModel {
74+
id: filteredModel
7475
sourceModel: root.model
7576

77+
function searchPredicate(ensName, displayName, aliasName) {
78+
const lowerCaseSearchString = memberSearch.text.toLowerCase()
79+
const secondaryName = ProfileUtils.displayName("", ensName, displayName, aliasName)
80+
81+
return secondaryName.toLowerCase().includes(lowerCaseSearchString)
82+
}
83+
7684
sorters : [
7785
StringSorter {
7886
roleName: "preferredDisplayName"
@@ -81,21 +89,23 @@ Item {
8189
]
8290

8391
filters: AnyOf {
92+
enabled: memberSearch.text !== ""
93+
// substring search for either nickname or the other primary/secondary display name
8494
SearchFilter {
8595
roleName: "localNickname"
8696
searchPhrase: memberSearch.text
8797
}
88-
SearchFilter {
89-
roleName: "displayName"
90-
searchPhrase: memberSearch.text
91-
}
92-
SearchFilter {
93-
roleName: "ensName"
94-
searchPhrase: memberSearch.text
98+
FastExpressionFilter {
99+
expression: {
100+
memberSearch.text
101+
return filteredModel.searchPredicate(model.ensName, model.displayName, model.alias)
102+
}
103+
expectedRoles: ["ensName", "displayName", "alias"]
95104
}
96-
SearchFilter {
97-
roleName: "alias"
98-
searchPhrase: memberSearch.text
105+
// exact search for the full key
106+
ValueFilter {
107+
roleName: "compressedPubKey"
108+
value: memberSearch.text
99109
}
100110
}
101111
}
@@ -346,25 +356,24 @@ Item {
346356
ProfileContextMenu {
347357
id: memberContextMenuView
348358

349-
property string pubKey
359+
required property string pubKey
350360

351-
onOpenProfileClicked: Global.openProfilePopup(memberContextMenuView.pubKey, null)
361+
onOpenProfileClicked: Global.openProfilePopup(pubKey, null)
352362
onCreateOneToOneChat: {
353363
Global.changeAppSectionBySectionType(Constants.appSection.chat)
354-
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", membersContextMenuView.pubKey, "")
364+
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", pubKey, "")
355365
}
356-
onReviewContactRequest: Global.openReviewContactRequestPopup(memberContextMenuView.pubKey, null)
357-
onSendContactRequest: Global.openContactRequestPopup(memberContextMenuView.pubKey, null)
358-
onEditNickname: Global.openNicknamePopupRequested(memberContextMenuView.pubKey, null)
359-
onRemoveNickname: root.rootStore.contactsStore.changeContactNickname(memberContextMenuView.pubKey,
360-
"", memberContextMenuView.displayName, true)
361-
onUnblockContact: Global.unblockContactRequested(memberContextMenuView.pubKey)
362-
onMarkAsUntrusted: Global.markAsUntrustedRequested(memberContextMenuView.pubKey)
363-
onRemoveTrustStatus: root.rootStore.contactsStore.removeTrustStatus(memberContextMenuView.pubKey)
364-
onRemoveContact: Global.removeContactRequested(memberContextMenuView.pubKey)
365-
onBlockContact: Global.blockContactRequested(memberContextMenuView.pubKey)
366-
onMarkAsTrusted: Global.openMarkAsIDVerifiedPopup(memberContextMenuView.pubKey, null)
367-
onRemoveTrustedMark: Global.openRemoveIDVerificationDialog(memberContextMenuView.pubKey, null)
366+
onReviewContactRequest: Global.openReviewContactRequestPopup(pubKey, null)
367+
onSendContactRequest: Global.openContactRequestPopup(pubKey, null)
368+
onEditNickname: Global.openNicknamePopupRequested(pubKey, null)
369+
onRemoveNickname: root.rootStore.contactsStore.changeContactNickname(pubKey, "", displayName, true)
370+
onUnblockContact: Global.unblockContactRequested(pubKey)
371+
onMarkAsUntrusted: Global.markAsUntrustedRequested(pubKey)
372+
onRemoveTrustStatus: root.rootStore.contactsStore.removeTrustStatus(pubKey)
373+
onRemoveContact: Global.removeContactRequested(pubKey)
374+
onBlockContact: Global.blockContactRequested(pubKey)
375+
onMarkAsTrusted: Global.openMarkAsIDVerifiedPopup(pubKey, null)
376+
onRemoveTrustedMark: Global.openRemoveIDVerificationDialog(pubKey, null)
368377
onClosed: destroy()
369378
}
370379
}

ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ StatusSectionLayout {
239239
StatusQUtils.Utils.filterXSS(item.introMessage),
240240
StatusQUtils.Utils.filterXSS(item.outroMessage),
241241
item.options.requestToJoinEnabled ? Constants.communityChatOnRequestAccess
242-
: Constants.communityChatPublicAccess,
242+
: Constants.communityChatPublicAccess,
243243
item.color.toString().toUpperCase(),
244244
item.selectedTags,
245245
Utils.getImageAndCropInfoJson(item.logoImagePath, item.logoCropRect),
@@ -298,7 +298,7 @@ StatusSectionLayout {
298298
declinedMembersModel: root.declinedMembers
299299

300300
editable: root.isAdmin || root.isOwner || root.isTokenMasterOwner
301-
memberRole: community.memberRole
301+
memberRole: root.community.memberRole
302302
communityName: root.community.name
303303

304304
onKickUserClicked: root.rootStore.removeUserFromCommunity(id)

0 commit comments

Comments
 (0)