From 2e11b7f203d0435511a8216a8a76b15aa520a9fb Mon Sep 17 00:00:00 2001 From: Noelia <97019400+noeliaSD@users.noreply.github.com> Date: Fri, 28 Jan 2022 15:29:29 +0100 Subject: [PATCH] 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 --- ui/StatusQ/sandbox/controls/ListItems.qml | 34 +++++ .../demoapp/StatusAppCommunityView.qml | 31 ++--- ui/StatusQ/sandbox/demoapp/data/Models.qml | 39 ++++++ .../src/StatusQ/Components/StatusListItem.qml | 129 +++++++++++++----- .../Components/StatusMemberListItem.qml | 76 +++++++++++ .../Components/StatusSmartIdenticon.qml | 15 ++ ui/StatusQ/src/StatusQ/Components/qmldir | 1 + ui/StatusQ/statusq.qrc | 1 + 8 files changed, 270 insertions(+), 56 deletions(-) create mode 100644 ui/StatusQ/src/StatusQ/Components/StatusMemberListItem.qml diff --git a/ui/StatusQ/sandbox/controls/ListItems.qml b/ui/StatusQ/sandbox/controls/ListItems.qml index 0d7ac34ff4..15fe32298b 100644 --- a/ui/StatusQ/sandbox/controls/ListItems.qml +++ b/ui/StatusQ/sandbox/controls/ListItems.qml @@ -345,4 +345,38 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I title: "Contact requests" 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 + } } diff --git a/ui/StatusQ/sandbox/demoapp/StatusAppCommunityView.qml b/ui/StatusQ/sandbox/demoapp/StatusAppCommunityView.qml index df49e54173..9c971ce651 100644 --- a/ui/StatusQ/sandbox/demoapp/StatusAppCommunityView.qml +++ b/ui/StatusQ/sandbox/demoapp/StatusAppCommunityView.qml @@ -257,26 +257,17 @@ StatusAppThreePanelLayout { anchors.bottom: parent.bottom anchors.bottomMargin: 16 boundsBehavior: Flickable.StopAtBounds - model: ["John", "Nick", "Maria", "Mike"] - delegate: Row { - width: parent.width - height: 30 - spacing: 8 - Rectangle { - width: 24 - height: 24 - radius: width/2 - color: Qt.rgba(Math.random(), Math.random(), Math.random(), 255) - } - 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 - } + model: Models.membersListModel + delegate: StatusMemberListItem { + implicitWidth: parent.width + nickName: model.nickName + userName: model.userName + chatKey: model.chatKey + trustIndicator: model.trustIndicator + isMutualContact: model.isMutualContact + image.source: model.source + image.isIdenticon: model.isIdenticon + isOnline: model.isOnline } } } diff --git a/ui/StatusQ/sandbox/demoapp/data/Models.qml b/ui/StatusQ/sandbox/demoapp/data/Models.qml index dcc5778c29..4c7213e57b 100644 --- a/ui/StatusQ/sandbox/demoapp/data/Models.qml +++ b/ui/StatusQ/sandbox/demoapp/data/Models.qml @@ -730,4 +730,43 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I 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 + } + } } diff --git a/ui/StatusQ/src/StatusQ/Components/StatusListItem.qml b/ui/StatusQ/src/StatusQ/Components/StatusListItem.qml index 07be3f7778..bbcabb1330 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusListItem.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusListItem.qml @@ -9,43 +9,21 @@ import StatusQ.Controls 0.1 Rectangle { 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 titleId: "" - property string title: "" property string titleAsideText: "" + property bool titleIcon1Visible + property bool titleIcon2Visible property string subTitle: "" - property string tertiaryTitle: "" - property alias badge: statusListItemBadge - + property string tertiaryTitle: "" + property string label: "" property real leftPadding: 16 property real rightPadding: 16 property bool enabled: true + property int type: StatusListItem.Type.Primary + property list components + property StatusIconSettings icon: StatusIconSettings { height: isLetterIdenticon ? 40 : 20 width: isLetterIdenticon ? 40 : 20 @@ -73,24 +51,69 @@ Rectangle { height: 40 isIdenticon: false } - property string label: "" - - property int type: StatusListItem.Type.Primary + property StatusIconSettings titleIcon1: 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: 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 badge: statusListItemBadge property alias statusListItemIcon: iconOrImage property alias statusListItemTitle: statusListItemTitle property alias statusListItemTitleAside: statusListItemTitleAsideText + property alias statusListItemTitleArea: statusListItemTitleArea property alias statusListItemSubTitle: statusListItemSubTitle property alias statusListItemTertiaryTitle: statusListItemTertiaryTitle property alias statusListItemComponentsSlot: statusListItemComponentsSlot - property list components - signal clicked(string itemId) 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: { if (components.length) { for (let idx in components) { @@ -125,6 +148,7 @@ Rectangle { active: statusListItem.icon.isLetterIdenticon || !!statusListItem.icon.name || !!statusListItem.image.source.toString() + badge.border.color: statusListItem.color } Item { @@ -144,7 +168,8 @@ Rectangle { height: visible ? contentHeight : 0 wrapMode: Text.WrapAtWordBoundaryOrAnywhere anchors.left: parent.left - anchors.right: !statusListItem.titleAsideText ? parent.right : undefined + anchors.right: !statusListItem.titleAsideText && !statusListItem.titleIcon1Visible && !statusListItem.titleIcon2Visible + ? parent.right : undefined color: { if (!statusListItem.enabled) { return Theme.palette.baseColor1 @@ -182,6 +207,38 @@ Rectangle { 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 { id: statusListItemSubTitle anchors.top: statusListItemTitle.bottom diff --git a/ui/StatusQ/src/StatusQ/Components/StatusMemberListItem.qml b/ui/StatusQ/src/StatusQ/Components/StatusMemberListItem.qml new file mode 100644 index 0000000000..8111ba410c --- /dev/null +++ b/ui/StatusQ/src/StatusQ/Components/StatusMemberListItem.qml @@ -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 +} diff --git a/ui/StatusQ/src/StatusQ/Components/StatusSmartIdenticon.qml b/ui/StatusQ/src/StatusQ/Components/StatusSmartIdenticon.qml index c058af76cf..9e053b6951 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusSmartIdenticon.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusSmartIdenticon.qml @@ -7,6 +7,9 @@ Loader { property string name: "" + // Badge color properties must be set if badgeItem.visible = true + property alias badge: statusBadge + property StatusIconSettings icon: StatusIconSettings { width: 40 height: 40 @@ -75,4 +78,16 @@ Loader { 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 + } } diff --git a/ui/StatusQ/src/StatusQ/Components/qmldir b/ui/StatusQ/src/StatusQ/Components/qmldir index 2e12be9774..568befdd14 100644 --- a/ui/StatusQ/src/StatusQ/Components/qmldir +++ b/ui/StatusQ/src/StatusQ/Components/qmldir @@ -15,6 +15,7 @@ StatusLetterIdenticon 0.1 StatusLetterIdenticon.qml StatusListItem 0.1 StatusListItem.qml StatusListSectionHeadline 0.1 StatusListSectionHeadline.qml StatusLoadingIndicator 0.1 StatusLoadingIndicator.qml +StatusMemberListItem 0.1 StatusMemberListItem.qml StatusNavigationListItem 0.1 StatusNavigationListItem.qml StatusNavigationPanelHeadline 0.1 StatusNavigationPanelHeadline.qml StatusRoundIcon 0.1 StatusRoundIcon.qml diff --git a/ui/StatusQ/statusq.qrc b/ui/StatusQ/statusq.qrc index c4ce56e13c..80dead5b85 100644 --- a/ui/StatusQ/statusq.qrc +++ b/ui/StatusQ/statusq.qrc @@ -318,5 +318,6 @@ src/StatusQ/Controls/StatusBanner.qml src/StatusQ/Controls/StatusProgressBar.qml src/StatusQ/Controls/StatusPasswordStrengthIndicator.qml + src/StatusQ/Components/StatusMemberListItem.qml