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" title: "HoldingsDropdown"
section: "Popups" section: "Popups"
} }
ListElement {
title: "MembersDropdown"
section: "Popups"
}
ListElement { ListElement {
title: "InDropdown" title: "InDropdown"
section: "Popups" 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=448%3A36296",
"https://www.figma.com/file/idUoxN7OIW2Jpp3PMJ1Rl8/%E2%9A%99%EF%B8%8F-Settings-%7C-Desktop?node-id=1573%3A296338" "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": [ "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%3A415655",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=8159%3A415935" "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%3A489607",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2975%3A492910" "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": [ "DidYouKnowSplashScreen": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba⎜Desktop?node-id=25878%3A518438&t=C7xTpNib38t7s7XU-4" "https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba⎜Desktop?node-id=25878%3A518438&t=C7xTpNib38t7s7XU-4"
], ],
@ -118,6 +124,10 @@
"LoginView": [ "LoginView": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=1080%3A313192" "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": [ "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=13200-352357&t=jKciSCy3BVlrZmBs-0",
"https://www.figma.com/file/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=13185-350333&t=b2AclcJgxjXDL6Wl-0", "https://www.figma.com/file/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=13185-350333&t=b2AclcJgxjXDL6Wl-0",
@ -172,11 +182,5 @@
"StatusCommunityCard": [ "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%3A416159",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=8159%3A416160" "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 visible: statesStack.size > 1
spacing: 0 spacing: 0
leftPadding: 4 leftPadding: 4
statusIcon: "next" statusIcon: "previous"
icon.width: 12 icon.width: 12
icon.height: 12 icon.height: 12
iconRotation: 180
text: qsTr("Back") 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 HoldingsDropdown 1.0 HoldingsDropdown.qml
InDropdown 1.0 InDropdown.qml InDropdown 1.0 InDropdown.qml
InlineNetworksComboBox 1.0 InlineNetworksComboBox.qml InlineNetworksComboBox 1.0 InlineNetworksComboBox.qml
MembersDropdown 1.0 MembersDropdown.qml
MembersSelectorPanel 1.0 MembersSelectorPanel.qml MembersSelectorPanel 1.0 MembersSelectorPanel.qml
PermissionItem 1.0 PermissionItem.qml PermissionItem 1.0 PermissionItem.qml
PermissionsDropdown 1.0 PermissionsDropdown.qml PermissionsDropdown 1.0 PermissionsDropdown.qml

View File

@ -23,13 +23,6 @@ Item {
readonly property alias count: contactGridView.count 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 { StatusGridView {
id: contactGridView id: contactGridView
anchors.fill: parent anchors.fill: parent