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 {
title: "ProfileFetchingView"
}
ListElement {
title: "MembersSelector"
}
}
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: {
root.mainModuleReady = true
Utils.mainModuleInst = this
root.mainModuleReady = true
}
Component.onDestruction: {
root.mainModuleReady = false

View File

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

View File

@ -19,13 +19,12 @@ Item {
property alias suggestionsModel: suggestionsListView.model
property alias suggestionsDelegate: suggestionsListView.delegate
property size suggestionsDelegateSize: Qt.size(344, 64)
readonly property alias label: label
readonly property alias warningLabel: warningLabel
readonly property alias edit: edit
property bool confirmBtnEnabled: (listView.count > 0)
signal confirmed()
signal rejected()
@ -66,105 +65,109 @@ Item {
Item {
Layout.fillWidth: true
Layout.fillHeight: true
onWidthChanged: {
listView.positionViewAtEnd();
}
StatusScrollView {
id: scrollView
function positionViewAtEnd() {
if (scrollView.contentWidth > scrollView.width) {
scrollView.contentX = scrollView.contentWidth - scrollView.width
} else {
scrollView.contentX = 0
}
}
RowLayout {
anchors.fill: parent
Item {
//40 px least space for input
Layout.preferredWidth: (listView.contentWidth < (parent.width - 40)) ?
listView.contentWidth : (parent.width - 40)
Layout.preferredHeight: 44
padding: 0
onContentWidthChanged: positionViewAtEnd()
onWidthChanged: positionViewAtEnd()
RowLayout {
height: scrollView.height
StatusListView {
clip: true
id: listView
width: parent.width
height: 30
anchors.verticalCenter: parent.verticalCenter
Layout.fillWidth: true
Layout.preferredHeight: 30
implicitWidth: contentWidth
orientation: ListView.Horizontal
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: {
if (event.matches(StandardKey.Paste)) {
event.accepted = true
const previousText = text;
const previousSelectedText = selectedText;
paste()
if (previousText === "" || previousSelectedText.length === previousText.length)
root.textPasted(text)
return;
}
TextInput {
id: edit
Layout.minimumWidth: 4
Layout.fillHeight: true
verticalAlignment: Text.AlignVCenter
font.pixelSize: 15
color: Theme.palette.directColor1
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()
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; }
}
} else {
if (event.key === Qt.Key_Backspace && edit.text === "" && listView.count > 0) {
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();
}
Keys.onPressed: {
if (event.matches(StandardKey.Paste)) {
event.accepted = true
const previousText = text;
const previousSelectedText = selectedText;
paste()
if (previousText === "" || previousSelectedText.length === previousText.length)
root.textPasted(text)
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
Item {
Layout.fillHeight: true
implicitWidth: 1
ScrollBar.horizontal: StatusScrollBar {
id: scrollBar
parent: scrollView.parent
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 {
objectName: "inlineSelectorConfirmButton"
Layout.alignment: Qt.AlignVCenter
enabled: root.confirmBtnEnabled
enabled: (listView.count > 0)
text: qsTr("Confirm")
onClicked: root.confirmed()
}
@ -207,9 +210,9 @@ Item {
Popup {
id: suggestionsDialog
parent: edit
x: (parent.contentWidth - Style.current.halfPadding)
y: (parent.height + Style.current.halfPadding)
parent: scrollView
x: Math.min(parent.width, parent.contentWidth)
y: parent.height + Style.current.halfPadding
visible: edit.text !== ""
padding: Style.current.halfPadding
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 {
anchors.fill: parent
StatusBaseText {
id: noResultsFoundText
Layout.fillWidth: true
visible: root.suggestionsModel.count === 0
text: qsTr("No results found")
color: Theme.palette.baseColor1
@ -237,11 +249,22 @@ Item {
StatusListView {
id: suggestionsListView
Layout.fillWidth: true
Layout.fillHeight: true
visible: root.suggestionsModel.count
implicitWidth: contentItem.childrenRect.width
implicitHeight: contentItem.childrenRect.height
highlightMoveDuration: 0
highlightMoveVelocity: -1
verticalScrollBar {
visible: contentHeight > height
policy: ScrollBar.AlwaysOn
}
onVisibleChanged: currentIndex = 0
onCountChanged: currentIndex = 0
}
}
}

View File

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

View File

@ -4,11 +4,20 @@ QtObject {
id: root
property var usersModule
property var usersModel
onUsersModuleChanged: {
if(!usersModule)
return
root.usersModel = usersModule.model
readonly property var usersModel: usersModule ? usersModule.model : null
readonly property var temporaryModel: usersModule ? usersModule.temporaryModel : null
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
MembersEditSelectorView {
sectionModule: root.chatSectionModule
chatContentModule: root.chatContentModule
rootStore: root.rootStore
usersStore: UsersStore {
usersModule: root.chatContentModule.usersModule
}
onConfirmed: root.state = d.stateInfoButtonContent
onRejected: root.state = d.stateInfoButtonContent

View File

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

View File

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

View File

@ -21,7 +21,11 @@ InlineSelectorPanel {
property var rootStore
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:")
warningLabel.text: qsTr("%1 USER LIMIT REACHED").arg(membersLimit)
@ -32,8 +36,10 @@ InlineSelectorPanel {
sourceModel: root.rootStore.contactsModel
function searchPredicate(displayName) {
return displayName.toLowerCase().includes(root.edit.text.toLowerCase())
function searchPredicate(displayName, localNickname, nameAlias) {
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) {
@ -49,7 +55,7 @@ InlineSelectorPanel {
enabled: root.edit.text !== ""
expression: {
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 {
@ -59,13 +65,22 @@ InlineSelectorPanel {
}
}
]
proxyRoles: ExpressionRole {
name: "title"
expression: model.localNickname || model.displayName || model.alias
}
sorters: StringSorter {
roleName: "displayName"
roleName: "title"
numericMode: true
}
}
suggestionsDelegate: ContactListItemDelegate {
highlighted: ListView.isCurrentItem
height: root.suggestionsDelegateSize.height
width: root.suggestionsDelegateSize.width
onClicked: root.entryAccepted(this)
}

View File

@ -1 +1,3 @@
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)
nickName: model.localNickname
userName: model.displayName
userName: model.displayName || model.alias
isVerified: model.isVerified
isUntrustworthy: model.isUntrustworthy
isContact: model.isContact