feat(StatusMemberListItem): Implement `StatusMemberListItem` (#539)

Create component StatusMemberListItem.

Add StatusMemberListItem component in sandbox\controls\ListItems for testing all its variants and demo app.

Add new properties in StatusListItem.

Reorganize StatusListItem.qml following Qt conventions.

Add badge in StatusSmartIndenticon component that allows configure a colored state.

Closes #515
This commit is contained in:
Noelia 2022-01-28 15:29:29 +01:00 committed by Michał Cieślak
parent a400a4c6a3
commit 2e11b7f203
8 changed files with 270 additions and 56 deletions

View File

@ -345,4 +345,38 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
title: "Contact requests" title: "Contact requests"
requestsCount: 3 requestsCount: 3
} }
StatusMemberListItem {
nickName: "This is an example"
userName: "annabelle"
chatKey: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
trustIndicator: StatusMemberListItem.TrustedType.Verified
isMutualContact: true
image.source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
image.isIdenticon: true
isOnline: true
}
StatusMemberListItem {
nickName: "carmen.eth"
isOnline: false
trustIndicator: StatusMemberListItem.TrustedType.Untrustworthy
}
StatusMemberListItem {
nickName: "This girl I know from work"
userName: "annabelle"
isOnline: true
}
StatusMemberListItem {
nickName: "Mark Cuban"
userName: "annabelle"
chatKey: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
isMutualContact: true
image.source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
image.isIdenticon: true
}
} }

View File

@ -257,26 +257,17 @@ StatusAppThreePanelLayout {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: 16 anchors.bottomMargin: 16
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
model: ["John", "Nick", "Maria", "Mike"] model: Models.membersListModel
delegate: Row { delegate: StatusMemberListItem {
width: parent.width implicitWidth: parent.width
height: 30 nickName: model.nickName
spacing: 8 userName: model.userName
Rectangle { chatKey: model.chatKey
width: 24 trustIndicator: model.trustIndicator
height: 24 isMutualContact: model.isMutualContact
radius: width/2 image.source: model.source
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 255) image.isIdenticon: model.isIdenticon
} isOnline: model.isOnline
StatusBaseText {
height: parent.height
horizontalAlignment: Text.AlignHCenter
opacity: (rightPanel.width > 50) ? 1.0 : 0.0
visible: (opacity > 0.1)
font.pixelSize: 15
color: Theme.palette.directColor1
text: modelData
}
} }
} }
} }

View File

@ -730,4 +730,43 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
hasExpired: false hasExpired: false
} }
} }
property var membersListModel: ListModel {
id: membersList
ListElement {
nickName: "This is an example"
userName: "annabelle"
chatKey: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
trustIndicator: StatusMemberListItem.TrustedType.Verified
isMutualContact: true
isOnline: true
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
isIdenticon: true
}
ListElement {
nickName: "carmen.eth"
trustIndicator: StatusMemberListItem.TrustedType.Untrustworthy
isOnline: false
}
ListElement {
nickName: "This girl I know from work"
userName: "annabelle"
isOnline: true
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzXUQpAQBRGYWQvLNAyLJDV8C5qpiGnv/M9al5Ot27X0IUwhMYQGkNoDKGJCRlLH67bftx9X+ap/+P9VcxEDK
ExhKZ4a9Uq3TZviZmIITSG0DRvlqcbqVbrlouZiCE0htD4h0hjCI0hNN5aNIbQGKKPxEzEEBpDaAyhMYTmDAAA//+gYCErzmCpCQAAAABJRU5ErkJggg=="
isIdenticon: true
}
ListElement {
nickName: "Mark Cuban"
userName: "annabelle"
chatKey: "0x043a7ed0e8752236a4688563652fd0296453cef00a5dcddbe252dc74f72cc1caa97a2b65e4a1a52d9c30a84c9966beaaaf6b333d659cbdd2e486b443ed1012cf04"
trustIndicator: StatusMemberListItem.TrustedType.Untrustworthy
isMutualContact: true
isOnline: false
source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
isIdenticon: true
}
}
} }

View File

@ -9,43 +9,21 @@ import StatusQ.Controls 0.1
Rectangle { Rectangle {
id: statusListItem id: statusListItem
implicitWidth: 448
implicitHeight: Math.max(64, statusListItemTitleArea.height + 16)
enum Type {
Primary,
Secondary,
Danger
}
color: {
if (sensor.containsMouse) {
switch(type) {
case StatusListItem.Type.Primary:
return Theme.palette.baseColor2
case StatusListItem.Type.Secondary:
return Theme.palette.statusListItem.secondaryHoverBackgroundColor
case StatusListItem.Type.Danger:
return Theme.palette.dangerColor3
}
}
return Theme.palette.statusListItem.backgroundColor
}
radius: 8
property string itemId: "" property string itemId: ""
property string titleId: "" property string titleId: ""
property string title: "" property string title: ""
property string titleAsideText: "" property string titleAsideText: ""
property bool titleIcon1Visible
property bool titleIcon2Visible
property string subTitle: "" property string subTitle: ""
property string tertiaryTitle: "" property string tertiaryTitle: ""
property alias badge: statusListItemBadge property string label: ""
property real leftPadding: 16 property real leftPadding: 16
property real rightPadding: 16 property real rightPadding: 16
property bool enabled: true property bool enabled: true
property int type: StatusListItem.Type.Primary
property list<Item> components
property StatusIconSettings icon: StatusIconSettings { property StatusIconSettings icon: StatusIconSettings {
height: isLetterIdenticon ? 40 : 20 height: isLetterIdenticon ? 40 : 20
width: isLetterIdenticon ? 40 : 20 width: isLetterIdenticon ? 40 : 20
@ -73,24 +51,69 @@ Rectangle {
height: 40 height: 40
isIdenticon: false isIdenticon: false
} }
property string label: "" property StatusIconSettings titleIcon1: StatusIconSettings {
width: dummyImage.width
property int type: StatusListItem.Type.Primary height: dummyImage.height
background: StatusIconBackgroundSettings {
width: 10
height: 10
}
// Only used to get implicit width and height from the actual image
property Image dummyImage: Image {
source: titleIcon1.name ? "../../assets/img/icons/" + titleIcon1.name + ".svg": ""
visible: false
}
}
property StatusIconSettings titleIcon2: StatusIconSettings {
width: dummyImage.width
height: dummyImage.height
background: StatusIconBackgroundSettings {
width: 10
height: 10
}
// Only used to get implicit width and height from the actual image
property Image dummyImage: Image {
source: titleIcon2.name ? "../../assets/img/icons/" + titleIcon2.name + ".svg": ""
visible: false
}
}
property alias sensor: sensor property alias sensor: sensor
property alias badge: statusListItemBadge
property alias statusListItemIcon: iconOrImage property alias statusListItemIcon: iconOrImage
property alias statusListItemTitle: statusListItemTitle property alias statusListItemTitle: statusListItemTitle
property alias statusListItemTitleAside: statusListItemTitleAsideText property alias statusListItemTitleAside: statusListItemTitleAsideText
property alias statusListItemTitleArea: statusListItemTitleArea
property alias statusListItemSubTitle: statusListItemSubTitle property alias statusListItemSubTitle: statusListItemSubTitle
property alias statusListItemTertiaryTitle: statusListItemTertiaryTitle property alias statusListItemTertiaryTitle: statusListItemTertiaryTitle
property alias statusListItemComponentsSlot: statusListItemComponentsSlot property alias statusListItemComponentsSlot: statusListItemComponentsSlot
property list<Item> components
signal clicked(string itemId) signal clicked(string itemId)
signal titleClicked(string titleId) signal titleClicked(string titleId)
enum Type {
Primary,
Secondary,
Danger
}
implicitWidth: 448
implicitHeight: Math.max(64, statusListItemTitleArea.height + 16)
color: {
if (sensor.containsMouse) {
switch(type) {
case StatusListItem.Type.Primary:
return Theme.palette.baseColor2
case StatusListItem.Type.Secondary:
return Theme.palette.statusListItem.secondaryHoverBackgroundColor
case StatusListItem.Type.Danger:
return Theme.palette.dangerColor3
}
}
return Theme.palette.statusListItem.backgroundColor
}
radius: 8
onComponentsChanged: { onComponentsChanged: {
if (components.length) { if (components.length) {
for (let idx in components) { for (let idx in components) {
@ -125,6 +148,7 @@ Rectangle {
active: statusListItem.icon.isLetterIdenticon || active: statusListItem.icon.isLetterIdenticon ||
!!statusListItem.icon.name || !!statusListItem.icon.name ||
!!statusListItem.image.source.toString() !!statusListItem.image.source.toString()
badge.border.color: statusListItem.color
} }
Item { Item {
@ -144,7 +168,8 @@ Rectangle {
height: visible ? contentHeight : 0 height: visible ? contentHeight : 0
wrapMode: Text.WrapAtWordBoundaryOrAnywhere wrapMode: Text.WrapAtWordBoundaryOrAnywhere
anchors.left: parent.left anchors.left: parent.left
anchors.right: !statusListItem.titleAsideText ? parent.right : undefined anchors.right: !statusListItem.titleAsideText && !statusListItem.titleIcon1Visible && !statusListItem.titleIcon2Visible
? parent.right : undefined
color: { color: {
if (!statusListItem.enabled) { if (!statusListItem.enabled) {
return Theme.palette.baseColor1 return Theme.palette.baseColor1
@ -182,6 +207,38 @@ Rectangle {
visible: !!statusListItem.titleAsideText visible: !!statusListItem.titleAsideText
} }
Row {
id: titleIconsRow
spacing: 4
anchors.left: !statusListItem.titleAsideText ? statusListItemTitle.right : statusListItemTitleAsideText.right
anchors.verticalCenter: statusListItemTitle.verticalCenter
anchors.leftMargin: titleIconsRow.spacing
StatusRoundIcon {
visible: statusListItem.titleIcon1Visible
icon.name: statusListItem.titleIcon1.name
icon.width: statusListItem.titleIcon1.width
icon.height: statusListItem.titleIcon1.height
icon.rotation: statusListItem.titleIcon1.rotation
icon.color: statusListItem.titleIcon1.color
icon.background.color: statusListItem.titleIcon1.background.color
icon.background.width: statusListItem.titleIcon1.background.width
icon.background.height: statusListItem.titleIcon1.background.height
}
StatusRoundIcon {
visible: statusListItem.titleIcon2Visible
icon.name: statusListItem.titleIcon2.name
icon.width: statusListItem.titleIcon2.width
icon.height: statusListItem.titleIcon2.height
icon.rotation: statusListItem.titleIcon2.rotation
icon.color: statusListItem.titleIcon2.color
icon.background.color: statusListItem.titleIcon2.background.color
icon.background.width: statusListItem.titleIcon2.background.width
icon.background.height: statusListItem.titleIcon2.background.height
}
}
StatusBaseText { StatusBaseText {
id: statusListItemSubTitle id: statusListItemSubTitle
anchors.top: statusListItemTitle.bottom anchors.top: statusListItemTitle.bottom

View File

@ -0,0 +1,76 @@
import QtQuick 2.0
import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1
StatusListItem {
id: root
property string nickName: ""
property string userName: ""
property string chatKey: ""
property bool isMutualContact: false
property var trustIndicator: StatusMemberListItem.TrustedType.None
property bool isOnline: false
enum TrustedType {
None, //0
Verified, //1
Untrustworthy //2
}
// Subtitle composition:
function composeSubtitile() {
var compose = ""
if(root.userName !== "")
compose = "(" + root.userName + ")"
if(compose !== "" && root.chatKey !== "")
// Composition
compose += " • " + composeShortKeyChat(root.chatKey)
else if(root.chatKey !== "")
compose = composeShortKeyChat(root.chatKey)
return compose
}
// Short keychat composition:
function composeShortKeyChat(chatKey) {
return chatKey.substring(0, 5) + "..." + chatKey.substring(chatKey.length - 3)
}
// root object settings:
title: root.nickName
titleIcon1Visible: root.isMutualContact
titleIcon2Visible: root.trustIndicator !== StatusMemberListItem.TrustedType.None
subTitle: composeSubtitile()
statusListItemSubTitle.font.pixelSize: 10
icon.isLetterIdenticon: !root.image.source.toString()
statusListItemIcon.badge.visible: true
statusListItemIcon.badge.color: root.isOnline ? Theme.palette.successColor1 : Theme.palette.baseColor1
color: sensor.containsMouse ? Theme.palette.baseColor2 : Theme.palette.baseColor4
// Default sizes/positions by design
implicitWidth: 256
implicitHeight: Math.max(56, statusListItemTitleArea.height + leftPadding)
leftPadding: 8
image.width: 32
image.height: 32
icon.width: 32
icon.height: 32
statusListItemIcon.anchors.verticalCenter: sensor.verticalCenter
statusListItemIcon.anchors.top: undefined
statusListItemIcon.badge.border.width: 2
statusListItemIcon.badge.implicitHeight: 12 // 8 px + 2 px * 2 borders
statusListItemIcon.badge.implicitWidth: 12 // 8 px + 2 px * 2 borders
// Trusted type icons definition:
titleIcon1.name: "tiny/tiny-contact"
titleIcon1.color: Theme.palette.indirectColor1
titleIcon1.background.color: Theme.palette.primaryColor1
// None and Untrustworthy types, same aspect (Icon will not be visible in case of None type):
titleIcon2.name: trustIndicator === StatusMemberListItem.TrustedType.Verified ? "tiny/tiny-checkmark" : "tiny/subtract"
titleIcon2.color: Theme.palette.indirectColor1
titleIcon2.background.color: trustIndicator === StatusMemberListItem.TrustedType.Verified ? Theme.palette.primaryColor1 : Theme.palette.dangerColor1
}

View File

@ -7,6 +7,9 @@ Loader {
property string name: "" property string name: ""
// Badge color properties must be set if badgeItem.visible = true
property alias badge: statusBadge
property StatusIconSettings icon: StatusIconSettings { property StatusIconSettings icon: StatusIconSettings {
width: 40 width: 40
height: 40 height: 40
@ -75,4 +78,16 @@ Loader {
letterSize: statusSmartIdenticon.icon.letterSize letterSize: statusSmartIdenticon.icon.letterSize
} }
} }
// State component
StatusBadge {
id: statusBadge
visible: false
anchors.bottom: statusSmartIdenticon.bottom
anchors.right: statusSmartIdenticon.right
border.width: 3
implicitHeight: 15
implicitWidth: 15
z: 100
}
} }

View File

@ -15,6 +15,7 @@ StatusLetterIdenticon 0.1 StatusLetterIdenticon.qml
StatusListItem 0.1 StatusListItem.qml StatusListItem 0.1 StatusListItem.qml
StatusListSectionHeadline 0.1 StatusListSectionHeadline.qml StatusListSectionHeadline 0.1 StatusListSectionHeadline.qml
StatusLoadingIndicator 0.1 StatusLoadingIndicator.qml StatusLoadingIndicator 0.1 StatusLoadingIndicator.qml
StatusMemberListItem 0.1 StatusMemberListItem.qml
StatusNavigationListItem 0.1 StatusNavigationListItem.qml StatusNavigationListItem 0.1 StatusNavigationListItem.qml
StatusNavigationPanelHeadline 0.1 StatusNavigationPanelHeadline.qml StatusNavigationPanelHeadline 0.1 StatusNavigationPanelHeadline.qml
StatusRoundIcon 0.1 StatusRoundIcon.qml StatusRoundIcon 0.1 StatusRoundIcon.qml

View File

@ -318,5 +318,6 @@
<file>src/StatusQ/Controls/StatusBanner.qml</file> <file>src/StatusQ/Controls/StatusBanner.qml</file>
<file>src/StatusQ/Controls/StatusProgressBar.qml</file> <file>src/StatusQ/Controls/StatusProgressBar.qml</file>
<file>src/StatusQ/Controls/StatusPasswordStrengthIndicator.qml</file> <file>src/StatusQ/Controls/StatusPasswordStrengthIndicator.qml</file>
<file>src/StatusQ/Components/StatusMemberListItem.qml</file>
</qresource> </qresource>
</RCC> </RCC>