mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-17 01:51:24 +00:00
3f8dfee3cd
* refactor(community_tokens): only fetch holders when going to the page Fixes #16307 Instead of fetching community token holders each time members change, we fetch when the page for the token is opened. It shows a small loading text then the resulting holders. If the list is already available (fetched previously, we show it directly). There is still the timer to refresh the list if you stay on the page. * add loading property to storybook
334 lines
12 KiB
QML
334 lines
12 KiB
QML
import QtQuick 2.15
|
|
import QtQuick.Layouts 1.14
|
|
import QtGraphicalEffects 1.0
|
|
|
|
import StatusQ 0.1
|
|
import StatusQ.Core 0.1
|
|
import StatusQ.Core.Theme 0.1
|
|
import StatusQ.Controls 0.1
|
|
import StatusQ.Components 0.1
|
|
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
|
|
|
import utils 1.0
|
|
import shared.panels 1.0
|
|
|
|
import AppLayouts.Communities.helpers 1.0
|
|
import AppLayouts.Communities.panels 1.0
|
|
|
|
import SortFilterProxyModel 0.2
|
|
|
|
StatusScrollView {
|
|
id: root
|
|
|
|
property int viewWidth: 560 // by design
|
|
property bool preview: false
|
|
property bool isOwnerTokenItem: false
|
|
|
|
// https://bugreports.qt.io/browse/QTBUG-84269
|
|
/* required */ property TokenObject token
|
|
/* required */ property string feeText
|
|
/* required */ property string feeErrorText
|
|
/* required */ property bool isFeeLoading
|
|
|
|
|
|
readonly property bool isAssetView: token.type === Constants.TokenType.ERC20
|
|
|
|
readonly property string name: token.name
|
|
readonly property string description: token.description
|
|
readonly property string symbol: token.symbol
|
|
readonly property int supply: token.supply
|
|
readonly property url artworkSource: token.artworkSource
|
|
readonly property rect artworkCropRect: token.artworkCropRect
|
|
readonly property bool infiniteSupply: token.infiniteSupply
|
|
readonly property int remainingTokens: root.preview ? root.supply : token.remainingTokens
|
|
readonly property int deployState: token.deployState
|
|
readonly property string accountName: token.accountName
|
|
readonly property string chainName: token.chainName
|
|
readonly property string chainId: token.chainId
|
|
readonly property string accountAddress: token.accountAddress
|
|
readonly property bool remotelyDestruct: token.remotelyDestruct
|
|
readonly property int remotelyDestructState: token.remotelyDestructState
|
|
readonly property bool transferable: token.transferable
|
|
readonly property string chainIcon: token.chainIcon
|
|
readonly property int decimals: token.decimals
|
|
readonly property int multiplierIndex: token.multiplierIndex
|
|
|
|
readonly property bool deploymentCompleted:
|
|
deployState === Constants.ContractTransactionStatus.Completed
|
|
|
|
readonly property string feeLabel:
|
|
isAssetView ? qsTr("Mint asset on %1").arg(token.chainName)
|
|
: qsTr("Mint collectible on %1").arg(token.chainName)
|
|
|
|
// Models:
|
|
property var tokenOwnersModel
|
|
property var membersModel
|
|
|
|
// Required for preview mode:
|
|
property var accounts
|
|
signal mintClicked()
|
|
|
|
signal airdropRequested(string address)
|
|
signal generalAirdropRequested
|
|
|
|
signal viewProfileRequested(string contactId)
|
|
signal viewMessagesRequested(string contactId)
|
|
|
|
signal remoteDestructRequested(string name, string address)
|
|
signal kickRequested(string name, string contactId, string address)
|
|
signal banRequested(string name, string contactId, string address)
|
|
|
|
signal startTokenHoldersManagement(int chainId, string address)
|
|
signal stopTokenHoldersManagement()
|
|
|
|
onVisibleChanged: {
|
|
if (visible) {
|
|
root.startTokenHoldersManagement(root.chainId, root.token.tokenAddress)
|
|
} else {
|
|
root.stopTokenHoldersManagement()
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: root.startTokenHoldersManagement(root.chainId, root.token.tokenAddress)
|
|
Component.onDestruction: root.stopTokenHoldersManagement()
|
|
|
|
QtObject {
|
|
id: d
|
|
|
|
readonly property int iconSize: 20
|
|
property bool loadingTokenHolders: false
|
|
|
|
readonly property var renamedTokenOwnersModel: RolesRenamingModel {
|
|
sourceModel: root.tokenOwnersModel
|
|
mapping: [
|
|
RoleRename {
|
|
from: "contactId"
|
|
to: "pubKey"
|
|
}
|
|
]
|
|
}
|
|
|
|
readonly property LeftJoinModel joinModel: LeftJoinModel {
|
|
leftModel: root.membersModel
|
|
rightModel: d.renamedTokenOwnersModel
|
|
|
|
joinRole: "pubKey"
|
|
}
|
|
}
|
|
|
|
padding: 0
|
|
contentWidth: mainLayout.width
|
|
contentHeight: mainLayout.height
|
|
|
|
ColumnLayout {
|
|
id: mainLayout
|
|
|
|
width: root.viewWidth
|
|
spacing: Style.current.padding
|
|
|
|
RowLayout {
|
|
visible: !root.preview && !root.deploymentCompleted
|
|
spacing: Style.current.halfPadding
|
|
|
|
StatusDotsLoadingIndicator { visible: (root.deployState === Constants.ContractTransactionStatus.InProgress) }
|
|
|
|
StatusIcon {
|
|
visible: (root.deployState === Constants.ContractTransactionStatus.Failed)
|
|
icon: "warning"
|
|
color: Theme.palette.dangerColor1
|
|
}
|
|
|
|
StatusBaseText {
|
|
elide: Text.ElideRight
|
|
font.pixelSize: Theme.primaryTextFontSize
|
|
text: (root.deployState === Constants.ContractTransactionStatus.InProgress) ?
|
|
(root.isAssetView ?
|
|
qsTr("Asset is being minted") : qsTr("Collectible is being minted")) :
|
|
(root.deployState === Constants.ContractTransactionStatus.Failed) ?
|
|
(root.isAssetView ? qsTr("Asset minting failed") : qsTr("Collectible minting failed")) : ""
|
|
color: (root.deployState === Constants.ContractTransactionStatus.Failed) ? Theme.palette.dangerColor1 : Theme.palette.directColor1
|
|
}
|
|
}
|
|
|
|
TokenInfoPanel {
|
|
Layout.fillWidth: true
|
|
|
|
token: root.token
|
|
}
|
|
|
|
RowLayout {
|
|
visible: root.preview
|
|
Layout.fillWidth: true
|
|
|
|
StatusIcon {
|
|
Layout.preferredWidth: d.iconSize
|
|
Layout.preferredHeight: d.iconSize
|
|
Layout.alignment: Qt.AlignTop
|
|
color: Theme.palette.baseColor1
|
|
icon: "info"
|
|
}
|
|
|
|
StatusBaseText {
|
|
Layout.fillWidth: true
|
|
wrapMode: Text.Wrap
|
|
font.pixelSize: Style.current.primaryTextFontSize
|
|
color: Theme.palette.baseColor1
|
|
text: qsTr("Review token details before minting it as they can't be edited later")
|
|
}
|
|
}
|
|
|
|
FeesBox {
|
|
id: feesBox
|
|
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: Style.current.padding
|
|
|
|
implicitWidth: 0
|
|
visible: root.preview
|
|
|
|
accountErrorText: root.feeErrorText
|
|
|
|
model: QtObject {
|
|
readonly property string title: root.feeLabel
|
|
readonly property string feeText: root.isFeeLoading ?
|
|
"" : root.feeText
|
|
readonly property bool error: root.feeErrorText !== ""
|
|
}
|
|
|
|
accountsSelector.model: root.accounts || null
|
|
accountsSelector.selectedAddress: root.token.accountAddress
|
|
|
|
Binding {
|
|
target: root.token
|
|
property: "accountAddress"
|
|
value: feesBox.accountsSelector.currentAccountAddress
|
|
}
|
|
|
|
Binding {
|
|
target: root.token
|
|
property: "accountName"
|
|
value: feesBox.accountsSelector.currentAccount.name
|
|
}
|
|
}
|
|
|
|
StatusButton {
|
|
Layout.preferredHeight: 44
|
|
Layout.alignment: Qt.AlignHCenter
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: Style.current.halfPadding
|
|
|
|
visible: root.preview
|
|
enabled: !root.isFeeLoading && root.feeErrorText === ""
|
|
|
|
text: qsTr("Mint")
|
|
|
|
onClicked: root.mintClicked()
|
|
}
|
|
|
|
Loader {
|
|
id: tokenHolderLoader
|
|
|
|
visible: !root.preview && root.deploymentCompleted
|
|
sourceComponent: isOwnerTokenItem ? tokenHolderContact : (token.tokenHoldersLoading ? tokenHoldersLoadingComponent : sortableTokenHolderPanel)
|
|
}
|
|
|
|
Component {
|
|
id: tokenHoldersLoadingComponent
|
|
|
|
StatusBaseText {
|
|
text: qsTr("Loading token holders...")
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: sortableTokenHolderPanel
|
|
|
|
SortableTokenHoldersPanel {
|
|
model: root.tokenOwnersModel
|
|
tokenName: root.name
|
|
showRemotelyDestructMenuItem: !root.isAssetView && root.remotelyDestruct
|
|
isAirdropEnabled: root.deploymentCompleted &&
|
|
(token.infiniteSupply || token.remainingTokens > 0)
|
|
multiplierIndex: root.multiplierIndex
|
|
|
|
Layout.topMargin: Style.current.padding
|
|
Layout.fillWidth: true
|
|
|
|
onViewProfileRequested: root.viewProfileRequested(contactId)
|
|
onViewMessagesRequested: root.viewMessagesRequested(contactId)
|
|
onAirdropRequested: root.airdropRequested(address)
|
|
onGeneralAirdropRequested: root.generalAirdropRequested()
|
|
onRemoteDestructRequested: root.remoteDestructRequested(name, address)
|
|
|
|
onKickRequested: root.kickRequested(name, contactId, address)
|
|
onBanRequested: root.banRequested(name, contactId, address)
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: tokenHolderContact
|
|
|
|
ColumnLayout {
|
|
Layout.topMargin: Style.current.padding
|
|
Layout.fillWidth: true
|
|
|
|
StatusBaseText {
|
|
Layout.fillWidth: true
|
|
|
|
wrapMode: Text.Wrap
|
|
font.pixelSize: Style.current.primaryTextFontSize
|
|
color: Theme.palette.baseColor1
|
|
|
|
text: qsTr("%1 token hodler").arg(root.name)
|
|
}
|
|
|
|
StatusInfoBoxPanel {
|
|
id: infoBoxPanel
|
|
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: Style.current.padding
|
|
|
|
enabled: root.deploymentCompleted && (token.infiniteSupply || token.remainingTokens > 0)
|
|
visible: d.joinModel.rowCount() === 0
|
|
title: qsTr("No hodlers just yet")
|
|
text: qsTr("You can Airdrop tokens to deserving Community members or to give individuals token-based permissions.")
|
|
buttonText: qsTr("Airdrop")
|
|
|
|
onClicked: root.generalAirdropRequested()
|
|
}
|
|
|
|
StatusListView {
|
|
id: tokenOwnerList
|
|
objectName: "tokenOwnerList"
|
|
|
|
clip: false
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
anchors.bottomMargin: Style.current.bigPadding
|
|
displayMarginEnd: anchors.bottomMargin
|
|
|
|
model: d.joinModel
|
|
delegate: StatusMemberListItem {
|
|
color: "transparent"
|
|
leftPadding: 0
|
|
rightPadding: 0
|
|
hoverEnabled: false
|
|
|
|
nickName: model.localNickname
|
|
userName: ProfileUtils.displayName("", model.ensName, model.displayName, model.alias)
|
|
pubKey: model.isEnsVerified ? "" : Utils.getCompressedPk(model.pubKey)
|
|
isContact: model.isContact
|
|
isVerified: model.verificationStatus === Constants.verificationStatus.verified
|
|
isUntrustworthy: model.trustStatus === Constants.trustStatus.untrustworthy
|
|
status: model.onlineStatus
|
|
icon.name: model.icon
|
|
icon.color: Utils.colorForPubkey(model.pubKey)
|
|
ringSettings.ringSpecModel: Utils.getColorHashAsJson(model.pubKey)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|