feat(StatusCommunityCard): First component iteration (#693)

It includes logo, title, community members, description, loaded, community id, popularity, tags row (that must be replaced to a new StatusQ component `StatusListItemTagRow`.

It also contains `locale` property used to decide the member's number format.

Added loading card.

Added Community Card page (components test) and view (demo app) in sandbox.

Added component documentation.

Part of task: https://github.com/status-im/status-desktop/issues/4936
This commit is contained in:
Noelia 2022-05-31 11:44:26 +02:00 committed by Michał Cieślak
parent 7281c4142a
commit 7bd9b37792
18 changed files with 667 additions and 6 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -25,10 +25,11 @@ Rectangle {
id: appSectionType id: appSectionType
readonly property int chat: 0 readonly property int chat: 0
readonly property int community: 1 readonly property int community: 1
readonly property int wallet: 2 readonly property int communitiesPortal: 2
readonly property int wallet: 3
readonly property int browser: 3 readonly property int browser: 3
readonly property int nodeManagement: 4 readonly property int nodeManagement: 5
readonly property int profileSettings: 5 readonly property int profileSettings: 6
readonly property int apiDocumentation: 100 readonly property int apiDocumentation: 100
readonly property int demoApp: 101 readonly property int demoApp: 101
} }
@ -118,6 +119,11 @@ Rectangle {
appView.sourceComponent = statusAppChatView appView.sourceComponent = statusAppChatView
demoApp.setActiveItem(model.sectionId) demoApp.setActiveItem(model.sectionId)
} }
else if(model.sectionType === appSectionType.communitiesPortal)
{
appView.sourceComponent = statusCommunityPortalView
demoApp.setActiveItem(model.sectionId)
}
else if(model.sectionType === appSectionType.profileSettings) else if(model.sectionType === appSectionType.profileSettings)
{ {
appView.sourceComponent = statusAppProfileSettingsView appView.sourceComponent = statusAppProfileSettingsView
@ -205,6 +211,11 @@ Rectangle {
StatusAppProfileSettingsView { } StatusAppProfileSettingsView { }
} }
Component {
id: statusCommunityPortalView
StatusAppCommunitiesPortalView { }
}
DemoContactRequestsModal { DemoContactRequestsModal {
id: demoContactRequestsModal id: demoContactRequestsModal
anchors.centerIn: parent anchors.centerIn: parent

View File

@ -0,0 +1,151 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import "../demoapp/data" 1.0
ScrollView {
id: root
QtObject {
id: d
property ListModel featuredCommunitiesModel: Models.featuredCommunitiesModel
property ListModel popularCommunitiesModel: Models.curatedCommunitiesModel
property ListModel tagsModel: Models.tagsModel
property string searchText: ""
property int layoutVMargin: 70
property int layoutHMargin: 64
property int titlePixelSize: 28
property int subtitlePixelSize: 17
property int stylePadding: 16
function navigateToCommunity(communityId) {
console.info("Clicked community ID: " + communityId)
}
}
contentHeight: column.height + d.layoutVMargin
contentWidth: column.width + d.layoutHMargin
clip: true
ColumnLayout {
id: column
spacing: 18
StatusBaseText {
Layout.topMargin: d.layoutVMargin
Layout.leftMargin: d.layoutHMargin
text: qsTr("Find community")
font.weight: Font.Bold
font.pixelSize: d.titlePixelSize
color: Theme.palette.directColor1
}
// Tags definition - Now hidden - Out of scope
// TODO: Replace by `StatusListItemTagRow`
Row {
visible: d.tagsModel.count > 0
Layout.leftMargin: d.layoutHMargin
Layout.rightMargin: d.layoutHMargin
width: 1234 // by design
spacing: d.stylePadding/2
Repeater {
model: d.tagsModel
delegate: StatusListItemTag {
border.color: Theme.palette.baseColor2
color: "transparent"
height: 32
radius: 36
closeButtonVisible: false
icon.emoji: model.emoji
icon.height: 32
icon.width: icon.height
icon.color: "transparent"
icon.isLetterIdenticon: true
title: model.name
titleText.font.pixelSize: 15
titleText.color: Theme.palette.primaryColor1
}
}
}
StatusBaseText {
Layout.leftMargin: d.layoutHMargin
Layout.topMargin: 20
text: qsTr("Featured")
font.weight: Font.Bold
font.pixelSize: d.subtitlePixelSize
color: Theme.palette.directColor1
}
GridLayout {
id: featuredGrid
Layout.leftMargin: d.layoutHMargin
columns: 3
columnSpacing: d.stylePadding
rowSpacing: d.stylePadding
Repeater {
model: d.featuredCommunitiesModel
delegate: StatusCommunityCard {
locale: "es"
communityId: model.communityId
loaded: model.available
logo: model.icon
name: model.name
description: model.description
members: model.members
popularity: model.popularity
categories: ListModel {
ListElement { name: "sport"; emoji: "🎾"}
ListElement { name: "food"; emoji: "🥑"}
ListElement { name: "privacy"; emoji: "👻"}
}
onClicked: { d.navigateToCommunity(communityId) }
}
}
}
StatusBaseText {
Layout.leftMargin: d.layoutHMargin
Layout.topMargin: 20
text: qsTr("Popular")
font.weight: Font.Bold
font.pixelSize: d.subtitlePixelSize
color: Theme.palette.directColor1
}
GridLayout {
Layout.leftMargin: d.layoutHMargin
columns: 3
columnSpacing: d.stylePadding
rowSpacing: d.stylePadding
Repeater {
model: d.popularCommunitiesModel
delegate: StatusCommunityCard {
locale: "es"
communityId: model.communityId
loaded: model.available
logo: model.icon
name: model.name
description: model.description
members: model.members
popularity: model.popularity
onClicked: { d.navigateToCommunity(communityId) }
}
}
}
}
}

View File

@ -968,8 +968,19 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
notificationsCount: 0 notificationsCount: 0
} }
ListElement { ListElement {
sectionId: "wallet" sectionId: "communitiesPortal"
sectionType: 2 sectionType: 2
name: "Communities Portal"
active: false
image: ""
icon: "communities"
color: ""
hasNotification: false
notificationsCount: 0
}
ListElement {
sectionId: "wallet"
sectionType: 3
name: "Wallet" name: "Wallet"
active: false active: false
image: "" image: ""
@ -980,7 +991,7 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
} }
ListElement { ListElement {
sectionId: "browser" sectionId: "browser"
sectionType: 3 sectionType: 4
name: "Browser" name: "Browser"
active: false active: false
image: "" image: ""
@ -1306,4 +1317,145 @@ CExPynn1gWf9bx498P7/nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2I
selected: false selected: false
} }
} }
property ListModel featuredCommunitiesModel : ListModel {
ListElement {
name: "CryptoKitties";
description: "A community of cat lovers, meow!";
icon:"qrc:/images/CryptoKitties.png";
members: 1045;
categories: [];
communityId: "341";
available: true;
popularity: 1
}
ListElement {
name: "Friends with Benefits";
description: "A group chat full of out favorite thinkers and creators.";
icon:"qrc:/images/FriendsBenefits.png";
members: 452;
categories: [];
communityId: "232";
available: true;
popularity: 2
}
ListElement {
name: "Status Hi!!";
description: "A new community description with long long long and repetitive repetitive repetitive repetitive explanation!!";
icon:"qrc:/images/SNT.png";
members: 89;
categories: [];
communityId: "223";
available: true;
popularity: 3
}
}
property ListModel curatedCommunitiesModel : ListModel {
ListElement {
name: "CryptoKitties";
description: "A community of cat lovers, meow!";
icon:"qrc:/images/CryptoKitties.png";
members: 1000;
categories: [];
communityId: "1";
available: true;
popularity: 1
}
ListElement {
name: "Friends with Benefits";
description: "A group chat full of out favorite thinkers and creators.";
icon:"qrc:/images/FriendsBenefits.png";
members: 452;
categories: [];
communityId: "2";
available: true;
popularity: 2
}
ListElement {
name: "Teller";
description: "A community of P2P crypto trades";
icon:"qrc:/images/P2PCrypto.png";
members: 50;
categories: [];
communityId: "3";
available: true;
popularity: 3
}
ListElement {
name: "Status";
description: "Community description goes here.";
icon:"qrc:/images/SNT.png";
members: 5288;
categories: [];
communityId: "4";
available: true;
popularity: 4
}
ListElement {
name: "Status Punks";
description: "Community description goes here.Community description goes here. Community description goes here. Community description goes here.";
icon:"qrc:/images/StatusPunks.png";
members: 4125;
categories: [];
communityId: "5";
available: false;
popularity: 5
}
ListElement {
name: "Uniswap";
description: "Community description goes here.";
icon:"qrc:/images/CryptoKitties.png";
members: 45;
categories: [];
communityId: "6";
available: false;
popularity: 6
}
ListElement {
name: "Dragonereum";
description: "Community description goes here.";
icon:"qrc:/images/Dragonerum.png";
members: 968;
categories: [];
communityId: "7";
available: true;
popularity: 7
}
ListElement {
name: "CryptoPunks";
description: "Community description goes here. Community description goes here. Community description goes here. Community description goes here. Community description goes here. Community description goes here.";
icon:"qrc:/images/CryptoPunks.png";
members: 4200;
categories: [];
communityId: "8";
available: true;
popularity: 8
}
ListElement {
name: "Socks";
description: "Community description goes here.";
icon:"qrc:/images/Socks.png"
members: 12;
categories: [];
communityId: "9";
available: true;
popularity: 9
}
}
property ListModel tagsModel : ListModel {
ListElement { name: "gaming"; emoji: "🎮"}
ListElement { name: "art"; emoji: "🖼️️"}
ListElement { name: "crypto"; emoji: "💸"}
ListElement { name: "nsfw"; emoji: "🍆"}
ListElement { name: "markets"; emoji: "💎"}
ListElement { name: "defi"; emoji: "📈"}
ListElement { name: "travel"; emoji: "🚁"}
ListElement { name: "web3"; emoji: "🗺"}
ListElement { name: "sport"; emoji: "🎾"}
ListElement { name: "food"; emoji: "🥑"}
ListElement { name: "enviroment"; emoji: "☠️"}
ListElement { name: "privacy"; emoji: "👻"}
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

View File

@ -284,6 +284,11 @@ StatusWindow {
selected: viewLoader.source.toString().includes(title) selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title); onClicked: mainPageView.page(title);
} }
StatusNavigationListItem {
title: "StatusCommunityCard"
selected: viewLoader.source.toString().includes(title)
onClicked: mainPageView.page(title);
}
StatusListSectionHeadline { text: "StatusQ.Popup" } StatusListSectionHeadline { text: "StatusQ.Popup" }
StatusNavigationListItem { StatusNavigationListItem {
title: "StatusPopupMenu" title: "StatusPopupMenu"

View File

@ -0,0 +1,43 @@
import QtQuick 2.0
import QtQuick.Layouts 1.13
import StatusQ.Components 0.1
import "../demoapp/data" 1.0
GridLayout {
QtObject {
id: d
function navigateToCommunity(communityID) {
console.log("Clicked community: " + communityID)
}
}
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
columns: 2
columnSpacing: 16
rowSpacing: 16
Repeater {
model: Models.curatedCommunitiesModel
delegate: StatusCommunityCard {
locale: "en"
communityId: model.communityId
loaded: model.available
logo: model.icon
name: model.name
description: model.description
members: model.members
popularity: model.popularity
categories: ListModel {
ListElement { name: "sport"; emoji: "🎾"}
ListElement { name: "food"; emoji: "🥑"}
ListElement { name: "privacy"; emoji: "👻"}
}
onClicked: { d.navigateToCommunity(communityId) }
}
}
}

View File

@ -44,5 +44,14 @@
<file>pages/StatusWizardStepperPage.qml</file> <file>pages/StatusWizardStepperPage.qml</file>
<file>pages/StatusTabBarButtonPage.qml</file> <file>pages/StatusTabBarButtonPage.qml</file>
<file>pages/StatusColorSpacePage.qml</file> <file>pages/StatusColorSpacePage.qml</file>
<file>images/CryptoKitties.png</file>
<file>images/CryptoPunks.png</file>
<file>images/Dragonerum.png</file>
<file>images/FriendsBenefits.png</file>
<file>images/logo-test-image.png</file>
<file>images/P2PCrypto.png</file>
<file>images/SNT.png</file>
<file>images/Socks.png</file>
<file>images/StatusPunks.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -0,0 +1,289 @@
import QtQuick 2.13
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
/*!
\qmltype StatusCommunityCard
\inherits Rectangle
\inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1
\brief It is a community card item that provides relevant information about a community model. Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-rectangle.html}{Rectangle}.
The \c StatusCommunityCard is a community card clickable item that represents a data model. The data model is commonly a JavaScript array or a ListModel object.
Example of how the component looks like:
\image status_community_card.png
Example of how to use it:
\qml
StatusCommunityCard {
locale: "en"
communityId: model.communityId
loaded: model.available
logo: model.icon
name: model.name
description: model.description
members: model.members
popularity: model.popularity
categories: model.categories
onClicked: { d.navigateToCommunity(communityId) }
}
\endqml
For a list of components available see StatusQ.
*/
Rectangle {
id: root
/*!
\qmlproperty string StatusCommunityCard::communityId
This property holds the community identifier value.
*/
property string communityId: ""
/*!
\qmlproperty bool StatusCommunityCard::loaded
This property holds a boolean value that represents if the community information is loaded or not.
*/
property bool loaded: true
/*!
\qmlproperty url StatusCommunityCard::logo
This property holds the community logo source.
*/
property url logo: ""
/*!
\qmlproperty string StatusCommunityCard::name
This property holds the community name.
*/
property string name: ""
/*!
\qmlproperty string StatusCommunityCard::description
This property holds the community description.
*/
property string description: ""
/*!
\qmlproperty int StatusCommunityCard::members
This property holds the community members value.
*/
property int members: 0
/*!
\qmlproperty int StatusCommunityCard::popularity
This property holds the community popularity (community rate).
*/
property int popularity: 0
/*!
\qmlproperty string StatusCommunityCard::categories
This property holds the data that will be populated as the tags row of the community.
Here an example of the model roles expected:
\qml
categories: ListModel {
ListElement { name: "gaming"; emoji: "🎮"; selected: false}
ListElement { name: "art"; emoji: "🖼️️"; selected: false}
ListElement { name: "crypto"; emoji: "💸"; selected: false}
ListElement { name: "nsfw"; emoji: "🍆"; selected: false}
ListElement { name: "markets"; emoji: "💎"; selected: false}
}
\endqml
*/
property ListModel categories: ListModel {}
/*!
\qmlproperty string StatusCommunityCard::locale
This property holds the application locale used to give format to members number representation.
If not provided, default value is "en".
*/
property string locale: "en"
/*!
\qmlsignal StatusCommunityCard::clicked(string communityId)
This signal is emitted when the card item is clicked.
*/
signal clicked(string communityId)
QtObject {
id: d
property int dMargins: 12
}
width: 400 // by design
height: 230 // by design
border.color: Theme.palette.baseColor2
color: sensor.containsMouse ? Theme.palette.baseColor4 : "transparent"
radius: 8
clip: true
// Community Card:
ColumnLayout {
visible: root.loaded
anchors.fill: parent
anchors.margins: d.dMargins
clip: true
spacing: 4
StatusRoundedImage {
width: 40
height: 40
image.source: root.logo
color: "transparent"
}
// TODO: Add here new component for community permissions / restrictions
// ...
RowLayout {
Layout.topMargin: 8
StatusBaseText {
Layout.alignment: Qt.AlignVCenter
text: root.name
font.weight: Font.Bold
font.pixelSize: 17
color: Theme.palette.directColor1
}
StatusIcon {
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: 6
icon: "tiny/tiny-contact"
width: 10
height: 10
color: Theme.palette.baseColor1
}
StatusBaseText {
Layout.alignment: Qt.AlignVCenter
text: Number(root.members).toLocaleString(Qt.locale(locale), 'f', 0)
font.pixelSize: 13
color: Theme.palette.directColor1
horizontalAlignment: Text.AlignLeft
}
}
StatusBaseText {
Layout.fillHeight: true
Layout.fillWidth: true
text: root.description
font.pixelSize: 15
color: Theme.palette.directColor1
wrapMode: Text.WordWrap
elide: Text.ElideRight
clip: true
}
// TODO: Replace by `StatusListItemTagRow` - To be done!
Row {
visible: root.categories.count > 0
Layout.bottomMargin: 4
width: 324 // by design
spacing: 8
clip: true
Repeater {
model: root.categories
delegate: StatusListItemTag {
border.color: Theme.palette.baseColor2
color: "transparent"
height: 32
radius: 36
closeButtonVisible: false
icon.emoji: model.emoji
icon.height: 32
icon.width: icon.height
icon.color: "transparent"
icon.isLetterIdenticon: true
title: model.name
titleText.font.pixelSize: 15
titleText.color: Theme.palette.primaryColor1
}
}
}
}
// Loading Card
ColumnLayout {
visible: !root.loaded
anchors.fill: parent
anchors.margins: d.dMargins
clip: true
spacing: 9
Rectangle {
width: 40
height: 40
color: Theme.palette.baseColor2
radius: width / 2
}
RowLayout {
Layout.topMargin: 8
Rectangle {
Layout.alignment: Qt.AlignBottom
Layout.topMargin: 8
width: 84
height: 16
color: Theme.palette.baseColor2
radius: 5
}
Rectangle {
Layout.leftMargin: 8
Layout.alignment: Qt.AlignBottom
width: 14
height: 14
color: Theme.palette.baseColor2
radius: width / 2
}
Rectangle {
Layout.alignment: Qt.AlignBottom
width: 50
height: 12
color: Theme.palette.baseColor2
radius: 5
}
}
Rectangle {
width: 311
height: 16
color: Theme.palette.baseColor2
radius: 5
}
Rectangle {
width: 271
height: 16
color: Theme.palette.baseColor2
radius: 5
}
// Filler
Item { Layout.fillHeight: true }
Row {
Layout.bottomMargin: 4
spacing: 8
Repeater {
model: 3
delegate:
Rectangle {
width: 76
height: 24
color: Theme.palette.baseColor2
radius: 20
}
}
}
}
MouseArea {
id: sensor
enabled: root.loaded
anchors.fill: parent
cursorShape: root.loaded ? Qt.PointingHandCursor : Qt.ArrowCursor
hoverEnabled: true
onClicked: root.clicked(root.communityId)
}
}

View File

@ -35,3 +35,4 @@ StatusToastMessage 0.1 StatusToastMessage.qml
StatusWizardStepper 0.1 StatusWizardStepper.qml StatusWizardStepper 0.1 StatusWizardStepper.qml
StatusImageCropPanel 0.1 StatusImageCropPanel.qml StatusImageCropPanel 0.1 StatusImageCropPanel.qml
StatusColorSpace 0.0 StatusColorSpace.qml StatusColorSpace 0.0 StatusColorSpace.qml
StatusCommunityCard 0.1 StatusCommunityCard.qml