feat(storybook): add MembersSelector to storybook
closes: #8178 fixes: #8210
This commit is contained in:
parent
d26ca0baf9
commit
bd7f890cce
|
@ -68,6 +68,9 @@ ApplicationWindow {
|
||||||
ListElement {
|
ListElement {
|
||||||
title: "ProfileFetchingView"
|
title: "ProfileFetchingView"
|
||||||
}
|
}
|
||||||
|
ListElement {
|
||||||
|
title: "MembersSelector"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SplitView {
|
SplitView {
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
CreateChatView 1.0 CreateChatView.qml
|
CreateChatView 1.0 CreateChatView.qml
|
||||||
|
MembersSelectorView 1.0 MembersSelectorView.qml
|
||||||
|
MembersEditSelectorView 1.0 MembersEditSelectorView.qml
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue