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%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=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%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": [ "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",

View File

@ -2,13 +2,15 @@ import QtQuick 2.14
import QtQuick.Controls 2.14 import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14 import QtQuick.Layouts 1.14
import Storybook 1.0
import utils 1.0 import utils 1.0
import shared.views 1.0 import shared.views 1.0
import mainui 1.0 import mainui 1.0
import StatusQ 0.1 import StatusQ 0.1
import StatusQ.Core.Utils 0.1 as CoreUtils
import Storybook 1.0
import Models 1.0
SplitView { SplitView {
id: root id: root
@ -55,7 +57,7 @@ SplitView {
alias: "Mock Alias Triplet", alias: "Mock Alias Triplet",
lastUpdated: Date.now(), lastUpdated: Date.now(),
lastUpdatedLocally: Date.now(), lastUpdatedLocally: Date.now(),
localNickname: "MockNickname", localNickname: localNickname.text,
thumbnailImage: "", thumbnailImage: "",
largeImage: userImage.checked ? Style.png("status-logo") : "", largeImage: userImage.checked ? Style.png("status-logo") : "",
isContact: isContact.checked, isContact: isContact.checked,
@ -74,8 +76,8 @@ SplitView {
text: "__twitter", text: "__twitter",
url: "https://twitter.com/ethstatus", url: "https://twitter.com/ethstatus",
icon: "twitter" icon: "twitter"
}, },
{ {
text: "__github", text: "__github",
url: "https://github.com/status-im", url: "https://github.com/status-im",
icon: "github" icon: "github"
@ -122,6 +124,8 @@ SplitView {
publicKey: switchOwnProfile.checked ? "0xdeadbeef" : "0xrandomguy" publicKey: switchOwnProfile.checked ? "0xdeadbeef" : "0xrandomguy"
onCloseRequested: logs.logEvent("closeRequested()")
profileStore: QtObject { profileStore: QtObject {
readonly property string pubkey: "0xdeadbeef" readonly property string pubkey: "0xdeadbeef"
readonly property string ensName: name.text readonly property string ensName: name.text
@ -169,6 +173,209 @@ SplitView {
logs.logEvent("contactsStore::verifiedUntrustworthy", ["publicKey"], arguments) 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 { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Label { text: "localNickname:" }
TextField {
id: localNickname
text: "MockNickname"
placeholderText: "Local Nickname"
}
Label { text: "displayName:" } Label { text: "displayName:" }
TextField { TextField {
id: displayName id: displayName
@ -237,7 +450,7 @@ SplitView {
TextField { TextField {
id: name id: name
enabled: ensVerified.checked enabled: ensVerified.checked
text: ensVerified.checked ? "mock-ens-name" : "" text: ensVerified.checked ? "mock-ens-name.eth" : ""
placeholderText: "ENS name" placeholderText: "ENS name"
} }
} }
@ -290,6 +503,7 @@ SplitView {
TextField { TextField {
Layout.fillWidth: true Layout.fillWidth: true
id: bio id: bio
selectByMouse: true
text: "Hi, I am Alex. I'm an indie developer who mainly works on web products. 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. 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 This signal is emitted when the ToastMessage contains a url and this url
is clicked by the user. is clicked by the user.
*/ */
signal linkActivated(var link) signal linkActivated(string link)
QtObject { QtObject {
id: d id: d

View File

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

View File

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

View File

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

View File

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

View File

@ -107,6 +107,7 @@ StatusSectionLayout {
profileStore: root.store.profileStore profileStore: root.store.profileStore
privacyStore: root.store.privacyStore privacyStore: root.store.privacyStore
contactsStore: root.store.contactsStore contactsStore: root.store.contactsStore
communitiesModel: root.store.communitiesList
sectionTitle: root.store.getNameForSubsection(Constants.settingsSubsection.profile) sectionTitle: root.store.getNameForSubsection(Constants.settingsSubsection.profile)
contentWidth: d.contentWidth 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 ProfileStore profileStore
property PrivacyStore privacyStore property PrivacyStore privacyStore
property ContactsStore contactsStore property ContactsStore contactsStore
property var communitiesModel
titleRowComponentLoader.sourceComponent: StatusButton { titleRowComponentLoader.sourceComponent: StatusButton {
objectName: "profileSettingsChangePasswordButton" objectName: "profileSettingsChangePasswordButton"
@ -72,6 +73,7 @@ SettingsContentBase {
profileStore: root.profileStore profileStore: root.profileStore
privacyStore: root.privacyStore privacyStore: root.privacyStore
walletStore: root.walletStore walletStore: root.walletStore
communitiesModel: root.communitiesModel
onVisibleChanged: if (visible) stackLayout.Layout.preferredHeight = settingsView.implicitHeight onVisibleChanged: if (visible) stackLayout.Layout.preferredHeight = settingsView.implicitHeight
Component.onCompleted: stackLayout.Layout.preferredHeight = Qt.binding(() => settingsView.implicitHeight) Component.onCompleted: stackLayout.Layout.preferredHeight = Qt.binding(() => settingsView.implicitHeight)
@ -82,6 +84,7 @@ SettingsContentBase {
profileStore: root.profileStore profileStore: root.profileStore
contactsStore: root.contactsStore contactsStore: root.contactsStore
communitiesModel: root.communitiesModel
dirtyValues: settingsView.dirtyValues dirtyValues: settingsView.dirtyValues
dirty: settingsView.dirty dirty: settingsView.dirty

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -159,6 +159,7 @@ Column {
anchors.centerIn: parent anchors.centerIn: parent
text: "GIF" text: "GIF"
font.pixelSize: 13 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 setNthEnabledSectionActive(int nthSection)
signal appSectionBySectionTypeChanged(int sectionType, int subsection) signal appSectionBySectionTypeChanged(int sectionType, int subsection)
signal openSendModal(string address)
signal switchToCommunity(string communityId)
signal playSendMessageSound() signal playSendMessageSound()
signal playNotificationSound() signal playNotificationSound()
signal playErrorSound() signal playErrorSound()