feat(Community/Airdrop): MembersDropdown

Closes: #9800
This commit is contained in:
Michał Cieślak 2023-04-17 11:35:44 +02:00 committed by Michał
parent c867e8f98c
commit 9fe3456e8f
7 changed files with 565 additions and 15 deletions

View File

@ -145,6 +145,10 @@ ListModel {
title: "HoldingsDropdown"
section: "Popups"
}
ListElement {
title: "MembersDropdown"
section: "Popups"
}
ListElement {
title: "InDropdown"
section: "Popups"

View File

@ -24,6 +24,9 @@
"https://www.figma.com/file/idUoxN7OIW2Jpp3PMJ1Rl8/%E2%9A%99%EF%B8%8F-Settings-%7C-Desktop?node-id=448%3A36296",
"https://www.figma.com/file/idUoxN7OIW2Jpp3PMJ1Rl8/%E2%9A%99%EF%B8%8F-Settings-%7C-Desktop?node-id=1573%3A296338"
],
"ChatAnchorButtonsPanel": [
"https://www.figma.com/file/Mr3rqxxgKJ2zMQ06UAKiWL/%F0%9F%92%AC-Chat%E2%8E%9CDesktop?node-id=14632-460085&t=SGTU2JeRA8ifbv2E-0"
],
"CommunitiesPortalLayout": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=8159%3A415655",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=8159%3A415935"
@ -76,6 +79,9 @@
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2975%3A489607",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2975%3A492910"
],
"DerivationPathInput": [
"https://www.figma.com/file/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=12272%3A269692&t=YiipgcxOhdOvqprP-0"
],
"DidYouKnowSplashScreen": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba⎜Desktop?node-id=25878%3A518438&t=C7xTpNib38t7s7XU-4"
],
@ -118,6 +124,10 @@
"LoginView": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=1080%3A313192"
],
"MembersDropdown": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22647-498410",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22642-497015"
],
"NetworkSelectPopup": [
"https://www.figma.com/file/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=13200-352357&t=jKciSCy3BVlrZmBs-0",
"https://www.figma.com/file/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=13185-350333&t=b2AclcJgxjXDL6Wl-0",
@ -172,11 +182,5 @@
"StatusCommunityCard": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=8159%3A416159",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=8159%3A416160"
],
"ChatAnchorButtonsPanel": [
"https://www.figma.com/file/Mr3rqxxgKJ2zMQ06UAKiWL/%F0%9F%92%AC-Chat%E2%8E%9CDesktop?node-id=14632-460085&t=SGTU2JeRA8ifbv2E-0"
],
"DerivationPathInput": [
"https://www.figma.com/file/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=12272%3A269692&t=YiipgcxOhdOvqprP-0"
]
}

View File

@ -0,0 +1,315 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml 2.15
import AppLayouts.Chat.controls.community 1.0
import utils 1.0
import SortFilterProxyModel 0.2
import Storybook 1.0
SplitView {
id: root
property bool globalUtilsReady: false
property bool mainModuleReady: false
orientation: Qt.Vertical
Logs { id: logs }
QtObject {
function isCompressedPubKey(publicKey) {
return true
}
function getColorId(publicKey) {
return Math.floor(Math.random() * 10)
}
Component.onCompleted: {
Utils.globalUtilsInst = this
globalUtilsReady = true
}
Component.onDestruction: {
globalUtilsReady = false
Utils.globalUtilsInst = {}
}
}
QtObject {
function getContactDetailsAsJson() {
return JSON.stringify({ ensVerified: true })
}
Component.onCompleted: {
mainModuleReady = true
Utils.mainModuleInst = this
}
Component.onDestruction: {
mainModuleReady = false
Utils.mainModuleInst = {}
}
}
ListModel {
id: members
property int counter: 0
function addMember() {
const i = counter++
const key = `pub_key_${i}`
const firstLetters = ["a", "b", "c", "d"]
const firstLetterIdx = Math.min(Math.floor(i / firstLetters.length),
firstLetters.length - 1)
const firstLetter = firstLetters[firstLetterIdx]
append({
alias: "",
colorId: "1",
displayName: `${firstLetter}contact ${i}`,
ensName: "",
icon: "",
isContact: true,
localNickname: "",
onlineStatus: 1,
pubKey: key,
isVerified: true,
isUntrustworthy: false
})
}
Component.onCompleted: {
for (let i = 0; i < 33; i++)
addMember()
}
}
Pane {
id: container
SplitView.fillWidth: true
SplitView.fillHeight: true
Rectangle {
id: startRect
border.color: "green"
color: "lightgreen"
border.width: 3
width: 50
height: width
x: 70
y: 70
radius: width / 2
Drag.active: dragArea.drag.active
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
}
}
Loader {
id: loader
anchors.centerIn: parent
active: globalUtilsReady && mainModuleReady
sourceComponent: MembersDropdown {
id: membersDropdown
closePolicy: Popup.NoAutoClose
model: SortFilterProxyModel {
Binding on sourceModel {
when: globalUtilsReady && mainModuleReady
value: members
restoreMode: Binding.RestoreBindingOrValue
}
filters: [
ExpressionFilter {
enabled: membersDropdown.searchText !== ""
function matchesAlias(name, filter) {
return name.split(" ").some(p => p.startsWith(filter))
}
expression: {
membersDropdown.selectedKeys
membersDropdown.searchText
if (membersDropdown.selectedKeys.indexOf(model.pubKey) > -1)
return true
const filter = membersDropdown.searchText.toLowerCase()
return matchesAlias(model.alias.toLowerCase(), filter)
|| model.displayName.toLowerCase().includes(filter)
|| model.ensName.toLowerCase().includes(filter)
|| model.localNickname.toLowerCase().includes(filter)
|| model.pubKey.toLowerCase().includes(filter)
}
}
]
}
onBackButtonClicked: {
logs.logEvent("MembersDropdown::backButtonClicked")
}
onAddButtonClicked: {
logs.logEvent("MembersDropdown::addButtonClicked, keys: "
+ membersDropdown.selectedKeys)
}
Component.onCompleted: open()
}
}
}
LogsAndControlsPanel {
SplitView.minimumHeight: 100
SplitView.preferredHeight: 250
logsView.logText: logs.logText
Loader {
active: loader.item
anchors.left: parent.left
anchors.right: parent.right
sourceComponent: ColumnLayout {
readonly property MembersDropdown membersDropdown: loader.item
RowLayout {
Label {
text: "maximum list height:"
}
Slider {
id: maxListHeightSlider
from: 100
to: 500
stepSize: 1
Component.onCompleted: {
value = membersDropdown.maximumListHeight
membersDropdown.maximumListHeight
= Qt.binding(() => value)
}
}
Label {
text: maxListHeightSlider.value
}
}
RowLayout {
Label {
text: "margins:"
}
Slider {
id: marginsSlider
from: -1
to: 50
stepSize: 1
Component.onCompleted: {
value = membersDropdown.margins
membersDropdown.margins = Qt.binding(() => value)
}
}
Label {
text: marginsSlider.value
}
}
RowLayout {
Label {
text: "bottom inset:"
}
Slider {
id: bottomInsetSlider
from: 0
to: 50
stepSize: 1
Component.onCompleted: {
value = membersDropdown.bottomInset
membersDropdown.bottomInset = Qt.binding(() => value)
}
}
Label {
text: bottomInsetSlider.value
}
}
RowLayout {
RadioButton {
id: anchorToItemRadioButton
text: "anchor to item"
checked: true
}
RadioButton {
id: anchorToOverlayRadioButton
text: "anchor to overlay"
}
Binding {
target: membersDropdown
property: "parent"
value: anchorToItemRadioButton.checked
? startRect : membersDropdown.Overlay.overlay
}
Binding {
target: membersDropdown.anchors
when: anchorToOverlayRadioButton.checked
property: "centerIn"
value: membersDropdown.parent
restoreMode: Binding.RestoreBindingOrValue
}
Binding {
target: membersDropdown
property: "x"
value: anchorToItemRadioButton.checked
? startRect.width / 2 : 0
}
Binding {
target: membersDropdown
property: "y"
value: anchorToItemRadioButton.checked
? startRect.height / 2 : 0
}
}
Label {
Layout.fillWidth: true
text: `selected members: ${membersDropdown.selectedKeys}`
wrapMode: Label.Wrap
}
}
}
}
}

View File

@ -166,10 +166,9 @@ StatusDropdown {
visible: statesStack.size > 1
spacing: 0
leftPadding: 4
statusIcon: "next"
statusIcon: "previous"
icon.width: 12
icon.height: 12
iconRotation: 180
text: qsTr("Back")
}

View File

@ -0,0 +1,234 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Utils 0.1
import shared.controls 1.0
import shared.controls.delegates 1.0
StatusDropdown {
id: root
property var selectedKeys: []
property int maximumListHeight: 288
property alias model: listView.model
readonly property alias count: listView.count
readonly property alias searchText: filterInput.text
property bool fixedYPosition: !anchors.centerIn && margins < 0
signal backButtonClicked
signal addButtonClicked
width: 295
height: Math.min(
d.availableExternalHeight,
content.requestedHeight + d.vPadding)
padding: 11
bottomInset: 10
bottomPadding: padding + bottomInset
QtObject {
id: d
readonly property int sectionDelegateHeight: 40
readonly property int delegateHeight: 47
readonly property int vPadding: root.topPadding + root.bottomPadding
readonly property int availableExternalHeight:
(root.Overlay.overlay ? root.Overlay.overlay.height : 0) - root.bottomMargin -
(root.fixedYPosition ? contentItem.parent.y : root.topMargin)
}
contentItem: ColumnLayout {
id: content
spacing: 8
height: root.availableHeight
clip: true
readonly property int requestedHeight:
backButton.height +
spacing + filterInput.height +
spacing + (listView.count
? Math.min(listView.contentHeight, root.maximumListHeight)
: noContactsText.Layout.preferredHeight) +
spacing + addButton.height
StatusIconTextButton {
id: backButton
Layout.preferredHeight: 48
Layout.maximumWidth: root.availableWidth
spacing: 0
leftPadding: 4
statusIcon: "previous"
icon.width: 12
icon.height: 12
text: qsTr("Back")
onClicked: root.backButtonClicked()
}
SearchBox {
id: filterInput
Layout.fillWidth: true
placeholderText: qsTr("Search members")
maximumHeight: 36
topPadding: 0
bottomPadding: 0
input.asset.width: 15
input.asset.height: 15
input.leftPadding: 13
input.font.pixelSize: 13
input.placeholder.font.pixelSize: 13
}
StatusBaseText {
id: noContactsText
Layout.fillWidth: true
Layout.preferredHeight: 50
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
visible: listView.count === 0
text: qsTr("No contacts found")
color: Theme.palette.baseColor1
font.pixelSize: Theme.tertiaryTextFontSize
elide: Text.ElideRight
lineHeight: 1.2
}
StatusListView {
id: listView
Layout.fillWidth: true
Layout.fillHeight: true
visible: count > 0
header: StatusCheckBox {
width: ListView.view.width
text: qsTr("Select all")
font.weight: Font.Medium
checked: root.selectedKeys.length === listView.count
leftSide: false
size: StatusCheckBox.Size.Small
indicator.anchors.rightMargin: 12
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
if (listView.headerItem.checked) {
root.selectedKeys = []
return
}
const count = root.model.rowCount()
const keys = []
for (let i = 0; i < count; i++) {
const key = ModelUtils.get(root.model, i, "pubKey")
keys.push(key)
}
root.selectedKeys = keys
}
}
}
delegate: ContactListItemDelegate {
id: delegateRoot
width: ListView.view.width
height: d.delegateHeight
asset.width: 29
asset.height: 29
rightPadding: 0
leftPadding: 6
color: "transparent"
onClicked: {
const index = root.selectedKeys.indexOf(model.pubKey)
const selectedKeysCopy = Object.assign(
[], root.selectedKeys)
if (index === -1)
selectedKeysCopy.push(model.pubKey)
else
selectedKeysCopy.splice(index, 1)
root.selectedKeys = selectedKeysCopy
}
components: [
StatusCheckBox {
id: contactCheckbox
size: StatusCheckBox.Size.Small
checked: root.selectedKeys.indexOf(model.pubKey) > -1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: delegateRoot.clicked(
delegateRoot.itemId, mouse)
}
}
]
}
section.property: "displayName"
section.criteria: ViewSection.FirstCharacter
section.delegate: StatusBaseText {
text: section.toUpperCase()
width: ListView.view.width
height: d.sectionDelegateHeight
padding: 5
verticalAlignment: Qt.AlignVCenter
color: Theme.palette.baseColor1
font.pixelSize: Theme.tertiaryTextFontSize
}
}
StatusButton {
id: addButton
Layout.fillWidth: true
textFillWidth: true
enabled: root.selectedKeys.length > 0
text: enabled
? qsTr("Add %n member(s)", "", root.selectedKeys.length)
: qsTr("Add")
onClicked: root.addButtonClicked()
}
}
}

View File

@ -8,6 +8,7 @@ HoldingTypes 1.0 HoldingTypes.qml
HoldingsDropdown 1.0 HoldingsDropdown.qml
InDropdown 1.0 InDropdown.qml
InlineNetworksComboBox 1.0 InlineNetworksComboBox.qml
MembersDropdown 1.0 MembersDropdown.qml
MembersSelectorPanel 1.0 MembersSelectorPanel.qml
PermissionItem 1.0 PermissionItem.qml
PermissionsDropdown 1.0 PermissionsDropdown.qml

View File

@ -23,13 +23,6 @@ Item {
readonly property alias count: contactGridView.count
signal contactClicked(var contact)
function matchesAlias(name, filter) {
let parts = name.split(" ")
return parts.some(p => p.startsWith(filter))
}
StatusGridView {
id: contactGridView
anchors.fill: parent