feat: Profile Showcase: Proof of concept for own Profile Dialog

- the goal of this PR is to get some bsais UI building blocks done for
the followup PRs
- the order of showcase tabs now is:
Communities/Accounts/Collectibles/Assets
- there will be further changes to accomodate for different types of
backend models as those get developed (for other users' profiles)

Fixes #9664
This commit is contained in:
Lukáš Tinkl 2023-02-28 16:00:10 +01:00 committed by Lukáš Tinkl
parent 2204273a22
commit 5c0f1981ad
24 changed files with 709 additions and 63 deletions

View File

@ -67,7 +67,11 @@
"https://www.figma.com/file/ibJOTPlNtIxESwS96vJb06/%F0%9F%91%A4-Profile-%7C-Desktop?node-id=682%3A17655",
"https://www.figma.com/file/ibJOTPlNtIxESwS96vJb06/%F0%9F%91%A4-Profile-%7C-Desktop?node-id=682%3A17087",
"https://www.figma.com/file/ibJOTPlNtIxESwS96vJb06/%F0%9F%91%A4-Profile-%7C-Desktop?node-id=4%3A23525",
"https://www.figma.com/file/ibJOTPlNtIxESwS96vJb06/%F0%9F%91%A4-Profile-%7C-Desktop?node-id=4%3A23932"
"https://www.figma.com/file/ibJOTPlNtIxESwS96vJb06/%F0%9F%91%A4-Profile-%7C-Desktop?node-id=4%3A23932",
"https://www.figma.com/file/ibJOTPlNtIxESwS96vJb06/%F0%9F%91%A4-Profile-%7C-Desktop?node-id=4%3A23932&t=h8DUW6Eysawqe5u0-0",
"https://www.figma.com/file/ibJOTPlNtIxESwS96vJb06/%F0%9F%91%A4-Profile-%7C-Desktop?node-id=724%3A15511&t=h8DUW6Eysawqe5u0-0",
"https://www.figma.com/file/ibJOTPlNtIxESwS96vJb06/%F0%9F%91%A4-Profile-%7C-Desktop?node-id=6%3A16845&t=h8DUW6Eysawqe5u0-0",
"https://www.figma.com/file/ibJOTPlNtIxESwS96vJb06/%F0%9F%91%A4-Profile-%7C-Desktop?node-id=4%3A25437&t=h8DUW6Eysawqe5u0-0"
],
"StatusCommunityCard": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=8159%3A416159",

View File

@ -2,13 +2,15 @@ import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import Storybook 1.0
import utils 1.0
import shared.views 1.0
import mainui 1.0
import StatusQ 0.1
import StatusQ.Core.Utils 0.1 as CoreUtils
import Storybook 1.0
import Models 1.0
SplitView {
id: root
@ -55,7 +57,7 @@ SplitView {
alias: "Mock Alias Triplet",
lastUpdated: Date.now(),
lastUpdatedLocally: Date.now(),
localNickname: "MockNickname",
localNickname: localNickname.text,
thumbnailImage: "",
largeImage: userImage.checked ? Style.png("status-logo") : "",
isContact: isContact.checked,
@ -74,8 +76,8 @@ SplitView {
text: "__twitter",
url: "https://twitter.com/ethstatus",
icon: "twitter"
},
{
},
{
text: "__github",
url: "https://github.com/status-im",
icon: "github"
@ -122,6 +124,8 @@ SplitView {
publicKey: switchOwnProfile.checked ? "0xdeadbeef" : "0xrandomguy"
onCloseRequested: logs.logEvent("closeRequested()")
profileStore: QtObject {
readonly property string pubkey: "0xdeadbeef"
readonly property string ensName: name.text
@ -169,6 +173,209 @@ SplitView {
logs.logEvent("contactsStore::verifiedUntrustworthy", ["publicKey"], arguments)
}
}
communitiesModel: ListModel {
ListElement {
name: "Not the cool gang"
amISectionAdmin: false
description: "Nothing to write home about"
color: "indigo"
image: ""
joined: true
members: [
ListElement { displayName: "Joe" }
]
}
ListElement {
name: "Awesome bunch"
amISectionAdmin: true
description: "Where the cool guys hang out & Nothing to write home about"
color: "green"
image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
joined: true
members: [
ListElement { displayName: "Alex" },
ListElement { displayName: "AlexJb" },
ListElement { displayName: "Michal" },
ListElement { displayName: "Noelia" },
ListElement { displayName: "Lukáš" }
]
}
ListElement {
name: "Invisible community (should not display!)"
amISectionAdmin: false
description: "Get outta here"
color: "red"
image: ""
joined: false
members: []
}
}
walletStore: QtObject {
function switchAccountByAddress(address) {
logs.logEvent("walletStore::switchAccountByAddress", ["address"], arguments)
}
function selectCollectible(slug, id) {
logs.logEvent("walletStore::selectCollectible", ["slug", "id"], arguments)
}
readonly property var accounts: ListModel {
ListElement {
name: "My Status Account"
address: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7420"
color: "aliceblue"
emoji: "🇨🇿"
walletType: ""
}
ListElement {
name: "testing (no emoji, colored, saved, seed)"
address: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7000"
color: "olive"
walletType: "seed"
}
ListElement {
name: "My Bro's Account"
address: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7421"
color: "ghostwhite"
emoji: "🇸🇰"
walletType: "watch"
}
ListElement {
name: "Keycard"
address: "0xdeadbeef"
color: "red"
emoji: ""
walletType: "key"
}
}
function getNameForSavedWalletAddress(address) {
return CoreUtils.ModelUtils.getByKey(savedAddresses, "address", address, "name") ?? ""
}
function createOrUpdateSavedAddress(name, address, favourite) {
logs.logEvent("walletStore::createOrUpdateSavedAddress", ["name", "address", "favourite"], arguments)
savedAddresses.append({name, address, favourite, ens: false})
return "" // no error
}
readonly property var savedAddresses: ListModel {
ListElement {
name: "My Status Saved Account"
address: "0xcdc2ea3b6ba8fed3a3402f8db8b2fab53e7b7000"
favourite: true
ens: false
}
}
readonly property var currentAccount: QtObject {
readonly property var assets: ListModel {
readonly property var data: [
{
symbol: "MANA",
enabledNetworkBalance: {
amount: 301,
symbol: "MANA"
},
changePct24hour: -2.1,
visibleForNetworkWithPositiveBalance: true
},
{
symbol: "AAVE",
enabledNetworkBalance: {
amount: 23.3,
symbol: "AAVE"
},
changePct24hour: 4.56,
visibleForNetworkWithPositiveBalance: true
},
{
symbol: "POLY",
enabledNetworkBalance: {
amount: 3590,
symbol: "POLY"
},
changePct24hour: -11.6789,
visibleForNetworkWithPositiveBalance: true
},
{
symbol: "CDT",
enabledNetworkBalance: {
amount: 1000,
symbol: "CDT"
},
changePct24hour: 0,
visibleForNetworkWithPositiveBalance: true
},
{
symbol: "MKR",
enabledNetworkBalance: {
amount: 1.3,
symbol: "MKR"
},
//changePct24hour: undefined // NB 'undefined' on purpose
visibleForNetworkWithPositiveBalance: true
},
{
symbol: "InvisibleHere",
enabledNetworkBalance: {},
changePct24hour: 0,
visibleForNetworkWithPositiveBalance: false
}
]
Component.onCompleted: append(data)
}
}
readonly property var flatCollectibles: ListModel {
readonly property var data: [
{
//id: 123,
name: "Crypto Kitties",
description: "Super Crypto Kitty",
backgroundColor: "",
imageUrl: ModelsData.collectibles.cryptoKitties,
permalink: ""
},
{
id: 34545656768,
name: "Kitty 1",
description: "",
backgroundColor: "green",
imageUrl: ModelsData.collectibles.kitty1Big,
permalink: ""
},
{
id: 123456,
name: "Kitty 2",
description: "",
backgroundColor: "",
imageUrl: ModelsData.collectibles.kitty2Big,
permalink: ""
},
{
id: 12345645459537432,
name: "",
description: "Kitty 3 description",
backgroundColor: "oink",
imageUrl: ModelsData.collectibles.kitty3Big,
permalink: ""
},
{
id: 691,
name: "KILLABEAR #691",
description: "Please note that weapons are not yet reflected in the rarity stats.",
backgroundColor: "#807c56",
imageUrl: "https://assets.killabears.com/content/killabears/img/691-e81f892696a8ae700e0dbc62eb072060679a2046d1ef5eb2671bdb1fad1f68e3.png",
permalink: "https://opensea.io/assets/ethereum/0xc99c679c50033bbc5321eb88752e89a93e9e83c5/691"
}
]
Component.onCompleted: append(data)
}
}
}
}
}
@ -192,6 +399,12 @@ SplitView {
}
RowLayout {
Layout.fillWidth: true
Label { text: "localNickname:" }
TextField {
id: localNickname
text: "MockNickname"
placeholderText: "Local Nickname"
}
Label { text: "displayName:" }
TextField {
id: displayName
@ -237,7 +450,7 @@ SplitView {
TextField {
id: name
enabled: ensVerified.checked
text: ensVerified.checked ? "mock-ens-name" : ""
text: ensVerified.checked ? "mock-ens-name.eth" : ""
placeholderText: "ENS name"
}
}
@ -290,6 +503,7 @@ SplitView {
TextField {
Layout.fillWidth: true
id: bio
selectByMouse: true
text: "Hi, I am Alex. I'm an indie developer who mainly works on web products.
I worked for several different companies and created a couple of my own products from scratch. Currently building Telescope and Prepacked.

View File

@ -140,7 +140,7 @@ Control {
This signal is emitted when the ToastMessage contains a url and this url
is clicked by the user.
*/
signal linkActivated(var link)
signal linkActivated(string link)
QtObject {
id: d

View File

@ -70,7 +70,7 @@ Item {
textSelectedAddress.text = selectedAccount.address
}
if (selectedAccount.currencyBalance) {
textSelectedAddressFiatBalance.text = selectedAccount.currencyBalance + " " + currency.toUpperCase()
textSelectedAddressFiatBalance.text = LocaleUtils.currencyAmountToLocaleString(selectedAccount.currencyBalance)
}
if (selectedAccount.assets && showBalanceForAssetSymbol) {
assetFound = Utils.findAssetByChainAndSymbol(root.chainId, selectedAccount.assets, showBalanceForAssetSymbol)
@ -239,7 +239,7 @@ Item {
id: txtFiatBalance
Layout.rightMargin: 4
font.pixelSize: 15
text: currencyBalance
text: LocaleUtils.currencyAmountToLocaleString(currencyBalance, {onlyAmount: true})
color: Theme.palette.directColor1
}
StatusBaseText {

View File

@ -18,11 +18,8 @@ ToolTip {
property int offset: 0
property alias arrow: arrow
implicitWidth: Math.min(maxWidth, contentItem.implicitWidth + 16)
leftPadding: 8
rightPadding: 8
topPadding: 8
bottomPadding: 8
implicitWidth: Math.min(maxWidth, implicitContentWidth + 16)
padding: 8
delay: 200
background: Item {
id: statusToolTipBackground
@ -73,6 +70,6 @@ ToolTip {
font.weight: Font.Medium
horizontalAlignment: Text.AlignHCenter
bottomPadding: 8
textFormat: Text.RichText
}
}

View File

@ -71,8 +71,7 @@ SettingsPageLayout {
}
}
readonly property string initialState: root.permissionsModel.count > 0
? d.permissionsViewState : d.welcomeViewState
readonly property string initialState: d.permissionsExist ? d.permissionsViewState : d.welcomeViewState
function initializeData() {
holdingsToEditModel = emptyModel

View File

@ -101,7 +101,7 @@ QtObject {
}
function setActiveCommunity(communityId) {
mainModule.setActiveSectionById(communityId);
root.mainModuleInst.setActiveSectionById(communityId);
}
function navigateToCommunity(communityId) {

View File

@ -107,6 +107,7 @@ StatusSectionLayout {
profileStore: root.store.profileStore
privacyStore: root.store.privacyStore
contactsStore: root.store.contactsStore
communitiesModel: root.store.communitiesList
sectionTitle: root.store.getNameForSubsection(Constants.settingsSubsection.profile)
contentWidth: d.contentWidth
}

View File

@ -0,0 +1,2 @@
CommunityDelegate 1.0 CommunityDelegate.qml
WalletAccountDelegate 1.0 WalletAccountDelegate.qml

View File

@ -25,6 +25,7 @@ SettingsContentBase {
property ProfileStore profileStore
property PrivacyStore privacyStore
property ContactsStore contactsStore
property var communitiesModel
titleRowComponentLoader.sourceComponent: StatusButton {
objectName: "profileSettingsChangePasswordButton"
@ -72,6 +73,7 @@ SettingsContentBase {
profileStore: root.profileStore
privacyStore: root.privacyStore
walletStore: root.walletStore
communitiesModel: root.communitiesModel
onVisibleChanged: if (visible) stackLayout.Layout.preferredHeight = settingsView.implicitHeight
Component.onCompleted: stackLayout.Layout.preferredHeight = Qt.binding(() => settingsView.implicitHeight)
@ -82,6 +84,7 @@ SettingsContentBase {
profileStore: root.profileStore
contactsStore: root.contactsStore
communitiesModel: root.communitiesModel
dirtyValues: settingsView.dirtyValues
dirty: settingsView.dirty

View File

@ -8,6 +8,7 @@ import StatusQ.Core.Theme 0.1
Item {
property alias profileStore: profilePreview.profileStore
property alias contactsStore: profilePreview.contactsStore
property alias communitiesModel: profilePreview.communitiesModel
property alias dirtyValues: profilePreview.dirtyValues
property alias dirty: profilePreview.dirty

View File

@ -26,6 +26,7 @@ ColumnLayout {
property PrivacyStore privacyStore
property ProfileStore profileStore
property WalletStore walletStore
property var communitiesModel
property QtObject dirtyValues: QtObject {
property string displayName: descriptionPanel.displayName.text
@ -158,7 +159,7 @@ ColumnLayout {
StatusListItem {
Layout.fillWidth: true
visible: Qt.platform.os == "osx"
visible: Qt.platform.os === Constants.mac
title: qsTr("Biometric login and transaction authentication")
asset.name: "touch-id"
components: [ StatusSwitch {
@ -218,13 +219,13 @@ ColumnLayout {
Repeater {
id: communitiesRepeater
model: communitiesModule.model
model: root.communitiesModel
CommunityDelegate {
width: parent.width
visible: joined
community: model
enabled: false
sensor.enabled: false
}
}
}
@ -246,7 +247,7 @@ ColumnLayout {
width: parent.width
account: model
showShevronIcon: false
enabled: false
sensor.enabled: false
}
}
}

View File

@ -141,6 +141,10 @@ QtObject {
walletSection.switchAccount(newIndex)
}
function switchAccountByAddress(address) {
walletSection.switchAccountByAddress(address)
}
function generateNewAccount(password, accountName, color, emoji, path, derivedFrom) {
return walletSectionAccounts.generateNewAccount(password, accountName, color, emoji, path, derivedFrom)
}
@ -198,6 +202,10 @@ QtObject {
walletSectionCurrentCollectible.update(slug, id)
}
function getNameForSavedWalletAddress(address) {
return walletSectionSavedAddresses.getNameByAddress(address)
}
function createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens) {
return walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address, favourite, chainShortNames, ens)
}

View File

@ -169,9 +169,9 @@ Rectangle {
components: [
StatusIcon {
icon: {
if (model.walletType == "watch")
if (model.walletType === Constants.watchWalletType)
return "show"
else if (model.walletType == "key")
if (model.walletType === Constants.keyWalletType)
return "keycard"
return ""

View File

@ -163,6 +163,14 @@ Item {
Global.settingsSubsection = subsection;
}
}
function onOpenSendModal(address: string) {
sendModal.open(address)
}
function onSwitchToCommunity(communityId: string) {
communitiesPortalLayoutContainer.communitiesStore.setActiveCommunity(communityId)
}
}
function changeAppSectionBySectionId(sectionId) {
@ -1119,7 +1127,10 @@ Item {
this.open = false
}
onLinkActivated: {
Qt.openUrlExternally(link);
if (link.startsWith("#")) // internal link to section
globalConns.onAppSectionBySectionTypeChanged(link.substring(1))
else
Global.openLink(link)
}
onClose: {

View File

@ -287,6 +287,7 @@ QtObject {
id: profilePopup
profileStore: rootStore.profileSectionStore.profileStore
contactsStore: rootStore.profileSectionStore.contactsStore
communitiesModel: rootStore.profileSectionStore.communitiesList
onClosed: {
if (profilePopup.parentPopup) {

View File

@ -14,8 +14,6 @@ import StatusQ.Core.Backpressure 1.0
import shared.controls 1.0
import utils 1.0
import utils 1.0
import "../controls"
Item {

View File

@ -13,6 +13,7 @@ StatusDialog {
property var profileStore
property var contactsStore
property var communitiesModel
width: 640
padding: 0
@ -24,6 +25,7 @@ StatusDialog {
publicKey: root.publicKey
profileStore: root.profileStore
contactsStore: root.contactsStore
communitiesModel: root.communitiesModel
onCloseRequested: root.close()
}
}

View File

@ -32,9 +32,8 @@ Item {
id: assetListView
objectName: "assetViewStatusListView"
anchors.fill: parent
model: RootStore.tokensLoading ? 25 : filteredModel
model: RootStore.tokensLoading ? Constants.dummyModelItems : filteredModel
delegate: RootStore.tokensLoading ? loadingTokenDelegate : tokenDelegate
ScrollBar.vertical: StatusScrollBar {}
}
SortFilterProxyModel {

View File

@ -1,7 +1,7 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.14
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
@ -9,6 +9,7 @@ import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Popups 0.1
import StatusQ.Popups.Dialog 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import utils 1.0
import shared.controls 1.0
@ -16,16 +17,23 @@ import shared.panels 1.0
import shared.popups 1.0
import shared.controls.chat 1.0
import shared.controls.chat.menuItems 1.0
import shared.views.profile 1.0
import SortFilterProxyModel 0.2
import AppLayouts.Wallet.stores 1.0 as WalletNS
Pane {
id: root
property bool readOnly
property bool readOnly // inside settings/profile/preview
property string publicKey: contactsStore.myPublicKey
property var profileStore
property var contactsStore
property var walletStore: WalletNS.RootStore
property var communitiesModel
property QtObject dirtyValues: null
property bool dirty: false
@ -547,8 +555,10 @@ Pane {
StatusScrollView {
id: scrollView
implicitWidth: contentWidth + leftPadding + rightPadding
implicitHeight: contentHeight + topPadding + bottomPadding
Layout.fillWidth: true
Layout.preferredHeight: contentHeight + topPadding + bottomPadding
Layout.fillHeight: true
Layout.leftMargin: -column.anchors.leftMargin
Layout.rightMargin: -column.anchors.rightMargin
Layout.topMargin: -column.spacing
@ -562,8 +572,7 @@ Pane {
Layout.fillWidth: true
Layout.leftMargin: column.anchors.leftMargin + Style.current.halfPadding
Layout.rightMargin: column.anchors.rightMargin + Style.current.halfPadding
bio: root.dirty ? root.dirtyValues.bio
: d.contactDetails.bio
bio: root.dirty ? root.dirtyValues.bio : d.contactDetails.bio
userSocialLinksJson: root.dirty ? root.profileStore.temporarySocialLinksJson
: d.contactDetails.socialLinks
}
@ -666,26 +675,17 @@ Pane {
height: width
mipmap: true
smooth: false
source: root.profileStore.getQrCodeSource(root.profileStore.pubkey)
source: root.profileStore.getQrCodeSource(Utils.getCompressedPk(root.profileStore.pubkey))
}
}
}
StatusTabBar {
id: showcaseTabBar
Layout.fillWidth: true
Layout.leftMargin: column.anchors.leftMargin
Layout.rightMargin: column.anchors.rightMargin
bottomPadding: -4
StatusTabButton {
leftPadding: 0
width: implicitWidth
text: qsTr("Assets")
}
StatusTabButton {
width: implicitWidth
text: qsTr("Collectibles")
}
StatusTabButton {
width: implicitWidth
text: qsTr("Communities")
@ -694,27 +694,32 @@ Pane {
width: implicitWidth
text: qsTr("Accounts")
}
StatusTabButton {
width: implicitWidth
text: qsTr("Collectibles")
}
StatusTabButton {
leftPadding: 0
width: implicitWidth
text: qsTr("Assets")
}
}
StatusDialogBackground {
// Profile Showcase
ProfileShowcaseView {
Layout.fillWidth: true
Layout.topMargin: -column.spacing
Layout.preferredHeight: 300
color: Theme.palette.baseColor4
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: parent.radius
color: parent.color
}
currentTabIndex: showcaseTabBar.currentIndex
isCurrentUser: d.isCurrentUser
mainDisplayName: d.mainDisplayName
readOnly: root.readOnly
profileStore: root.profileStore
walletStore: root.walletStore
communitiesModel: root.communitiesModel
StatusBaseText {
anchors.centerIn: parent
color: Theme.palette.baseColor1
text: qsTr("More content to appear here soon...")
}
onCloseRequested: root.closeRequested()
}
}
}

View File

@ -159,6 +159,7 @@ Column {
anchors.centerIn: parent
text: "GIF"
font.pixelSize: 13
color: "white"
}
}
}

View File

@ -0,0 +1,395 @@
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.Components 0.1
import StatusQ.Popups.Dialog 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import utils 1.0
import shared.controls 1.0 // Timer
import SortFilterProxyModel 0.2
Control {
id: root
property alias currentTabIndex: stackLayout.currentIndex
property bool isCurrentUser
property string mainDisplayName
property bool readOnly
property var profileStore
property var walletStore
property var communitiesModel
signal closeRequested()
horizontalPadding: readOnly ? 20 : 40 // smaller in settings/preview
topPadding: Style.current.bigPadding
QtObject {
id: d
readonly property string copyLiteral: qsTr("Copy")
readonly property var timer: Timer {
id: timer
}
}
background: StatusDialogBackground {
color: Theme.palette.baseColor4
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: parent.radius
color: parent.color
}
}
contentItem: StackLayout {
id: stackLayout
// communities
ColumnLayout {
StatusBaseText {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter
visible: communitiesView.count == 0
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: Theme.palette.directColor1
text: qsTr("%1 hasn't joined any communities yet").arg(root.mainDisplayName)
}
StatusGridView {
Layout.fillWidth: true
Layout.fillHeight: true
id: communitiesView
rightMargin: Style.current.halfPadding
cellWidth: (width-rightMargin)/2
cellHeight: cellWidth/2
visible: count
model: SortFilterProxyModel {
sourceModel: root.isCurrentUser ? root.communitiesModel : null // TODO show other users too
filters: ValueFilter {
roleName: "joined"
value: true
}
sorters: [
RoleSorter {
roleName: "amISectionAdmin"
sortOrder: Qt.DescendingOrder // admin first
},
StringSorter {
roleName: "name"
caseSensitivity: Qt.CaseInsensitive
}
]
}
ScrollBar.vertical: StatusScrollBar { }
delegate: StatusListItem { // TODO custom delegate
width: GridView.view.cellWidth - Style.current.smallPadding
height: GridView.view.cellHeight - Style.current.smallPadding
title: model.name
statusListItemTitle.font.pixelSize: 17
statusListItemTitle.font.bold: true
subTitle: model.description
tertiaryTitle: qsTr("%n member(s)", "", model.members.count)
asset.name: model.image ?? model.name
asset.isImage: asset.name.startsWith("data:image/")
asset.isLetterIdenticon: !model.image
asset.color: model.color
asset.width: 40
asset.height: 40
border.width: 1
border.color: Theme.palette.baseColor2
components: [
StatusIcon {
visible: model.amISectionAdmin
anchors.verticalCenter: parent.verticalCenter
icon: "crown"
color: Theme.palette.directColor1
}
]
onClicked: {
if (root.readOnly)
return
root.closeRequested()
Global.switchToCommunity(model.id)
}
}
}
}
// wallets/accounts
ColumnLayout {
StatusBaseText {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter
visible: accountsView.count == 0
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: Theme.palette.directColor1
text: qsTr("%1 doesn't have any wallet accounts yet").arg(root.mainDisplayName)
}
StatusListView {
Layout.fillWidth: true
Layout.fillHeight: true
id: accountsView
spacing: Style.current.halfPadding
visible: count
model: SortFilterProxyModel {
sourceModel: root.isCurrentUser ? root.walletStore.accounts : null // TODO show other users too
filters: ValueFilter { // everything except keycards
roleName: "walletType"
value: Constants.keyWalletType
inverted: true
}
}
delegate: StatusListItem {
id: accountDelegate
property bool saved: root.walletStore.getNameForSavedWalletAddress(model.address) !== ""
border.width: 1
border.color: Theme.palette.baseColor2
width: ListView.view.width
title: model.name
subTitle: StatusQUtils.Utils.elideText(model.address, 6, 4).replace("0x", "0×")
asset.color: model.color
asset.emoji: model.emoji ?? ""
asset.name: asset.emoji || "filled-account"
asset.isLetterIdenticon: asset.emoji
asset.letterSize: 14
asset.bgColor: Theme.palette.primaryColor3
asset.isImage: asset.emoji
components: [
StatusIcon {
anchors.verticalCenter: parent.verticalCenter
visible: model.walletType === Constants.watchWalletType
icon: "show"
color: Theme.palette.directColor1
},
StatusFlatButton {
anchors.verticalCenter: parent.verticalCenter
size: StatusBaseButton.Size.Small
enabled: !accountDelegate.saved
text: accountDelegate.saved ? qsTr("Address saved") : qsTr("Save Address")
onClicked: {
accountDelegate.saved = root.walletStore.createOrUpdateSavedAddress(model.name, model.address, false) === ""
Global.displayToastMessage(qsTr("%1 saved to your wallet").arg(accountDelegate.subTitle),
qsTr("Go to your wallet"),
"wallet",
false,
Constants.ephemeralNotificationType.normal,
`#${Constants.appSection.wallet}` // internal link to wallet section
)
}
},
StatusFlatRoundButton {
anchors.verticalCenter: parent.verticalCenter
type: StatusFlatRoundButton.Type.Secondary
icon.name: "send"
tooltip.text: qsTr("Send")
onClicked: {
root.walletStore.switchAccountByAddress(model.address)
Global.openSendModal(model.address)
}
},
StatusFlatRoundButton {
anchors.verticalCenter: parent.verticalCenter
type: StatusFlatRoundButton.Type.Secondary
icon.name: "copy"
tooltip.text: d.copyLiteral
onClicked: {
tooltip.text = qsTr("Copied")
root.profileStore.copyToClipboard(model.address)
d.timer.setTimeout(function() {
tooltip.text = d.copyLiteral
}, 2000);
}
}
]
onClicked: {
if (root.readOnly)
return
root.walletStore.switchAccountByAddress(model.address)
}
}
}
}
// collectibles/NFTs
ColumnLayout {
StatusBaseText {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter
visible: collectiblesView.count == 0
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: Theme.palette.directColor1
text: qsTr("%1 doesn't have any collectibles/NFTs yet").arg(root.mainDisplayName)
}
StatusGridView {
Layout.fillWidth: true
Layout.fillHeight: true
id: collectiblesView
rightMargin: Style.current.halfPadding
cellWidth: (width-rightMargin)/4
cellHeight: cellWidth
visible: count
model: root.isCurrentUser ? root.walletStore.flatCollectibles : null // TODO show other users too
ScrollBar.vertical: StatusScrollBar { }
delegate: StatusRoundedImage {
width: GridView.view.cellWidth - Style.current.smallPadding
height: GridView.view.cellHeight - Style.current.smallPadding
border.width: 1
border.color: Theme.palette.directColor7
color: !!model.backgroundColor ? model.backgroundColor : "transparent"
radius: Style.current.radius
showLoadingIndicator: model.isLoading
image.fillMode: Image.PreserveAspectCrop
image.source: model.imageUrl ?? ""
RowLayout {
anchors.left: parent.left
anchors.leftMargin: Style.current.halfPadding
anchors.bottom: parent.bottom
anchors.bottomMargin: Style.current.halfPadding
anchors.right: parent.right
anchors.rightMargin: Style.current.halfPadding
Control {
Layout.maximumWidth: parent.width
horizontalPadding: Style.current.halfPadding
verticalPadding: Style.current.halfPadding/2
visible: !!model.id
background: Rectangle {
radius: Style.current.halfPadding/2
color: Theme.palette.indirectColor2
}
contentItem: StatusBaseText {
font.pixelSize: 13
font.weight: Font.Medium
maximumLineCount: 1
elide: Text.ElideRight
text: `#${model.id}`
}
}
}
StatusToolTip {
visible: hhandler.hovered && (!!model.name || !!model.description)
text: {
const name = model.name
const descr = model.description
const sep = !!name && !!descr ? "<br>" : ""
return `<b>${name}</b>${sep}${descr}`
}
}
HoverHandler {
id: hhandler
cursorShape: hovered ? Qt.PointingHandCursor : undefined
}
TapHandler {
onSingleTapped: {
Global.openLink(model.permalink)
}
}
}
}
}
// assets/tokens
ColumnLayout {
StatusBaseText {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter
visible: assetsView.count == 0
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: Theme.palette.directColor1
text: qsTr("%1 doesn't have any assets/tokens yet").arg(root.mainDisplayName)
}
StatusGridView {
Layout.fillWidth: true
Layout.fillHeight: true
id: assetsView
rightMargin: Style.current.halfPadding
cellWidth: (width-rightMargin)/3
cellHeight: cellWidth/2.5
visible: count
model: SortFilterProxyModel {
// TODO show assets for all accounts, not just the current one?
sourceModel: root.isCurrentUser ? root.walletStore.currentAccount.assets : null // TODO show other users too
filters: ValueFilter {
roleName: "visibleForNetworkWithPositiveBalance"
value: true
}
sorters: [
StringSorter {
roleName: "name"
},
StringSorter {
roleName: "symbol"
}
]
}
ScrollBar.vertical: StatusScrollBar { }
delegate: StatusListItem {
readonly property double changePct24hour: model.changePct24hour ?? 0
readonly property string textColor: changePct24hour === 0
? Theme.palette.baseColor1 : changePct24hour < 0
? Theme.palette.dangerColor1 : Theme.palette.successColor1
readonly property string arrow: changePct24hour === 0 ? "" : changePct24hour < 0 ? "↓" : "↑"
width: GridView.view.cellWidth - Style.current.halfPadding
height: GridView.view.cellHeight - Style.current.halfPadding
title: LocaleUtils.currencyAmountToLocaleString(model.enabledNetworkBalance)
statusListItemTitle.font.weight: Font.Medium
tertiaryTitle: qsTr("%1% today %2")
.arg(LocaleUtils.numberToLocaleString(changePct24hour, changePct24hour === 0 ? 0 : 2)).arg(arrow)
statusListItemTertiaryTitle.color: textColor
statusListItemTertiaryTitle.font.pixelSize: Theme.asideTextFontSize
statusListItemTertiaryTitle.anchors.topMargin: 6
leftPadding: Style.current.halfPadding
rightPadding: Style.current.halfPadding
border.width: 1
border.color: Theme.palette.baseColor2
components: [
Image {
width: 40
height: 40
anchors.verticalCenter: parent.verticalCenter
source: Style.png("tokens/" + model.symbol)
onStatusChanged: {
if (status === Image.Error) {
source = Style.png("tokens/DEFAULT-TOKEN")
}
}
}
]
onClicked: {
if (root.readOnly)
return
// TODO what to do here?
}
}
}
}
}
}

View File

@ -0,0 +1 @@
ProfileShowcaseView 1.0 ProfileShowcaseView.qml

View File

@ -47,6 +47,9 @@ QtObject {
signal setNthEnabledSectionActive(int nthSection)
signal appSectionBySectionTypeChanged(int sectionType, int subsection)
signal openSendModal(string address)
signal switchToCommunity(string communityId)
signal playSendMessageSound()
signal playNotificationSound()
signal playErrorSound()