355 lines
11 KiB
QML
355 lines
11 KiB
QML
import QtQuick 2.15
|
|
import QtQuick.Controls 2.15
|
|
|
|
import StatusQ 0.1
|
|
import StatusQ.Core 0.1
|
|
import StatusQ.Components 0.1
|
|
import StatusQ.Controls 0.1
|
|
import StatusQ.Core.Theme 0.1
|
|
import StatusQ.Core.Utils 0.1
|
|
|
|
import SortFilterProxyModel 0.2
|
|
|
|
import utils 1.0
|
|
|
|
StatusListView {
|
|
id: root
|
|
|
|
required property var selectedSharedAddressesMap // Map[address, [selected, isAirdrop]]
|
|
|
|
property var walletAssetsModel
|
|
property bool hasPermissions
|
|
|
|
property var uniquePermissionAssetsKeys
|
|
property var uniquePermissionCollectiblesKeys
|
|
|
|
property var walletCollectiblesModel
|
|
property var communityCollectiblesModel
|
|
property string communityId
|
|
|
|
property var getCurrencyAmount: function (balance, symbol){}
|
|
|
|
signal toggleAddressSelection(string keyUid, string address)
|
|
signal airdropAddressSelected (string address)
|
|
|
|
leftMargin: d.absLeftMargin
|
|
topMargin: Style.current.padding
|
|
rightMargin: Style.current.padding
|
|
bottomMargin: Style.current.padding
|
|
|
|
|
|
ModelChangeTracker {
|
|
id: communityCollectiblesModelTracker
|
|
|
|
model: communityCollectiblesModel
|
|
|
|
onRevisionChanged: {
|
|
const collectibles = ModelUtils.modelToArray(
|
|
communityCollectiblesModel,
|
|
["key", "name", "symbol", "icon", "communityId"])
|
|
.filter(e => e.communityId === communityId)
|
|
|
|
const keysToNames = {}
|
|
|
|
for (const c of collectibles)
|
|
keysToNames[c.key] = {
|
|
name: c.name,
|
|
symbol: c.symbol,
|
|
icon: c.icon
|
|
}
|
|
|
|
d.collectiblesMetadata
|
|
= uniquePermissionCollectiblesKeys.map(k => keysToNames[k])
|
|
d.collectiblesNames = d.collectiblesMetadata.map(c => c.name)
|
|
|
|
const namesToSymbols = {}
|
|
d.collectiblesMetadata.forEach(c => namesToSymbols[c.name] = c.symbol)
|
|
d.collectiblesNamesToSymbols = namesToSymbols
|
|
|
|
const namesToImages = {}
|
|
d.collectiblesMetadata.forEach(c => namesToImages[c.name] = c.icon)
|
|
d.collectiblesNamesToImages = namesToImages
|
|
}
|
|
|
|
Component.onCompleted: revisionChanged()
|
|
}
|
|
|
|
ModelChangeTracker {
|
|
id: filteredWalletCollectiblesTracker
|
|
|
|
model: filteredWalletCollectibles
|
|
}
|
|
|
|
QtObject {
|
|
id: d
|
|
|
|
property var collectiblesMetadata: []
|
|
property var collectiblesNames: []
|
|
property var collectiblesNamesToSymbols: ({})
|
|
property var collectiblesNamesToImages: ({})
|
|
|
|
readonly property int selectedSharedAddressesCount: root.selectedSharedAddressesMap.size
|
|
|
|
// UI
|
|
readonly property int absLeftMargin: 12
|
|
|
|
readonly property ButtonGroup airdropGroup: ButtonGroup {
|
|
exclusive: true
|
|
}
|
|
|
|
readonly property ButtonGroup addressesGroup: ButtonGroup {
|
|
exclusive: false
|
|
}
|
|
|
|
function getTotalBalance(balances, decimals) {
|
|
let totalBalance = 0
|
|
for(let i = 0; i < balances.count; i++) {
|
|
let balancePerAddressPerChain = ModelUtils.get(balances, i)
|
|
totalBalance += AmountsArithmetic.toNumber(balancePerAddressPerChain.balance, decimals)
|
|
}
|
|
return totalBalance
|
|
}
|
|
}
|
|
|
|
SortFilterProxyModel {
|
|
id: filteredWalletCollectibles
|
|
|
|
sourceModel: walletCollectiblesModel
|
|
|
|
filters: [
|
|
ValueFilter {
|
|
roleName: "communityId"
|
|
value: root.communityId
|
|
},
|
|
FastExpressionFilter {
|
|
expression: d.collectiblesNames.includes(model.name)
|
|
expectedRoles: ["name"]
|
|
}
|
|
]
|
|
}
|
|
|
|
ListModel {
|
|
id: walletAccountCollectiblesModel
|
|
|
|
readonly property var data: {
|
|
filteredWalletCollectiblesTracker.revision
|
|
const usedCollectibles = ModelUtils.modelToArray(filteredWalletCollectibles)
|
|
|
|
const collectiblesGroupingMap = {}
|
|
|
|
for (const c of usedCollectibles) {
|
|
const key = `${c.name}_${c.ownership[0].accountAddress}`
|
|
|
|
if (!collectiblesGroupingMap[key])
|
|
collectiblesGroupingMap[key] = []
|
|
|
|
collectiblesGroupingMap[key].push(c)
|
|
}
|
|
|
|
const collectiblesGrouped = Object.values(collectiblesGroupingMap).map(e => {
|
|
const firstEntry = e[0]
|
|
|
|
return {
|
|
symbol: d.collectiblesNamesToSymbols[firstEntry.name] || "",
|
|
enabledNetworkBalance: e.length,
|
|
account: firstEntry.ownership[0].accountAddress,
|
|
imageUrl: d.collectiblesNamesToImages[firstEntry.name] || ""
|
|
}
|
|
})
|
|
|
|
return collectiblesGrouped
|
|
}
|
|
|
|
onDataChanged: {
|
|
clear()
|
|
append(data)
|
|
}
|
|
}
|
|
|
|
spacing: Style.current.halfPadding
|
|
delegate: StatusListItem {
|
|
readonly property string address: model.address.toLowerCase()
|
|
readonly property int tokenCount: tagsCount
|
|
|
|
id: listItem
|
|
width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin
|
|
statusListItemTitle.font.weight: Font.Medium
|
|
title: model.name
|
|
tertiaryTitle: root.hasPermissions && !tagsCount ? qsTr("No relevant tokens") : ""
|
|
|
|
onClicked: shareAddressCheckbox.toggle()
|
|
|
|
SubmodelProxyModel {
|
|
id: filteredBalances
|
|
sourceModel: root.walletAssetsModel
|
|
submodelRoleName: "balances"
|
|
delegateModel: SortFilterProxyModel {
|
|
sourceModel: submodel
|
|
|
|
filters: RegExpFilter {
|
|
roleName: "account"
|
|
pattern: listItem.address
|
|
syntax: RegExpFilter.FixedString
|
|
caseSensitivity: Qt.CaseInsensitive
|
|
}
|
|
}
|
|
}
|
|
|
|
SortFilterProxyModel {
|
|
id: accountCollectiblesModel
|
|
|
|
sourceModel: walletAccountCollectiblesModel
|
|
|
|
filters: RegExpFilter {
|
|
roleName: "account"
|
|
pattern: listItem.address
|
|
syntax: RegExpFilter.FixedString
|
|
caseSensitivity: Qt.CaseInsensitive
|
|
}
|
|
}
|
|
|
|
ConcatModel {
|
|
id: concatModel
|
|
|
|
markerRoleName: "type"
|
|
|
|
sources: [
|
|
SourceModel {
|
|
model: walletAccountAssetsModel
|
|
markerRoleValue: Constants.TokenType.ERC20
|
|
},
|
|
SourceModel {
|
|
model: accountCollectiblesModel
|
|
markerRoleValue: Constants.TokenType.ERC721
|
|
}
|
|
]
|
|
}
|
|
|
|
tagsModel: SortFilterProxyModel {
|
|
sourceModel: concatModel
|
|
|
|
filters: ValueFilter {
|
|
roleName: "enabledNetworkBalance"
|
|
value: 0
|
|
inverted: true
|
|
}
|
|
|
|
sorters: [
|
|
RoleSorter {
|
|
roleName: "symbol"
|
|
}
|
|
]
|
|
}
|
|
|
|
SortFilterProxyModel {
|
|
id: walletAccountAssetsModel
|
|
|
|
sourceModel: filteredBalances
|
|
|
|
function filterPredicate(symbol) {
|
|
return root.uniquePermissionAssetsKeys.includes(symbol.toUpperCase())
|
|
}
|
|
|
|
proxyRoles: [
|
|
FastExpressionRole {
|
|
name: "enabledNetworkBalance"
|
|
expression: d.getTotalBalance(model.balances, model.decimals)
|
|
expectedRoles: ["balances", "decimals"]
|
|
},
|
|
FastExpressionRole {
|
|
name: "imageUrl"
|
|
|
|
function getIcon(symbol) {
|
|
return Constants.tokenIcon(symbol.toUpperCase())
|
|
}
|
|
|
|
// Singletons cannot be used directly in sfpm's expressions
|
|
expression: getIcon(model.symbol)
|
|
expectedRoles: ["symbol"]
|
|
}
|
|
]
|
|
|
|
filters: FastExpressionFilter {
|
|
expression: walletAccountAssetsModel.filterPredicate(model.symbol)
|
|
expectedRoles: ["symbol"]
|
|
}
|
|
}
|
|
|
|
statusListItemInlineTagsSlot.spacing: Style.current.padding
|
|
|
|
tagsDelegate: Row {
|
|
spacing: 4
|
|
StatusRoundedImage {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: 16
|
|
height: 16
|
|
image.source: model.imageUrl
|
|
}
|
|
StatusBaseText {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
font.pixelSize: Theme.tertiaryTextFontSize
|
|
|
|
readonly property int type: model.type
|
|
|
|
text: {
|
|
if (type === Constants.TokenType.ERC20)
|
|
return LocaleUtils.currencyAmountToLocaleString(
|
|
root.getCurrencyAmount(model.enabledNetworkBalance,
|
|
model.symbol))
|
|
|
|
return LocaleUtils.numberToLocaleString(model.enabledNetworkBalance)
|
|
+ " " + model.symbol
|
|
}
|
|
}
|
|
}
|
|
|
|
asset.color: !!model.color ? model.color : ""
|
|
asset.emoji: model.emoji
|
|
asset.name: !model.emoji ? "filled-account": ""
|
|
asset.letterSize: 14
|
|
asset.isLetterIdenticon: !!model.emoji
|
|
asset.isImage: asset.isLetterIdenticon
|
|
|
|
components: [
|
|
StatusFlatButton {
|
|
ButtonGroup.group: d.airdropGroup
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
icon.name: "airdrop"
|
|
icon.color: hovered ? Theme.palette.primaryColor3 :
|
|
checked ? Theme.palette.primaryColor1
|
|
: disabledTextColor
|
|
checkable: true
|
|
checked: {
|
|
const obj = root.selectedSharedAddressesMap.get(listItem.address)
|
|
if (!!obj) {
|
|
return obj.isAirdrop
|
|
}
|
|
return false
|
|
}
|
|
enabled: shareAddressCheckbox.checked && d.selectedSharedAddressesCount > 1 // last cannot be unchecked
|
|
visible: shareAddressCheckbox.checked
|
|
opacity: enabled ? 1.0 : 0.3
|
|
|
|
onToggled: root.airdropAddressSelected(listItem.address)
|
|
|
|
StatusToolTip {
|
|
text: qsTr("Use this address for any Community airdrops")
|
|
visible: parent.hovered
|
|
delay: 500
|
|
}
|
|
},
|
|
StatusCheckBox {
|
|
id: shareAddressCheckbox
|
|
|
|
ButtonGroup.group: d.addressesGroup
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
checkable: true
|
|
checked: root.selectedSharedAddressesMap.has(listItem.address)
|
|
enabled: !(d.selectedSharedAddressesCount === 1 && checked) // last cannot be unchecked
|
|
|
|
onToggled: root.toggleAddressSelection(model.keyUid, listItem.address)
|
|
}
|
|
]
|
|
}
|
|
}
|