mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-09 13:56:10 +00:00
0c2674e7cb
After this change there is not need to pass sendModal instance from AppMain to other parts of app. Then SendModal should be launched simply by calling Global.openSendModal(....)
1017 lines
40 KiB
QML
1017 lines
40 KiB
QML
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()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|