status-desktop/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml

1017 lines
40 KiB
QML
Raw Normal View History

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml 2.15
import StatusQ.Controls 0.1
import StatusQ.Core.Backpressure 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import AppLayouts.Communities.controls 1.0
import AppLayouts.Communities.helpers 1.0
import AppLayouts.Communities.layouts 1.0
import AppLayouts.Communities.popups 1.0
import AppLayouts.Communities.views 1.0
import AppLayouts.Wallet.helpers 1.0
import shared.controls 1.0
import utils 1.0
import shared.popups 1.0
import SortFilterProxyModel 0.2
StackView {
id: root
// General properties:
property int viewWidth: 560 // by design
property string previousPageName: depth > 1 ? qsTr("Back") : ""
required property string communityId
required property string communityName
required property string communityLogo
required property color communityColor
// User profile props:
required property bool isOwner
required property bool isTokenMasterOwner
required property bool isAdmin
readonly property bool isAdminOnly: root.isAdmin && !root.isPrivilegedTokenOwnerProfile
readonly property bool isPrivilegedTokenOwnerProfile: root.isOwner || root.isTokenMasterOwner
// Owner and TMaster token related properties:
readonly property bool arePrivilegedTokensDeployed: root.isOwnerTokenDeployed && root.isTMasterTokenDeployed
property bool isOwnerTokenDeployed: false
property bool isTMasterTokenDeployed: false
property bool anyPrivilegedTokenFailed: false
// It will monitorize if Owner and/or TMaster token items are included in the `tokensModel` despite the deployment state
property bool ownerOrTMasterTokenItemsExist: false
// Network related properties:
property var flatNetworks
readonly property int ownerTokenChainId: SQUtils.ModelUtils.get(root.tokensModel, "privilegesLevel", Constants.TokenPrivilegesLevel.Owner).chainId ?? 0
readonly property int chainIndex: NetworkModelHelpers.getChainIndexByChainId(root.flatNetworks, root.ownerTokenChainId)
readonly property string chainName: NetworkModelHelpers.getChainName(root.flatNetworks, chainIndex)
property string enabledChainIds
// Models:
property var tokensModel
property var membersModel
property var accounts // Expected roles: address, name, color, emoji, walletType
required property var referenceAssetsBySymbolModel
signal mintCollectible(var collectibleItem)
signal mintAsset(var assetItem)
signal mintOwnerToken(var ownerToken, var tMasterToken)
signal kickUserRequested(string contactId)
signal banUserRequested(string contactId)
signal remotelyDestructCollectibles(var walletsAndAmounts, // { [walletAddress (string), amount (int)] }
string tokenKey,
string accountAddress)
signal remotelyDestructAndBan(string contactId, string tokenKey, string accountAddress,
bool removeMessages)
signal remotelyDestructAndKick(string contactId, string tokenKey, string accountAddress)
signal burnToken(string tokenKey, string amount, string accountAddress)
signal airdropToken(string tokenKey, string amount, int type, var addresses)
signal deleteToken(string tokenKey)
signal refreshToken(string tokenKey)
signal registerDeployFeesSubscriber(var feeSubscriber)
signal registerSelfDestructFeesSubscriber(var feeSubscriber)
signal registerBurnTokenFeesSubscriber(var feeSubscriber)
signal startTokenHoldersManagement(int chainId, string address)
signal stopTokenHoldersManagement()
signal enableNetwork(int chainId)
function navigateBack() {
pop(StackView.Immediate)
}
function resetNavigation() {
pop(initialItem, StackView.Immediate)
}
// This method will be called from the outside from a different section like Airdrop or Permissions
function openNewTokenForm(isAssetView) {
resetNavigation()
if(root.isAdminOnly) {
// Admins can only see the initial tokens page. They cannot mint. Initial view.
return
}
if(root.arePrivilegedTokensDeployed) {
// Regular minting flow for Owner and TMaster owner, selecting the specific tab
const properties = { isAssetView }
root.push(newTokenViewComponent, properties, StackView.Immediate)
return
}
if(root.ownerOrTMasterTokenItemsExist) {
// Owner and TMaster tokens deployment action has been started at least ones but still without success. Initial view.
return
}
if(root.isOwner) {
// Owner and TMaster tokens to be deployed. Never tried.
root.push(ownerTokenViewComponent, StackView.Immediate)
return
}
}
QtObject {
id: d
property string networkThatIsNotActive
// Owner or TMaster token retry navigation
function retryPrivilegedToken(key, chainId, accountName, accountAddress) {
var properties = {
key: key,
chainId: chainId,
accountName: accountName,
accountAddress: accountAddress,
}
root.push(ownerTokenEditViewComponent, properties,
StackView.Immediate)
}
}
onVisibleChanged: {
if (!visible) {
return
}
// If the tokens' network is not activated, show a warning to the user
if (!root.enabledChainIds.includes(root.ownerTokenChainId)) {
d.networkThatIsNotActive = root.chainName
} else {
d.networkThatIsNotActive = ""
}
}
initialItem: SettingsPage {
implicitWidth: 0
title: qsTr("Tokens")
buttons: [
StatusButton {
objectName: "addNewItemButton"
text: qsTr("Mint token")
interactive: root.isPrivilegedTokenOwnerProfile && root.arePrivilegedTokensDeployed
onClicked: root.push(newTokenViewComponent, StackView.Immediate)
tooltip.text: root.isAdminOnly ? qsTr("In order to mint, you must hodl the TokenMaster token for %1").arg(root.communityName) : ""
}
]
contentItem: MintedTokensView {
model: SortFilterProxyModel {
sourceModel: root.tokensModel
proxyRoles: ExpressionRole {
name: "color"
expression: root.communityColor
}
}
isOwner: root.isOwner
isAdmin: root.isAdmin
communityName: root.communityName
communityId: root.communityId
anyPrivilegedTokenFailed: root.anyPrivilegedTokenFailed
onItemClicked: root.push(tokenViewComponent, { tokenKey }, StackView.Immediate)
onMintOwnerTokenClicked: root.push(ownerTokenViewComponent, StackView.Immediate)
onRetryOwnerTokenClicked: d.retryPrivilegedToken(tokenKey, chainId, accountName, accountAddress)
}
}
Component {
id: tokenObjectComponent
TokenObject {}
}
// Mint tokens possible view contents:
Component {
id: ownerTokenViewComponent
SettingsPage {
id: ownerTokenPage
title: qsTr("Mint Owner token")
contentItem: OwnerTokenWelcomeView {
viewWidth: root.viewWidth
communityLogo: root.communityLogo
communityColor: root.communityColor
communityName: root.communityName
onNextClicked: {
const chainId = !!root.flatNetworks? SQUtils.ModelUtils.getByKey(root.flatNetworks, "layer", 2).chainId : 0
const accountName = !!root.accounts && root.accounts.count > 0? SQUtils.ModelUtils.get(root.accounts, 0).name : ""
const accountAddress = !!root.accounts && root.accounts.count > 0? SQUtils.ModelUtils.get(root.accounts, 0).address : ""
d.retryPrivilegedToken("", chainId, accountName, accountAddress)
}
}
}
}
Component {
id: ownerTokenEditViewComponent
SettingsPage {
id: ownerTokenPage
property int chainId
property string accountName
property string accountAddress
title: qsTr("Mint Owner token")
contentItem: EditOwnerTokenView {
id: editOwnerTokenView
function signMintTransaction() {
root.mintOwnerToken(ownerToken, tMasterToken)
root.resetNavigation()
}
viewWidth: root.viewWidth
communityLogo: root.communityLogo
communityColor: root.communityColor
communityName: root.communityName
ownerToken.chainId: ownerTokenPage.chainId
ownerToken.accountName: ownerTokenPage.accountName
ownerToken.accountAddress: ownerTokenPage.accountAddress
tMasterToken.chainId: ownerTokenPage.chainId
tMasterToken.accountName: ownerTokenPage.accountName
tMasterToken.accountAddress: ownerTokenPage.accountAddress
flatNetworks: root.flatNetworks
accounts: root.accounts
feeText: feeSubscriber.feeText
feeErrorText: feeSubscriber.feeErrorText
isFeeLoading: !feeSubscriber.feesResponse
onMintClicked: signMintPopup.open()
DeployFeesSubscriber {
id: feeSubscriber
communityId: root.communityId
chainId: editOwnerTokenView.ownerToken.chainId
tokenType: editOwnerTokenView.ownerToken.type
isOwnerDeployment: editOwnerTokenView.ownerToken.isPrivilegedToken
accountAddress: editOwnerTokenView.ownerToken.accountAddress
enabled: editOwnerTokenView.visible || signMintPopup.visible
Component.onCompleted: root.registerDeployFeesSubscriber(feeSubscriber)
}
SignTransactionsPopup {
id: signMintPopup
title: qsTr("Sign transaction - Mint %1 tokens").arg(
editOwnerTokenView.communityName)
totalFeeText: editOwnerTokenView.isFeeLoading ?
"" : editOwnerTokenView.feeText
errorText: editOwnerTokenView.feeErrorText
accountName: editOwnerTokenView.ownerToken.accountName
model: QtObject {
readonly property string title: editOwnerTokenView.feeLabel
readonly property string feeText: signMintPopup.totalFeeText
readonly property bool error: editOwnerTokenView.feeErrorText !== ""
}
onSignTransactionClicked: editOwnerTokenView.signMintTransaction()
}
}
}
}
Component {
id: newTokenViewComponent
SettingsPage {
id: newTokenPage
readonly property string chainIcon: NetworkModelHelpers.getChainIconUrl(root.flatNetworks, root.chainIndex)
property TokenObject asset: TokenObject{
type: Constants.TokenType.ERC20
multiplierIndex: 18
// Minted tokens will use ALWAYS the same chain where the owner token was deployed.
chainId: root.ownerTokenChainId
chainName: root.chainName
chainIcon: newTokenPage.chainIcon
}
property TokenObject collectible: TokenObject {
type: Constants.TokenType.ERC721
// Minted tokens will use ALWAYS the same chain where the owner token was deployed.
chainId: root.ownerTokenChainId
chainName: root.chainName
chainIcon: newTokenPage.chainIcon
}
property bool isAssetView: false
property int validationMode: StatusInput.ValidationMode.OnlyWhenDirty
property string referenceName: ""
property string referenceSymbol: ""
title: qsTr("Mint token")
contentItem: ColumnLayout {
width: root.viewWidth
spacing: Theme.padding
StatusSwitchTabBar {
id: optionsTab
Layout.preferredWidth: root.viewWidth
currentIndex: newTokenPage.isAssetView ? 1 : 0
StatusSwitchTabButton {
id: collectiblesTab
text: qsTr("Collectibles")
}
StatusSwitchTabButton {
id: assetsTab
text: qsTr("Assets")
}
}
StackLayout {
Layout.preferredWidth: root.viewWidth
Layout.fillHeight: true
currentIndex: optionsTab.currentIndex
CustomEditCommunityTokenView {
id: newCollectibleView
isAssetView: false
validationMode: !newTokenPage.isAssetView
? newTokenPage.validationMode
: StatusInput.ValidationMode.OnlyWhenDirty
token: newTokenPage.collectible
}
CustomEditCommunityTokenView {
id: newAssetView
isAssetView: true
validationMode: newTokenPage.isAssetView
? newTokenPage.validationMode
: StatusInput.ValidationMode.OnlyWhenDirty
token: newTokenPage.asset
}
component CustomEditCommunityTokenView: EditCommunityTokenView {
id: editView
viewWidth: root.viewWidth
accounts: root.accounts
tokensModel: root.tokensModel
referenceAssetsBySymbolModel: root.referenceAssetsBySymbolModel
referenceName: newTokenPage.referenceName
referenceSymbol: newTokenPage.referenceSymbol
feeText: deployFeeSubscriber.feeText
feeErrorText: deployFeeSubscriber.feeErrorText
isFeeLoading: !deployFeeSubscriber.feesResponse
networkThatIsNotActive: d.networkThatIsNotActive
onEnableNetwork: {
root.enableNetwork(root.ownerTokenChainId)
d.networkThatIsNotActive = ""
}
onPreviewClicked: {
const properties = {
token: token
}
root.push(previewTokenViewComponent, properties,
StackView.Immediate)
}
DeployFeesSubscriber {
id: deployFeeSubscriber
communityId: root.communityId
chainId: editView.token.chainId
tokenType: editView.token.type
isOwnerDeployment: editView.token.isPrivilegedToken
accountAddress: editView.token.accountAddress
enabled: editView.visible
Component.onCompleted: root.registerDeployFeesSubscriber(deployFeeSubscriber)
}
}
}
}
}
}
Component {
id: previewTokenViewComponent
SettingsPage {
id: tokenPreviewPage
property alias token: preview.token
title: token.name
subtitle: token.symbol
contentItem: CommunityTokenView {
id: preview
viewWidth: root.viewWidth
preview: true
accounts: root.accounts
feeText: feeSubscriber.feeText
feeErrorText: feeSubscriber.feeErrorText
isFeeLoading: !feeSubscriber.feesResponse
onMintClicked: signMintPopup.open()
function signMintTransaction() {
if(preview.isAssetView)
root.mintAsset(token)
else
root.mintCollectible(token)
root.resetNavigation()
}
DeployFeesSubscriber {
id: feeSubscriber
communityId: root.communityId
chainId: preview.token.chainId
tokenType: preview.token.type
isOwnerDeployment: preview.token.isPrivilegedToken
accountAddress: preview.token.accountAddress
enabled: preview.visible || signMintPopup.visible
Component.onCompleted: root.registerDeployFeesSubscriber(feeSubscriber)
}
SignTransactionsPopup {
id: signMintPopup
title: qsTr("Sign transaction - Mint %1 token").arg(
preview.token.name)
totalFeeText: preview.isFeeLoading ? "" : preview.feeText
accountName: preview.token.accountName
model: QtObject {
readonly property string title: preview.feeLabel
readonly property string feeText: signMintPopup.totalFeeText
readonly property bool error: preview.feeErrorText !== ""
}
onSignTransactionClicked: preview.signMintTransaction()
}
}
}
}
component TokenViewPage: SettingsPage {
id: tokenViewPage
property TokenObject token: TokenObject {}
readonly property bool deploymentFailed: view.deployState === Constants.ContractTransactionStatus.Failed
property var membersModel
property var tokenOwnersModel
property string airdropKey
// Owner and TMaster related props
readonly property bool isPrivilegedTokenItem: isOwnerTokenItem || isTMasterTokenItem
readonly property bool isOwnerTokenItem: token.privilegesLevel === Constants.TokenPrivilegesLevel.Owner
readonly property bool isTMasterTokenItem: token.privilegesLevel === Constants.TokenPrivilegesLevel.TMaster
title: view.name
subtitle: view.symbol
buttons: [
StatusButton {
text: qsTr("Delete")
type: StatusBaseButton.Type.Danger
visible: (!tokenViewPage.isPrivilegedTokenItem) && !root.isAdminOnly && tokenViewPage.deploymentFailed
onClicked: deleteTokenAlertPopup.open()
},
StatusButton {
function retryAssetOrCollectible() {
// https://bugreports.qt.io/browse/QTBUG-91917
var isAssetView = tokenViewPage.token.type === Constants.TokenType.ERC20
// Copy TokenObject
var tokenObject = tokenObjectComponent.createObject(null, view.token)
// Then move on to the new token view, but token pre-filled:
var properties = {
isAssetView,
referenceName: tokenObject.name,
referenceSymbol: tokenObject.symbol,
validationMode: StatusInput.ValidationMode.Always,
[isAssetView ? "asset" : "collectible"]: tokenObject
}
var tokenView = root.push(newTokenViewComponent, properties,
StackView.Immediate)
// Cleanup dynamically created TokenObject
tokenView.Component.destruction.connect(() => tokenObject.destroy())
}
text: qsTr("Retry mint")
visible: (tokenViewPage.isPrivilegedTokenItem && root.isOwner && tokenViewPage.deploymentFailed) ||
(!tokenViewPage.isPrivilegedTokenItem && !root.isAdminOnly && tokenViewPage.deploymentFailed)
onClicked: {
if(tokenViewPage.isPrivilegedTokenItem) {
d.retryPrivilegedToken(view.token.key, view.token.chainId, view.token.accountName, view.token.accountAddress)
} else {
retryAssetOrCollectible()
}
}
},
StatusButton {
text: qsTr("Refresh")
visible: localAppSettings.refreshTokenEnabled && (tokenViewPage.token.deployState === Constants.ContractTransactionStatus.InProgress)
onClicked: root.refreshToken(tokenViewPage.token.key)
tooltip.text: qsTr("Restart token's transaction listening if the token got stuck in minting state")
}
]
contentItem: CommunityTokenView {
id: view
property string airdropKey: tokenViewPage.airdropKey // TO REMOVE: Temporal property until airdrop backend is not ready to use token key instead of symbol
viewWidth: root.viewWidth
token: tokenViewPage.token
membersModel: tokenViewPage.membersModel
tokenOwnersModel: tokenViewPage.tokenOwnersModel
isOwnerTokenItem: tokenViewPage.isOwnerTokenItem
onStartTokenHoldersManagement: root.startTokenHoldersManagement(chainId, address)
onStopTokenHoldersManagement: root.stopTokenHoldersManagement()
onGeneralAirdropRequested: {
root.airdropToken(view.airdropKey,
"1" + "0".repeat(view.token.multiplierIndex),
view.token.type, []) // tokenKey instead when backend airdrop ready to use key instead of symbol
}
onAirdropRequested: {
root.airdropToken(view.airdropKey,
"1" + "0".repeat(view.token.multiplierIndex),
view.token.type, [address]) // tokenKey instead when backend airdrop ready to use key instead of symbol
}
onViewProfileRequested: {
Global.openProfilePopup(contactId)
}
onViewMessagesRequested: {
// TODO: https://github.com/status-im/status-desktop/issues/11860
console.warn("View Messages is not implemented yet")
}
onRemoteDestructRequested: {
if (token.isPrivilegedToken) {
tokenMasterActionPopup.openPopup(
TokenMasterActionPopup.ActionType.RemotelyDestruct,
name, address, "")
} else {
remotelyDestructPopup.open()
// TODO: set the address selected in the popup's list
}
}
onBanRequested: {
if (token.isPrivilegedToken)
tokenMasterActionPopup.openPopup(
TokenMasterActionPopup.ActionType.Ban, name,
address, contactId)
else
kickBanPopup.openPopup(KickBanPopup.Mode.Ban, name, contactId)
}
onKickRequested: {
if (token.isPrivilegedToken)
tokenMasterActionPopup.openPopup(
TokenMasterActionPopup.ActionType.Kick, name,
address, contactId)
else
kickBanPopup.openPopup(KickBanPopup.Mode.Kick, name, contactId)
}
TokenMasterActionPopup {
id: tokenMasterActionPopup
property string address: ""
property string contactId: ""
communityName: root.communityName
networkName: view.token.chainName
accountsModel: root.accounts
feeText: selfDestructFeesSubscriber.feeText
feeErrorText: selfDestructFeesSubscriber.feeErrorText
isFeeLoading: !selfDestructFeesSubscriber.feesResponse
function openPopup(type, userName, address, contactId) {
tokenMasterActionPopup.actionType = type
tokenMasterActionPopup.userName = userName ||
SQUtils.Utils.elideAndFormatWalletAddress(address)
tokenMasterActionPopup.address = address
tokenMasterActionPopup.contactId = contactId
open()
}
onRemotelyDestructClicked: signPopup.open()
onKickClicked: signPopup.open()
onBanClicked: signPopup.open()
SelfDestructFeesSubscriber {
id: selfDestructFeesSubscriber
walletsAndAmounts: [{
walletAddress: tokenMasterActionPopup.address,
amount: 1
}]
accountAddress: tokenMasterActionPopup.selectedAccount
tokenKey: view.token.key
enabled: tokenMasterActionPopup.opened
Component.onCompleted: root.registerSelfDestructFeesSubscriber(
selfDestructFeesSubscriber)
}
SignTransactionsPopup {
id: signPopup
title: qsTr("Sign transaction - Remotely-destruct TokenMaster token")
totalFeeText: tokenMasterActionPopup.feeText
errorText: tokenMasterActionPopup.feeErrorText
accountName: tokenMasterActionPopup.selectedAccountName
model: QtObject {
readonly property string title: tokenMasterActionPopup.feeLabel
readonly property string feeText: tokenMasterActionPopup.feeText
readonly property bool error: tokenMasterActionPopup.feeErrorText !== ""
}
onSignTransactionClicked: {
// https://bugreports.qt.io/browse/QTBUG-91917
var contactId = tokenMasterActionPopup.contactId
var tokenKey = tokenViewPage.token.key
var accountAddress = tokenMasterActionPopup.selectedAccount
switch (tokenMasterActionPopup.actionType) {
case TokenMasterActionPopup.ActionType.RemotelyDestruct:
var tokenToDestruct = {
walletAddress: tokenMasterActionPopup.address,
amount: 1
}
root.remotelyDestructCollectibles(
[tokenToDestruct],
tokenKey, accountAddress)
break
case TokenMasterActionPopup.ActionType.Kick:
root.remotelyDestructAndKick(contactId, tokenKey,
accountAddress)
break
case TokenMasterActionPopup.ActionType.Ban:
root.remotelyDestructAndBan(contactId, tokenKey,
accountAddress,
tokenMasterActionPopup.deleteMessages)
break
}
tokenMasterActionPopup.close()
}
}
}
KickBanPopup {
id: kickBanPopup
property string contactId
communityName: root.communityName
onAccepted: {
if (mode === KickBanPopup.Mode.Kick)
root.kickUserRequested(contactId)
else
root.banUserRequested(contactId)
}
function openPopup(mode, userName, contactId) {
kickBanPopup.mode = mode
kickBanPopup.username = userName
kickBanPopup.contactId = contactId
open()
}
}
}
footer: MintTokensFooterPanel {
id: footer
readonly property TokenObject token: view.token
readonly property bool isAssetView: view.isAssetView
readonly property bool deployStateCompleted: token.deployState === Constants.ContractTransactionStatus.Completed
function closePopups() {
remotelyDestructPopup.close()
alertPopup.close()
signTransactionPopup.close()
burnTokensPopup.close()
}
communityName: root.communityName
visible: {
if(tokenViewPage.isOwnerTokenItem || tokenViewPage.isTMasterTokenItem)
// Only footer if owner profile
return root.isOwner
// Always present
return true
}
airdropEnabled: deployStateCompleted &&
(token.infiniteSupply ||
token.remainingTokens > 0)
remotelyDestructEnabled: deployStateCompleted &&
!!view.tokenOwnersModel &&
view.tokenOwnersModel.count > 0
burnEnabled: deployStateCompleted
sendOwnershipEnabled: deployStateCompleted
sendOwnershipVisible: root.isOwner && tokenViewPage.isOwnerTokenItem
airdropVisible: !tokenViewPage.isOwnerTokenItem
remotelyDestructVisible: !tokenViewPage.isOwnerTokenItem && token.remotelyDestruct
burnVisible: !tokenViewPage.isOwnerTokenItem && !token.infiniteSupply
onAirdropClicked: root.airdropToken(
view.airdropKey,
"1" + "0".repeat(view.token.multiplierIndex),
view.token.type, [])
onRemotelyDestructClicked: remotelyDestructPopup.open()
onBurnClicked: burnTokensPopup.open()
onSendOwnershipClicked: Global.openTransferOwnershipPopup(root.communityId,
root.communityName,
root.communityLogo,
tokenViewPage.token)
// helper properties to pass data through popups
property var walletsAndAmounts
property string burnAmount
property string accountAddress
RemotelyDestructPopup {
id: remotelyDestructPopup
property alias feeSubscriber: remotelyDestructFeeSubscriber
collectibleName: view.token.name
model: view.tokenOwnersModel || null
accounts: root.accounts
chainName: view.token.chainName
feeText: remotelyDestructFeeSubscriber.feeText
feeErrorText: remotelyDestructFeeSubscriber.feeErrorText
isFeeLoading: !remotelyDestructFeeSubscriber.feesResponse
networkThatIsNotActive: d.networkThatIsNotActive
onEnableNetwork: {
root.enableNetwork(root.ownerTokenChainId)
d.networkThatIsNotActive = ""
}
onRemotelyDestructClicked: {
remotelyDestructPopup.close()
footer.accountAddress = accountAddress
footer.walletsAndAmounts = walletsAndAmounts
alertPopup.open()
}
SelfDestructFeesSubscriber {
id: remotelyDestructFeeSubscriber
walletsAndAmounts: remotelyDestructPopup.selectedWalletsAndAmounts
accountAddress: remotelyDestructPopup.selectedAccount
tokenKey: view.token.key
enabled: remotelyDestructPopup.tokenCount > 0 && accountAddress !== "" && (remotelyDestructPopup.opened || signTransactionPopup.opened)
Component.onCompleted: root.registerSelfDestructFeesSubscriber(remotelyDestructFeeSubscriber)
}
}
BurnTokensPopup {
id: burnTokensPopup
property alias feeSubscriber: burnTokensFeeSubscriber
communityName: root.communityName
tokenName: footer.token.name
remainingTokens: footer.token.remainingTokens
multiplierIndex: footer.token.multiplierIndex
tokenSource: footer.token.artworkSource
chainName: footer.token.chainName
networkThatIsNotActive: d.networkThatIsNotActive
onEnableNetwork: {
root.enableNetwork(root.ownerTokenChainId)
d.networkThatIsNotActive = ""
}
onAmountToBurnChanged: burnTokensFeeSubscriber.updateAmount()
feeText: burnTokensFeeSubscriber.feeText
feeErrorText: burnTokensFeeSubscriber.feeErrorText
isFeeLoading: burnTokensFeeSubscriber.feeText === "" && burnTokensFeeSubscriber.feeErrorText === ""
accounts: root.accounts
onBurnClicked: {
burnTokensPopup.close()
footer.burnAmount = burnAmount
footer.accountAddress = accountAddress
signTransactionPopup.isRemotelyDestructTransaction = false
signTransactionPopup.open()
}
BurnTokenFeesSubscriber {
id: burnTokensFeeSubscriber
readonly property var updateAmount: Backpressure.debounce(burnTokensFeeSubscriber, 500, () => {
burnTokensFeeSubscriber.amount = burnTokensPopup.amountToBurn
})
amount: ""
tokenKey: tokenViewPage.token.key
accountAddress: burnTokensPopup.selectedAccountAddress
enabled: burnTokensPopup.visible || signTransactionPopup.visible
Component.onCompleted: root.registerBurnTokenFeesSubscriber(burnTokensFeeSubscriber)
}
}
AlertPopup {
id: alertPopup
title: qsTr("Remotely destruct %n token(s)", "",
remotelyDestructPopup.tokenCount)
acceptBtnText: qsTr("Remotely destruct")
alertText: qsTr("Continuing will destroy tokens held by members and revoke any permissions they are given. To undo you will have to issue them new tokens.")
onAcceptClicked: {
signTransactionPopup.isRemotelyDestructTransaction = true
signTransactionPopup.open()
}
}
SignTransactionsPopup {
id: signTransactionPopup
property bool isRemotelyDestructTransaction
readonly property string tokenName: footer.token.name
title: isRemotelyDestructTransaction
? qsTr("Sign transaction - Remotely destruct %1 token").arg(tokenName)
: qsTr("Sign transaction - Burn %1 tokens").arg(tokenName)
accountName: footer.token.accountName
totalFeeText: isRemotelyDestructTransaction
? remotelyDestructPopup.feeText
: burnTokensPopup.feeText
errorText: isRemotelyDestructTransaction
? remotelyDestructPopup.feeErrorText
: burnTokensPopup.feeErrorText
model: QtObject {
readonly property string title:
signTransactionPopup.isRemotelyDestructTransaction
? qsTr("Remotely destruct %Ln %1 token(s) on %2", "",
remotelyDestructPopup.tokenCount)
.arg(remotelyDestructPopup.collectibleName)
.arg(remotelyDestructPopup.chainName)
: burnTokensPopup.feeLabel
readonly property string feeText: signTransactionPopup.totalFeeText
readonly property bool error: signTransactionPopup.errorText !== ""
}
onSignTransactionClicked: {
if(signTransactionPopup.isRemotelyDestructTransaction)
root.remotelyDestructCollectibles(footer.walletsAndAmounts,
tokenKey, footer.accountAddress)
else
root.burnToken(tokenKey, footer.burnAmount, footer.accountAddress)
footer.closePopups()
}
}
}
AlertPopup {
id: deleteTokenAlertPopup
readonly property string tokenName: view.token.name
width: 521
title: qsTr("Delete %1").arg(tokenName)
acceptBtnText: qsTr("Delete %1 token").arg(tokenName)
alertText: qsTr("%1 is not yet minted, are you sure you want to delete it? All data associated with this token including its icon and description will be permanently deleted.").arg(tokenName)
onAcceptClicked: {
root.deleteToken(tokenViewPage.token.key)
root.navigateBack()
}
}
}
Component {
id: tokenViewComponent
Item {
id: tokenViewPageWrapper
property string tokenKey
Repeater {
model: SortFilterProxyModel {
sourceModel: root.tokensModel
filters: ValueFilter {
roleName: "contractUniqueKey"
value: tokenViewPageWrapper.tokenKey
}
}
delegate: TokenViewPage {
required property var model
implicitWidth: 0
anchors.fill: parent
tokenOwnersModel: model.tokenOwnersModel
membersModel: root.membersModel
airdropKey: model.symbol // TO BE REMOVED: When airdrop backend is ready to use token key instead of symbol
token.privilegesLevel: model.privilegesLevel
token.color: root.communityColor
token.accountName: model.accountName
token.artworkSource: model.image
token.chainIcon: model.chainIcon
token.chainId: model.chainId
token.chainName: model.chainName
token.decimals: model.decimals
token.deployState: model.deployState
token.description: model.description
token.infiniteSupply: model.infiniteSupply
token.key: model.contractUniqueKey
token.name: model.name
token.remainingTokens: model.remainingSupply
token.remotelyDestruct: model.remoteSelfDestruct
token.supply: model.supply
token.symbol: model.symbol
token.transferable: model.transferable
token.type: model.tokenType
token.burnState: model.burnState
token.remotelyDestructState: model.remotelyDestructState
token.accountAddress: model.accountAddress
token.multiplierIndex: model.multiplierIndex
token.tokenAddress: model.tokenAddress
token.tokenHoldersLoading: model.tokenHoldersLoading
}
onCountChanged: {
if (count === 0)
root.navigateBack()
}
}
}
}
}