mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-20 18:48:47 +00:00
feat(wallet): Send modal community tokens always grouped and listview sections changed
Closes #13430
This commit is contained in:
parent
7736cd8950
commit
5ff2bfa4d0
@ -74,10 +74,10 @@ QtObject:
|
||||
proc getCollectionData(self: CollectiblesEntry): backend.CollectionData =
|
||||
return self.data.collectionData.get()
|
||||
|
||||
proc hasCommunityData(self: CollectiblesEntry): bool =
|
||||
proc hasCommunityData*(self: CollectiblesEntry): bool =
|
||||
return self.data != nil and isSome(self.data.communityData)
|
||||
|
||||
proc getCommunityData(self: CollectiblesEntry): backend.CommunityData =
|
||||
proc getCommunityData*(self: CollectiblesEntry): backend.CommunityData =
|
||||
return self.data.communityData.get()
|
||||
|
||||
proc hasOwnership(self: CollectiblesEntry): bool =
|
||||
@ -364,4 +364,4 @@ QtObject:
|
||||
tokenID: stint.u256(0)
|
||||
)
|
||||
let extradata = ExtraData()
|
||||
return newCollectibleDetailsBasicEntry(id, extradata)
|
||||
return newCollectibleDetailsBasicEntry(id, extradata)
|
||||
|
@ -9,6 +9,7 @@ type
|
||||
collectionId: string
|
||||
collectionName: string
|
||||
isCollection: bool
|
||||
communityId: string
|
||||
|
||||
proc initItem*(
|
||||
id: string,
|
||||
@ -17,7 +18,8 @@ proc initItem*(
|
||||
iconUrl: string,
|
||||
collectionId: string,
|
||||
collectionName: string,
|
||||
isCollection: bool
|
||||
isCollection: bool,
|
||||
communityId: string,
|
||||
): Item =
|
||||
result.id = id
|
||||
result.chainId = chainId
|
||||
@ -26,6 +28,7 @@ proc initItem*(
|
||||
result.collectionId = collectionId
|
||||
result.collectionName = collectionName
|
||||
result.isCollection = isCollection
|
||||
result.communityId = communityId
|
||||
|
||||
proc `$`*(self: Item): string =
|
||||
result = fmt"""CollectiblesNestedEntry(
|
||||
@ -36,6 +39,7 @@ proc `$`*(self: Item): string =
|
||||
collectionId: {self.collectionId},
|
||||
collectionName: {self.collectionName},
|
||||
isCollection: {self.isCollection},
|
||||
communityId: {self.communityId},
|
||||
]"""
|
||||
|
||||
proc getId*(self: Item): string =
|
||||
@ -58,3 +62,6 @@ proc getCollectionName*(self: Item): string =
|
||||
|
||||
proc getIsCollection*(self: Item): bool =
|
||||
return self.isCollection
|
||||
|
||||
proc getCommunityId*(self: Item): string =
|
||||
return self.communityId
|
||||
|
@ -15,6 +15,7 @@ type
|
||||
CollectionUid
|
||||
CollectionName
|
||||
IsCollection
|
||||
CommunityId
|
||||
|
||||
QtObject:
|
||||
type
|
||||
@ -80,6 +81,7 @@ QtObject:
|
||||
CollectiblesNestedRole.CollectionUid.int:"collectionUid",
|
||||
CollectiblesNestedRole.CollectionName.int:"collectionName",
|
||||
CollectiblesNestedRole.IsCollection.int:"isCollection",
|
||||
CollectiblesNestedRole.CommunityId.int:"communityId",
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
@ -107,6 +109,8 @@ QtObject:
|
||||
result = newQVariant(item.getCollectionName())
|
||||
of CollectiblesNestedRole.IsCollection:
|
||||
result = newQVariant(item.getIsCollection())
|
||||
of CollectiblesNestedRole.CommunityId:
|
||||
result = newQVariant(item.getCommunityId())
|
||||
|
||||
proc rowData(self: Model, index: int, column: string): string {.slot.} =
|
||||
if (index >= self.items.len):
|
||||
@ -120,6 +124,7 @@ QtObject:
|
||||
of "collectionUid": result = item.getCollectionId()
|
||||
of "collectionName": result = item.getCollectionName()
|
||||
of "isCollection": result = $item.getIsCollection()
|
||||
of "communityId": result = item.getCommunityId()
|
||||
|
||||
proc getCollectiblesPerCollectionId(items: seq[flat_item.CollectiblesEntry]): Table[string, seq[flat_item.CollectiblesEntry]] =
|
||||
var collectiblesPerCollection = initTable[string, seq[flat_item.CollectiblesEntry]]()
|
||||
@ -140,10 +145,16 @@ QtObject:
|
||||
for collectionId, collectionCollectibles in collectiblesPerCollection.pairs:
|
||||
if self.currentCollectionUid == "":
|
||||
# No collection selected
|
||||
# If the collection contains more than 1 collectible, we add a single collection item
|
||||
# If the collection contains more than 1 collectible or if collectibles has community data, we add a single collection item
|
||||
# Otherwise, we add the collectible
|
||||
if collectionCollectibles.len > 1:
|
||||
let collectionItem = collectibleToCollectionNestedItem(collectionCollectibles[0])
|
||||
var hasCommunityData: bool
|
||||
var collectionItem: nested_item.Item
|
||||
if collectionCollectibles.len > 0:
|
||||
let firstItem = collectionCollectibles[0]
|
||||
hasCommunityData = firstItem.hasCommunityData()
|
||||
collectionItem = collectibleToCollectionNestedItem(firstItem)
|
||||
|
||||
if collectionCollectibles.len > 1 or hasCommunityData:
|
||||
self.items.add(collectionItem)
|
||||
else:
|
||||
for collectible in collectionCollectibles:
|
||||
|
@ -9,7 +9,8 @@ proc collectibleToCollectibleNestedItem*(flatItem: flat_item.CollectiblesEntry):
|
||||
flatItem.getImageURL(),
|
||||
flatItem.getCollectionID(),
|
||||
flatItem.getCollectionName(),
|
||||
false
|
||||
false,
|
||||
flatItem.getCommunityID()
|
||||
)
|
||||
|
||||
proc collectibleToCollectionNestedItem*(flatItem: flat_item.CollectiblesEntry): nested_item.Item =
|
||||
@ -20,5 +21,6 @@ proc collectibleToCollectionNestedItem*(flatItem: flat_item.CollectiblesEntry):
|
||||
flatItem.getCollectionImageURL(),
|
||||
flatItem.getCollectionID(),
|
||||
flatItem.getCollectionName(),
|
||||
true
|
||||
true,
|
||||
flatItem.getCommunityID()
|
||||
)
|
||||
|
@ -37,7 +37,16 @@ ListModel {
|
||||
collectionUid: "custom",
|
||||
collectionName: "Custom",
|
||||
isCollection: false,
|
||||
}
|
||||
},
|
||||
{
|
||||
uid: "ID-Community1",
|
||||
chainId: 1,
|
||||
name: "Community Admin Token",
|
||||
iconUrl: ModelsData.collectibles.mana,
|
||||
collectionUid: "community-uid-1",
|
||||
isCollection: false,
|
||||
communityId: "community-id-1"
|
||||
},
|
||||
]
|
||||
|
||||
readonly property var criptoKittiesData: [
|
||||
|
@ -9,13 +9,15 @@ import StatusQ.Popups.Dialog 0.1
|
||||
import utils 1.0
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
signal openInfoPopup()
|
||||
property alias text: sectionTitle.text
|
||||
|
||||
spacing: 0
|
||||
|
||||
StatusDialogDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.current.padding
|
||||
Layout.bottomMargin: Style.current.halfPadding
|
||||
}
|
||||
RowLayout {
|
||||
@ -24,13 +26,15 @@ ColumnLayout {
|
||||
Layout.rightMargin: Style.current.smallPadding
|
||||
Layout.bottomMargin: 4
|
||||
StatusBaseText {
|
||||
text: qsTr("Community assets")
|
||||
id: sectionTitle
|
||||
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
StatusFlatButton {
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
visible: !!root.text
|
||||
icon.name: "info"
|
||||
textColor: Theme.palette.baseColor1
|
||||
horizontalPadding: 0
|
||||
@ -39,4 +43,3 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,4 +67,24 @@ QtObject {
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
function assetsSectionTitle(sectionNeeded, hasCommunityTokens, isInsideCollection, isERC20List) {
|
||||
// if we have non-empty section name, setting section's "visible" property to false does not work properly.
|
||||
// So we have to return empty section here if we want to hide it
|
||||
let title = ""
|
||||
if (!isInsideCollection) {
|
||||
if (sectionNeeded) {
|
||||
title = qsTr("Community minted")
|
||||
}
|
||||
else {
|
||||
if (!isERC20List) {
|
||||
// Show "Other" only if there are "Community minted" tokens on the list
|
||||
if (hasCommunityTokens) {
|
||||
title = qsTr("Other")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return title
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import StatusQ.Core.Theme 0.1
|
||||
|
||||
StatusInput {
|
||||
property bool showTopBorder: false
|
||||
property bool showBottomBorder: true
|
||||
|
||||
placeholderText: qsTr("Search")
|
||||
input.implicitHeight: 56
|
||||
@ -23,6 +24,7 @@ StatusInput {
|
||||
color: Theme.palette.baseColor2
|
||||
}
|
||||
Rectangle {
|
||||
visible: showBottomBorder
|
||||
anchors.bottom: parent.bottom
|
||||
height: 1
|
||||
width: parent.width
|
||||
|
@ -5,6 +5,7 @@ import QtQuick.Layouts 1.13
|
||||
|
||||
import shared.controls 1.0
|
||||
import shared.popups 1.0
|
||||
import shared.popups.send 1.0
|
||||
import utils 1.0
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
@ -67,7 +68,7 @@ Item {
|
||||
readonly property var updateSearchText: Backpressure.debounce(root, 1000, function(inputText) {
|
||||
searchText = inputText
|
||||
})
|
||||
|
||||
|
||||
function isAsset(type) {
|
||||
return type === Constants.TokenType.ERC20
|
||||
}
|
||||
@ -116,6 +117,13 @@ Item {
|
||||
|
||||
property var collectibleComboBoxModel: SortFilterProxyModel {
|
||||
sourceModel: d.collectibleNetworksJointModel
|
||||
proxyRoles: [
|
||||
FastExpressionRole {
|
||||
name: "isCommunityAsset"
|
||||
expression: !!model.communityId
|
||||
expectedRoles: ["communityId"]
|
||||
}
|
||||
]
|
||||
filters: [
|
||||
ExpressionFilter {
|
||||
expression: {
|
||||
@ -123,10 +131,16 @@ Item {
|
||||
}
|
||||
}
|
||||
]
|
||||
sorters: RoleSorter {
|
||||
roleName: "isCollection"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
}
|
||||
sorters: [
|
||||
RoleSorter {
|
||||
roleName: "isCommunityAsset"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "isCollection"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
readonly property string searchPlaceholderText: {
|
||||
@ -149,6 +163,7 @@ Item {
|
||||
readonly property int collectibleContentIconSize: 28
|
||||
readonly property int assetContentTextSize: 28
|
||||
readonly property int collectibleContentTextSize: 15
|
||||
|
||||
}
|
||||
|
||||
HoldingItemSelector {
|
||||
@ -158,6 +173,8 @@ Item {
|
||||
|
||||
defaultIconSource: Style.png("tokens/DEFAULT-TOKEN@3x")
|
||||
placeholderText: d.isCurrentBrowsingTypeAsset ? qsTr("Select token") : qsTr("Select collectible")
|
||||
property bool hasCommunityTokens: false
|
||||
|
||||
comboBoxDelegate: Item {
|
||||
property var itemModel: model // read 'model' from the delegate's context
|
||||
width: loader.width
|
||||
@ -192,26 +209,28 @@ Item {
|
||||
itemTextFn: d.isCurrentBrowsingTypeAsset ? d.assetTextFn : d.collectibleTextFn
|
||||
itemIconSourceFn: d.isCurrentBrowsingTypeAsset ? d.assetIconSourceFn : d.collectibleIconSourceFn
|
||||
comboBoxModel: d.isCurrentBrowsingTypeAsset ? root.assetsModel : d.collectibleComboBoxModel
|
||||
onComboBoxModelChanged: updateHasCommunityTokens()
|
||||
|
||||
function updateHasCommunityTokens() {
|
||||
if (comboBoxModel.count > 0) {
|
||||
const item = comboBoxModel.get(0)
|
||||
holdingItemSelector.hasCommunityTokens = item.isCommunityAsset
|
||||
}
|
||||
}
|
||||
|
||||
contentIconSize: d.isAsset(d.currentHoldingType) ? d.assetContentIconSize : d.collectibleContentIconSize
|
||||
contentTextSize: d.isAsset(d.currentHoldingType) ? d.assetContentTextSize : d.collectibleContentTextSize
|
||||
comboBoxListViewSection.property: "isCommunityAsset"
|
||||
comboBoxListViewSection.delegate: Loader {
|
||||
width: ListView.view.width
|
||||
required property string section
|
||||
sourceComponent: d.isCurrentBrowsingTypeAsset && section === "true" ? sectionDelegate : null
|
||||
}
|
||||
comboBoxListViewSection.delegate: AssetsSectionDelegate {
|
||||
height: !!text ? implicitHeight : 0 // binding loop here. Without implicitHeight, binding on height does not work properly - height is 0 in some cases
|
||||
width: ListView.view.width
|
||||
required property bool section
|
||||
text: Helpers.assetsSectionTitle(section, holdingItemSelector.hasCommunityTokens, d.isBrowsingCollection, d.isCurrentBrowsingTypeAsset)
|
||||
onOpenInfoPopup: Global.openPopup(communityInfoPopupCmp)
|
||||
}
|
||||
comboBoxControl.popup.onClosed: comboBoxControl.popup.contentItem.headerItem.clear()
|
||||
}
|
||||
|
||||
Component {
|
||||
id: sectionDelegate
|
||||
AssetsSectionDelegate {
|
||||
width: parent.width
|
||||
onOpenInfoPopup: Global.openPopup(communityInfoPopupCmp)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: communityInfoPopupCmp
|
||||
CommunityAssetsInfoPopup {}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.14
|
||||
import QtQuick 2.15
|
||||
import QtQml 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
@ -13,6 +14,7 @@ import utils 1.0
|
||||
|
||||
import shared.controls 1.0
|
||||
import shared.popups 1.0
|
||||
import shared.popups.send 1.0
|
||||
import "../controls"
|
||||
|
||||
Item {
|
||||
@ -77,6 +79,7 @@ Item {
|
||||
}
|
||||
|
||||
readonly property bool isBrowsingTypeERC20: root.browsingHoldingType === Constants.TokenType.ERC20
|
||||
readonly property bool isBrowsingCollection: !isBrowsingTypeERC20 && !!root.collectibles && root.collectibles.currentCollectionUid !== ""
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
@ -132,15 +135,73 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
header: d.isBrowsingTypeERC20 ? tokenHeader : collectibleHeader
|
||||
model: d.isBrowsingTypeERC20 ? root.assets : collectiblesModel
|
||||
delegate: d.isBrowsingTypeERC20 ? tokenDelegate : collectiblesDelegate
|
||||
visible: d.isBrowsingTypeERC20
|
||||
header: tokenHeader
|
||||
model: root.assets
|
||||
delegate: tokenDelegate
|
||||
|
||||
property bool hasCommunityTokens: false
|
||||
function updateHasCommunityTokens() {
|
||||
if (count > 0) {
|
||||
// For assets community minted tokens are at the end of the list by design.
|
||||
// It is faster than iterate over all the items and check their isCommunityAsset role
|
||||
const isCommunityAsset = model.get(count - 1).isCommunityAsset
|
||||
hasCommunityTokens = hasCommunityTokens || isCommunityAsset
|
||||
}
|
||||
}
|
||||
|
||||
onCountChanged: updateHasCommunityTokens()
|
||||
section {
|
||||
property: "isCommunityAsset"
|
||||
delegate: Loader {
|
||||
width: ListView.view.width
|
||||
required property string section
|
||||
sourceComponent: d.isBrowsingTypeERC20 && section === "true" ? sectionDelegate : null
|
||||
required property bool section
|
||||
width: parent.width
|
||||
property string sectionTitle: Helpers.assetsSectionTitle(section, tokenList.hasCommunityTokens, d.isBrowsingCollection, d.isBrowsingTypeERC20)
|
||||
active: !!sectionTitle
|
||||
sourceComponent: AssetsSectionDelegate {
|
||||
text: parent.sectionTitle
|
||||
onOpenInfoPopup: Global.openPopup(communityInfoPopupCmp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We have a separate listview for collectibles because if we switch models within one listview,
|
||||
// sections are not displayed correctly, if we play with their "visible" or "height" properties
|
||||
// when we want to hide some sections. It feels like sections are not designed to be hidden and
|
||||
// do not hide well in complex scenarios.
|
||||
StatusListView {
|
||||
id: collectiblesList
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
visible: !d.isBrowsingTypeERC20
|
||||
header: collectibleHeader
|
||||
model: collectiblesModel
|
||||
delegate: collectiblesDelegate
|
||||
|
||||
property bool hasCommunityTokens: false
|
||||
onCountChanged: updateHasCommunityTokens()
|
||||
|
||||
function updateHasCommunityTokens() {
|
||||
if (count > 0) {
|
||||
const isCommunityAsset = model.get(0).isCommunityAsset
|
||||
hasCommunityTokens = hasCommunityTokens || isCommunityAsset
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
property: "isCommunityAsset"
|
||||
delegate: Loader {
|
||||
required property bool section
|
||||
width: parent.width
|
||||
property string sectionTitle: Helpers.assetsSectionTitle(section, collectiblesList.hasCommunityTokens, d.isBrowsingCollection, d.isBrowsingTypeERC20)
|
||||
active: !!sectionTitle
|
||||
sourceComponent: AssetsSectionDelegate {
|
||||
text: parent.sectionTitle
|
||||
onOpenInfoPopup: Global.openPopup(communityInfoPopupCmp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,6 +210,13 @@ Item {
|
||||
|
||||
property var collectiblesModel: SortFilterProxyModel {
|
||||
sourceModel: d.collectiblesNetworksJointModel
|
||||
proxyRoles: [
|
||||
FastExpressionRole {
|
||||
name: "isCommunityAsset"
|
||||
expression: !!model.communityId
|
||||
expectedRoles: ["communityId"]
|
||||
}
|
||||
]
|
||||
filters: [
|
||||
ExpressionFilter {
|
||||
expression: {
|
||||
@ -156,10 +224,16 @@ Item {
|
||||
}
|
||||
}
|
||||
]
|
||||
sorters: RoleSorter {
|
||||
roleName: "isCollection"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
}
|
||||
sorters: [
|
||||
RoleSorter {
|
||||
roleName: "isCommunityAsset"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: "isCollection"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Component {
|
||||
@ -168,7 +242,7 @@ Item {
|
||||
width: ListView.view.width
|
||||
selectedSenderAccount: root.selectedSenderAccount
|
||||
balancesModel: LeftJoinModel {
|
||||
leftModel: !!model & !!model.balances ? model.balances : nil
|
||||
leftModel: !!model & !!model.balances ? model.balances : null
|
||||
rightModel: root.networksModel
|
||||
joinRole: "chainId"
|
||||
}
|
||||
@ -186,6 +260,7 @@ Item {
|
||||
id: tokenHeader
|
||||
SearchBoxWithRightIcon {
|
||||
showTopBorder: !root.onlyAssets
|
||||
showBottomBorder: false
|
||||
width: ListView.view.width
|
||||
placeholderText: qsTr("Search for token or enter token address")
|
||||
onTextChanged: Qt.callLater(d.updateAssetSearchText, text)
|
||||
@ -213,7 +288,7 @@ Item {
|
||||
spacing: 0
|
||||
CollectibleBackButtonWithInfo {
|
||||
Layout.fillWidth: true
|
||||
visible: !!root.collectibles && root.collectibles.currentCollectionUid !== ""
|
||||
visible: d.isBrowsingCollection
|
||||
count: root.collectibles.count
|
||||
name: d.currentBrowsingCollectionName
|
||||
onBackClicked: root.collectibles.currentCollectionUid = ""
|
||||
@ -221,20 +296,13 @@ Item {
|
||||
SearchBoxWithRightIcon {
|
||||
Layout.fillWidth: true
|
||||
showTopBorder: true
|
||||
showBottomBorder: false
|
||||
placeholderText: qsTr("Search collectibles")
|
||||
onTextChanged: Qt.callLater(d.updateCollectibleSearchText, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: sectionDelegate
|
||||
AssetsSectionDelegate {
|
||||
width: parent.width
|
||||
onOpenInfoPopup: Global.openPopup(communityInfoPopupCmp)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: communityInfoPopupCmp
|
||||
CommunityAssetsInfoPopup {}
|
||||
|
2
vendor/status-go
vendored
2
vendor/status-go
vendored
@ -1 +1 @@
|
||||
Subproject commit cc708ce0ce83ef6d04f803b2562674dc6bf3b5b1
|
||||
Subproject commit 12d70e0ce45a7dcac78c7e323ab81d8a56d996ce
|
Loading…
x
Reference in New Issue
Block a user