feat(storybook): add MembersSelector to storybook

closes: #8178
fixes: #8210
This commit is contained in:
Patryk Osmaczko 2022-11-08 09:36:08 +01:00 committed by osmaczko
parent d26ca0baf9
commit bd7f890cce
14 changed files with 581 additions and 150 deletions

View File

@ -68,6 +68,9 @@ ApplicationWindow {
ListElement { ListElement {
title: "ProfileFetchingView" title: "ProfileFetchingView"
} }
ListElement {
title: "MembersSelector"
}
} }
SplitView { SplitView {

View File

@ -0,0 +1,120 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
Item {
id: root
property alias model: listView.model
implicitWidth: layout.implicitWidth
implicitHeight: layout.implicitHeight
signal removeClicked(int index)
signal removeAllClicked
signal addClicked
ColumnLayout {
id: layout
anchors.fill: parent
ListView {
id: listView
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
spacing: 32
delegate: ColumnLayout {
id: delegate
spacing: 0
width: ListView.view.width
Row {
Label {
width: delegate.width / 2
anchors.verticalCenter: parent.verticalCenter
text: "displayName:\t"
}
TextField {
width: delegate.width / 2
text: model.displayName
onTextChanged: model.displayName = text
}
}
Row {
Label {
width: delegate.width / 2
anchors.verticalCenter: parent.verticalCenter
text: "localNickname:\t"
}
TextField {
width: delegate.width / 2
text: model.localNickname
onTextChanged: model.localNickname = text
}
}
Row {
Label {
width: delegate.width / 2
anchors.verticalCenter: parent.verticalCenter
text: "isVerified:\t"
}
Switch {
width: delegate.width / 2
checked: model.isVerified
onCheckedChanged: model.isVerified = checked
}
}
Row {
Label {
width: delegate.width / 2
anchors.verticalCenter: parent.verticalCenter
text: "isUntrustworthy:\t"
}
Switch {
width: delegate.width / 2
checked: model.isUntrustworthy
onCheckedChanged: model.isUntrustworthy = checked
}
}
Row {
Label {
width: delegate.width / 2
anchors.verticalCenter: parent.verticalCenter
text: "onlineStatus:\t"
}
SpinBox {
width: delegate.width / 2
from: 0
to: 1
value: model.onlineStatus
onValueChanged: model.onlineStatus = value
}
}
Button {
text: "remove"
onClicked: root.removeClicked(index)
}
}
ScrollBar.vertical: ScrollBar {}
}
Button {
Layout.fillWidth: true
text: "remove all"
onClicked: root.removeAllClicked()
}
Button {
Layout.fillWidth: true
text: "add"
onClicked: root.addClicked()
}
}
}

View File

@ -0,0 +1,267 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import AppLayouts.Chat.views 1.0
import Storybook 1.0
import utils 1.0
SplitView {
id: root
Logs { id: logs }
property bool globalUtilsReady: false
property bool mainModuleReady: false
QtObject {
function isCompressedPubKey(publicKey) {
return true
}
function getCompressedPk(publicKey) {
return "123456789"
}
function getColorHashAsJson(publicKey) {
return JSON.stringify([{colorId: 0, segmentLength: 1},
{colorId: 19, segmentLength: 2}])
}
function getColorId(publicKey) {
return Math.floor(Math.random() * 10)
}
function isEnsVerified(publicKey) {
return false
}
Component.onCompleted: {
Utils.globalUtilsInst = this
root.globalUtilsReady = true
}
Component.onDestruction: {
root.globalUtilsReady = false
Utils.globalUtilsInst = {}
}
}
QtObject {
function getContactDetailsAsJson() {
return JSON.stringify({ ensVerified: false })
}
Component.onCompleted: {
Utils.mainModuleInst = this
root.mainModuleReady = true
}
Component.onDestruction: {
root.mainModuleReady = false
Utils.mainModuleInst = {}
}
}
QtObject {
id: rootStoreMock
readonly property var contactsModel: ListModel {
id: contactsModel
Component.onCompleted: {
for(let i=0; i < 20; i++) {
append(d.createUserDict(i))
}
}
}
readonly property var contactsStore: QtObject {
readonly property var mainModuleInst: null
}
function amIChatAdmin() {
return chatAdminSwitch.checked
}
}
QtObject {
id: usersStoreMock
readonly property var usersModel: ListModel {
Component.onCompleted: {
for(let i=0; i < 4; i++) {
append(d.createMemberDict(i))
}
}
}
readonly property var temporaryModel: ListModel {
Component.onCompleted: usersStoreMock.resetTemporaryModel()
}
function appendTemporaryModel(pubKey, displayName) {
temporaryModel.append({
pubKey: pubKey,
displayName: displayName,
})
}
function removeFromTemporaryModel(pubKey) {
for(let i = 0; i < temporaryModel.count; i++) {
if (temporaryModel.get(i).pubKey === pubKey) {
temporaryModel.remove(i, 1)
return
}
}
}
function resetTemporaryModel() {
temporaryModel.clear()
for(let i = 0; i < usersModel.count; i++) {
const obj = usersModel.get(i)
temporaryModel.append(obj)
}
}
function updateGroupMembers() {
const users = []
for(let i = 0; i < temporaryModel.count; i++) {
const obj = temporaryModel.get(i)
users.push({
pubKey: obj.pubKey,
displayName: obj.displayName,
localNickname: "",
alias: "three word name(%1)".arg(obj.pubKey),
isVerified: false,
isUntrustworthy: false,
isContact: true,
icon: "",
color: "red",
onlineStatus: 0,
isAdmin: i == 0 ? true : false
})
}
usersModel.clear()
usersModel.append(users)
logs.logEvent("UsersStore::updateGroupMembers")
}
}
QtObject {
id: d
function createUserDict(seed: int) {
const pubKey = "0x%1".arg(seed)
return {
pubKey: pubKey,
displayName: seed%8 ? "user%1".arg(seed) : "",
localNickname: seed%3 ? "" : "nickname%1".arg(seed),
alias: "three word name(%1)".arg(pubKey),
isVerified: seed%3 ? false : true,
isUntrustworthy: seed%5 ? false : true,
isContact: true,
icon: "",
color: seed%2 ? "white" : "red",
onlineStatus: seed%2,
}
}
function createMemberDict(seed: int) {
var member = createUserDict(seed)
member["isAdmin"] = seed === 0
return member
}
}
SplitView {
orientation: Qt.Vertical
SplitView.fillWidth: true
SplitView.fillHeight: true
SwipeView {
id: swipeView
SplitView.fillWidth: true
SplitView.fillHeight: true
interactive: false
currentIndex: selectorsSwitch.checked
Item {
Loader {
active: root.globalUtilsReady && root.mainModuleReady
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 64
}
sourceComponent: MembersSelectorView {
rootStore: rootStoreMock
}
}
}
Item {
Loader {
active: root.globalUtilsReady && root.mainModuleReady
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 64
}
sourceComponent: MembersEditSelectorView {
rootStore: rootStoreMock
usersStore: usersStoreMock
}
}
}
}
LogsAndControlsPanel {
id: logsAndControlsPanel
SplitView.minimumHeight: 100
SplitView.preferredHeight: 200
logsView.logText: logs.logText
ColumnLayout {
Switch {
id: selectorsSwitch
text: "members editor"
}
Switch {
id: chatAdminSwitch
visible: selectorsSwitch.checked
text: "chat admin"
onCheckedChanged: usersStore.resetTemporaryModel()
}
}
}
}
Pane {
SplitView.minimumWidth: 300
SplitView.preferredWidth: 300
MembersSelectorModelEditor {
anchors.fill: parent
model: contactsModel
onRemoveClicked: contactsModel.remove(index, 1)
onRemoveAllClicked: contactsModel.clear()
onAddClicked: contactsModel.append(d.createUserDict(contactsModel.count))
}
}
}

View File

@ -70,8 +70,8 @@ SplitView {
}) })
} }
Component.onCompleted: { Component.onCompleted: {
root.mainModuleReady = true
Utils.mainModuleInst = this Utils.mainModuleInst = this
root.mainModuleReady = true
} }
Component.onDestruction: { Component.onDestruction: {
root.mainModuleReady = false root.mainModuleReady = false

View File

@ -33,6 +33,8 @@ ListView {
readonly property int availableWidth: width - leftMargin - rightMargin readonly property int availableWidth: width - leftMargin - rightMargin
readonly property int availableHeight: height - topMargin - bottomMargin readonly property int availableHeight: height - topMargin - bottomMargin
readonly property alias horizontalScrollBar: horizontalScrollBar
readonly property alias verticalScrollBar: verticalScrollBar
clip: true clip: true
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
@ -40,11 +42,13 @@ ListView {
synchronousDrag: true synchronousDrag: true
ScrollBar.horizontal: StatusScrollBar { ScrollBar.horizontal: StatusScrollBar {
id: horizontalScrollBar
policy: ScrollBar.AsNeeded policy: ScrollBar.AsNeeded
visible: resolveVisibility(policy, root.width, root.contentWidth) visible: resolveVisibility(policy, root.width, root.contentWidth)
} }
ScrollBar.vertical: StatusScrollBar { ScrollBar.vertical: StatusScrollBar {
id: verticalScrollBar
policy: ScrollBar.AsNeeded policy: ScrollBar.AsNeeded
visible: resolveVisibility(policy, root.height, root.contentHeight) visible: resolveVisibility(policy, root.height, root.contentHeight)
} }

View File

@ -19,13 +19,12 @@ Item {
property alias suggestionsModel: suggestionsListView.model property alias suggestionsModel: suggestionsListView.model
property alias suggestionsDelegate: suggestionsListView.delegate property alias suggestionsDelegate: suggestionsListView.delegate
property size suggestionsDelegateSize: Qt.size(344, 64)
readonly property alias label: label readonly property alias label: label
readonly property alias warningLabel: warningLabel readonly property alias warningLabel: warningLabel
readonly property alias edit: edit readonly property alias edit: edit
property bool confirmBtnEnabled: (listView.count > 0)
signal confirmed() signal confirmed()
signal rejected() signal rejected()
@ -66,105 +65,109 @@ Item {
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
onWidthChanged: { StatusScrollView {
listView.positionViewAtEnd(); id: scrollView
}
function positionViewAtEnd() {
if (scrollView.contentWidth > scrollView.width) {
scrollView.contentX = scrollView.contentWidth - scrollView.width
} else {
scrollView.contentX = 0
}
}
RowLayout {
anchors.fill: parent anchors.fill: parent
Item { padding: 0
//40 px least space for input
Layout.preferredWidth: (listView.contentWidth < (parent.width - 40)) ? onContentWidthChanged: positionViewAtEnd()
listView.contentWidth : (parent.width - 40) onWidthChanged: positionViewAtEnd()
Layout.preferredHeight: 44
RowLayout {
height: scrollView.height
StatusListView { StatusListView {
clip: true
id: listView id: listView
width: parent.width Layout.fillWidth: true
height: 30 Layout.preferredHeight: 30
anchors.verticalCenter: parent.verticalCenter implicitWidth: contentWidth
orientation: ListView.Horizontal orientation: ListView.Horizontal
spacing: Style.current.halfPadding spacing: Style.current.halfPadding
ScrollBar.horizontal: scrollBar
onCountChanged: {
positionViewAtEnd();
}
}
StatusScrollBar {
id: scrollBar
parent: listView.parent
anchors.top: listView.bottom
anchors.left: listView.left
anchors.right: listView.right
policy: ScrollBar.AsNeeded
visible: resolveVisibility(policy, listView.width, listView.contentWidth)
}
}
TextInput {
id: edit
Layout.fillWidth: true
Layout.fillHeight: true
verticalAlignment: Text.AlignVCenter
font.pixelSize: 15
color: Theme.palette.directColor1
clip: true
selectByMouse: true
selectionColor: Theme.palette.primaryColor2
selectedTextColor: color
cursorDelegate: Rectangle {
color: Theme.palette.primaryColor1
implicitWidth: 2
radius: 1
visible: edit.cursorVisible
SequentialAnimation on visible {
loops: Animation.Infinite
running: edit.cursorVisible
PropertyAnimation { to: false; duration: 600; }
PropertyAnimation { to: true; duration: 600; }
}
} }
Keys.onPressed: { TextInput {
if (event.matches(StandardKey.Paste)) { id: edit
event.accepted = true Layout.minimumWidth: 4
const previousText = text; Layout.fillHeight: true
const previousSelectedText = selectedText; verticalAlignment: Text.AlignVCenter
paste() font.pixelSize: 15
if (previousText === "" || previousSelectedText.length === previousText.length) color: Theme.palette.directColor1
root.textPasted(text)
return;
}
if (suggestionsDialog.visible) { selectByMouse: true
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { selectionColor: Theme.palette.primaryColor2
root.entryAccepted(suggestionsListView.itemAtIndex(suggestionsListView.currentIndex)) selectedTextColor: color
} else if (event.key === Qt.Key_Up) {
suggestionsListView.decrementCurrentIndex() cursorDelegate: Rectangle {
} else if (event.key === Qt.Key_Down) { color: Theme.palette.primaryColor1
suggestionsListView.incrementCurrentIndex() implicitWidth: 2
radius: 1
visible: edit.cursorVisible
SequentialAnimation on visible {
loops: Animation.Infinite
running: edit.cursorVisible
PropertyAnimation { to: false; duration: 600; }
PropertyAnimation { to: true; duration: 600; }
} }
} else { }
if (event.key === Qt.Key_Backspace && edit.text === "" && listView.count > 0) {
root.entryRemoved(listView.itemAtIndex(listView.count - 1)) Keys.onPressed: {
} else if (event.key === Qt.Key_Return || event.key === Qt.Enter) { if (event.matches(StandardKey.Paste)) {
root.enterKeyPressed() event.accepted = true
} else if (event.key === Qt.Key_Escape) { const previousText = text;
root.rejected() const previousSelectedText = selectedText;
} else if (event.key === Qt.Key_Up) { paste()
root.upKeyPressed(); if (previousText === "" || previousSelectedText.length === previousText.length)
} else if (event.key === Qt.Key_Down) { root.textPasted(text)
root.downKeyPressed(); return;
}
if (suggestionsDialog.visible) {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
root.entryAccepted(suggestionsListView.itemAtIndex(suggestionsListView.currentIndex))
} else if (event.key === Qt.Key_Up) {
suggestionsListView.decrementCurrentIndex()
} else if (event.key === Qt.Key_Down) {
suggestionsListView.incrementCurrentIndex()
}
} else {
if (event.key === Qt.Key_Backspace && edit.text === "") {
root.entryRemoved(listView.itemAtIndex(listView.count - 1))
} else if (event.key === Qt.Key_Return || event.key === Qt.Enter) {
root.enterKeyPressed()
} else if (event.key === Qt.Key_Escape) {
root.rejected()
} else if (event.key === Qt.Key_Up) {
root.upKeyPressed()
} else if (event.key === Qt.Key_Down) {
root.downKeyPressed()
}
} }
} }
} }
// ensure edit cursor is visible
Item {
Layout.fillHeight: true
implicitWidth: 1
}
} }
// ensure edit cursor is visible ScrollBar.horizontal: StatusScrollBar {
Item { id: scrollBar
Layout.fillHeight: true parent: scrollView.parent
implicitWidth: 1 anchors.top: scrollView.bottom
anchors.left: scrollView.left
anchors.right: scrollView.right
policy: ScrollBar.AsNeeded
visible: resolveVisibility(policy, scrollView.width, scrollView.contentWidth)
} }
} }
} }
@ -192,7 +195,7 @@ Item {
StatusButton { StatusButton {
objectName: "inlineSelectorConfirmButton" objectName: "inlineSelectorConfirmButton"
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
enabled: root.confirmBtnEnabled enabled: (listView.count > 0)
text: qsTr("Confirm") text: qsTr("Confirm")
onClicked: root.confirmed() onClicked: root.confirmed()
} }
@ -207,9 +210,9 @@ Item {
Popup { Popup {
id: suggestionsDialog id: suggestionsDialog
parent: edit parent: scrollView
x: (parent.contentWidth - Style.current.halfPadding) x: Math.min(parent.width, parent.contentWidth)
y: (parent.height + Style.current.halfPadding) y: parent.height + Style.current.halfPadding
visible: edit.text !== "" visible: edit.text !== ""
padding: Style.current.halfPadding padding: Style.current.halfPadding
background: StatusDialogBackground { background: StatusDialogBackground {
@ -226,10 +229,19 @@ Item {
} }
} }
height: suggestionsListView.count ?
Math.min(400, suggestionsListView.count * suggestionsDelegateSize.height + 2 * padding) :
noResultsFoundText.height + 2 * padding
width: suggestionsDelegateSize.width
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
StatusBaseText { StatusBaseText {
id: noResultsFoundText
Layout.fillWidth: true
visible: root.suggestionsModel.count === 0 visible: root.suggestionsModel.count === 0
text: qsTr("No results found") text: qsTr("No results found")
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1
@ -237,11 +249,22 @@ Item {
StatusListView { StatusListView {
id: suggestionsListView id: suggestionsListView
Layout.fillWidth: true
Layout.fillHeight: true
visible: root.suggestionsModel.count visible: root.suggestionsModel.count
implicitWidth: contentItem.childrenRect.width highlightMoveDuration: 0
implicitHeight: contentItem.childrenRect.height highlightMoveVelocity: -1
verticalScrollBar {
visible: contentHeight > height
policy: ScrollBar.AlwaysOn
}
onVisibleChanged: currentIndex = 0 onVisibleChanged: currentIndex = 0
onCountChanged: currentIndex = 0
} }
} }
} }

View File

@ -63,6 +63,10 @@ QtObject {
return chatCommunitySectionModule.getMySectionId() return chatCommunitySectionModule.getMySectionId()
} }
function amIChatAdmin() {
return currentChatContentModule().amIChatAdmin()
}
function acceptContactRequest(pubKey) { function acceptContactRequest(pubKey) {
chatCommunitySectionModule.acceptContactRequest(pubKey) chatCommunitySectionModule.acceptContactRequest(pubKey)
} }

View File

@ -4,11 +4,20 @@ QtObject {
id: root id: root
property var usersModule property var usersModule
property var usersModel
onUsersModuleChanged: { readonly property var usersModel: usersModule ? usersModule.model : null
if(!usersModule) readonly property var temporaryModel: usersModule ? usersModule.temporaryModel : null
return
root.usersModel = usersModule.model function appendTemporaryModel(pubKey, displayName) {
usersModule.appendTemporaryModel(pubKey, displayName)
}
function removeFromTemporaryModel(pubKey) {
usersModule.removeFromTemporaryModel(pubKey)
}
function resetTemporaryModel() {
usersModule.resetTemporaryModel()
}
function updateGroupMembers() {
usersModule.updateGroupMembers()
} }
} }

View File

@ -345,9 +345,10 @@ Item {
id: membersSelector id: membersSelector
MembersEditSelectorView { MembersEditSelectorView {
sectionModule: root.chatSectionModule
chatContentModule: root.chatContentModule
rootStore: root.rootStore rootStore: root.rootStore
usersStore: UsersStore {
usersModule: root.chatContentModule.usersModule
}
onConfirmed: root.state = d.stateInfoButtonContent onConfirmed: root.state = d.stateInfoButtonContent
onRejected: root.state = d.stateInfoButtonContent onRejected: root.state = d.stateInfoButtonContent

View File

@ -19,35 +19,32 @@ import SortFilterProxyModel 0.2
MembersSelectorBase { MembersSelectorBase {
id: root id: root
// TODO: use stores instead of modules property var usersStore
property var sectionModule
property var chatContentModule
confirmBtnEnabled: true
onConfirmed: { onConfirmed: {
d.updateGroupMembers() usersStore.updateGroupMembers()
d.resetTemporaryModel() usersStore.resetTemporaryModel()
} }
onRejected: { onRejected: {
d.resetTemporaryModel() usersStore.resetTemporaryModel()
} }
limitReached: (model.count === membersLimit)
onEntryAccepted: { onEntryAccepted: if (suggestionsDelegate) {
if (!root.limitReached) { if (!root.limitReached) {
d.appendTemporaryModel(suggestionsDelegate._pubKey, suggestionsDelegate.userName) usersStore.appendTemporaryModel(suggestionsDelegate._pubKey, suggestionsDelegate.userName)
root.edit.clear() root.edit.clear()
} }
} }
onEntryRemoved: { onEntryRemoved: if (delegate) {
if (!delegate.isReadonly) { if (!delegate.isReadonly) {
d.removeFromTemporaryModel(delegate._pubKey) usersStore.removeFromTemporaryModel(delegate._pubKey)
} }
} }
model: SortFilterProxyModel { model: SortFilterProxyModel {
sourceModel: root.chatContentModule.usersModule.temporaryModel sourceModel: root.usersStore.temporaryModel
sorters: RoleSorter { sorters: RoleSorter {
roleName: "isAdmin" roleName: "isAdmin"
sortOrder: Qt.DescendingOrder sortOrder: Qt.DescendingOrder
@ -58,35 +55,19 @@ MembersSelectorBase {
readonly property string _pubKey: model.pubKey readonly property string _pubKey: model.pubKey
height: ListView.view.height height: ListView.view.height
text: model.displayName !== "" ? model.displayName : model.alias text: root.tagText(model.localNickname, model.displayName, model.alias)
isReadonly: { isReadonly: {
if (model.isAdmin) return true if (model.isAdmin) return true
if (root.chatContentModule.amIChatAdmin()) return false if (root.rootStore.amIChatAdmin()) return false
return index < root.chatContentModule.usersModule.model.count return index < root.usersStore.usersModel.count
} }
icon: model.isAdmin ? "crown" : "" icon: model.isAdmin ? "crown" : ""
onClicked: root.entryRemoved(this) onClicked: root.entryRemoved(this)
} }
QtObject {
id: d
function appendTemporaryModel(pubKey, displayName) {
root.chatContentModule.usersModule.appendTemporaryModel(pubKey, displayName)
}
function removeFromTemporaryModel(pubKey) {
root.chatContentModule.usersModule.removeFromTemporaryModel(pubKey)
}
function resetTemporaryModel() {
root.chatContentModule.usersModule.resetTemporaryModel()
}
function updateGroupMembers() {
root.chatContentModule.usersModule.updateGroupMembers()
}
}
Component.onCompleted: { Component.onCompleted: {
d.resetTemporaryModel() usersStore.resetTemporaryModel()
} }
} }

View File

@ -14,19 +14,21 @@ import SortFilterProxyModel 0.2
MembersSelectorBase { MembersSelectorBase {
id: root id: root
limitReached: model.count >= membersLimit - 1 // -1 because creator is not on the list of members when creating chat
function cleanup() { function cleanup() {
root.edit.clear(); root.edit.clear()
d.selectedMembers.clear(); d.selectedMembers.clear()
} }
onEntryAccepted: { onEntryAccepted: if (suggestionsDelegate) {
if (root.limitReached) if (root.limitReached)
return return
if (d.addMember(suggestionsDelegate._pubKey, suggestionsDelegate.userName, suggestionsDelegate.isAdmin)) if (d.addMember(suggestionsDelegate._pubKey, suggestionsDelegate.userName, suggestionsDelegate.nickName))
root.edit.clear() root.edit.clear()
} }
onEntryRemoved: { onEntryRemoved: if (delegate) {
d.removeMember(delegate._pubKey) d.removeMember(delegate._pubKey)
} }
@ -40,10 +42,10 @@ MembersSelectorBase {
delegate: StatusTagItem { delegate: StatusTagItem {
readonly property string _pubKey: model.pubKey readonly property string _pubKey: model.pubKey
height: ListView.view.height height: ListView.view.height
text: model.displayName text: root.tagText(model.localNickname, model.displayName, model.alias)
isReadonly: model.isAdmin
icon: model.isAdmin ? "crown" : ""
onClicked: root.entryRemoved(this) onClicked: root.entryRemoved(this)
} }
@ -58,7 +60,7 @@ MembersSelectorBase {
root.rootStore.contactsStore.resolveENS(value) root.rootStore.contactsStore.resolveENS(value)
} }
function addMember(pubKey, displayName, isAdmin) { function addMember(pubKey, displayName, localNickname) {
for (let i = 0; i < d.selectedMembers.count; ++i) { for (let i = 0; i < d.selectedMembers.count; ++i) {
if (d.selectedMembers.get(i).pubKey === pubKey) if (d.selectedMembers.get(i).pubKey === pubKey)
return false return false
@ -67,7 +69,7 @@ MembersSelectorBase {
d.selectedMembers.append({ d.selectedMembers.append({
"pubKey": pubKey, "pubKey": pubKey,
"displayName": displayName, "displayName": displayName,
"isAdmin": isAdmin "localNickname": localNickname
}) })
return true return true
} }

View File

@ -21,7 +21,11 @@ InlineSelectorPanel {
property var rootStore property var rootStore
readonly property int membersLimit: 20 // see: https://github.com/status-im/status-mobile/issues/13066 readonly property int membersLimit: 20 // see: https://github.com/status-im/status-mobile/issues/13066
property bool limitReached: (model.count === (membersLimit-1)) property bool limitReached: model.count >= membersLimit
function tagText(localNickname, displayName, aliasName) {
return localNickname || displayName || aliasName
}
label.text: qsTr("To:") label.text: qsTr("To:")
warningLabel.text: qsTr("%1 USER LIMIT REACHED").arg(membersLimit) warningLabel.text: qsTr("%1 USER LIMIT REACHED").arg(membersLimit)
@ -32,8 +36,10 @@ InlineSelectorPanel {
sourceModel: root.rootStore.contactsModel sourceModel: root.rootStore.contactsModel
function searchPredicate(displayName) { function searchPredicate(displayName, localNickname, nameAlias) {
return displayName.toLowerCase().includes(root.edit.text.toLowerCase()) return displayName.toLowerCase().includes(root.edit.text.toLowerCase()) ||
localNickname.toLowerCase().includes(root.edit.text.toLowerCase()) ||
(!displayName && nameAlias.toLowerCase().includes(root.edit.text.toLowerCase()))
} }
function notAMemberPredicate(pubKey) { function notAMemberPredicate(pubKey) {
@ -49,7 +55,7 @@ InlineSelectorPanel {
enabled: root.edit.text !== "" enabled: root.edit.text !== ""
expression: { expression: {
root.edit.text // ensure expression is reevaluated when edit.text changes root.edit.text // ensure expression is reevaluated when edit.text changes
return _suggestionsModel.searchPredicate(model.displayName) return _suggestionsModel.searchPredicate(model.displayName, model.localNickname, model.alias)
} }
}, },
ExpressionFilter { ExpressionFilter {
@ -59,13 +65,22 @@ InlineSelectorPanel {
} }
} }
] ]
proxyRoles: ExpressionRole {
name: "title"
expression: model.localNickname || model.displayName || model.alias
}
sorters: StringSorter { sorters: StringSorter {
roleName: "displayName" roleName: "title"
numericMode: true
} }
} }
suggestionsDelegate: ContactListItemDelegate { suggestionsDelegate: ContactListItemDelegate {
highlighted: ListView.isCurrentItem highlighted: ListView.isCurrentItem
height: root.suggestionsDelegateSize.height
width: root.suggestionsDelegateSize.width
onClicked: root.entryAccepted(this) onClicked: root.entryAccepted(this)
} }

View File

@ -1 +1,3 @@
CreateChatView 1.0 CreateChatView.qml CreateChatView 1.0 CreateChatView.qml
MembersSelectorView 1.0 MembersSelectorView.qml
MembersEditSelectorView 1.0 MembersEditSelectorView.qml

View File

@ -18,7 +18,7 @@ StatusMemberListItem {
pubKey: hasEnsName ? "" : Utils.getCompressedPk(model.pubKey) pubKey: hasEnsName ? "" : Utils.getCompressedPk(model.pubKey)
nickName: model.localNickname nickName: model.localNickname
userName: model.displayName userName: model.displayName || model.alias
isVerified: model.isVerified isVerified: model.isVerified
isUntrustworthy: model.isUntrustworthy isUntrustworthy: model.isUntrustworthy
isContact: model.isContact isContact: model.isContact