feat(OwnerToken): Add all mint states views design details

- Added `storybook` support to change minted tokens model with Owner and TMaster tokens.
- Added new properties into the `tokenModel` model.
- Extended `CollectiblesView` to allow Owner and TMaster tokens representation.
- Updated `MintedTokensView` in order to display Owner and TMaster tokens.
- Added logic to `enable/disable` MINT and AIRDROP token depending on the owner /  tmaster tokens deploy state.
- Added temp buttons in MINT and AIRDROP pages that keeps enabled the flows although owner and tmaster backend is not ready.
- Extended navigation from outsite to minting section, depending on user profile and owner and tmaster states.
- Hide footer options in case of owner token item visualized.
- Added retry flow.

Closes #11299
This commit is contained in:
Noelia 2023-07-18 19:34:57 +02:00 committed by Noelia
parent 9800dad3ba
commit d2051c6247
15 changed files with 509 additions and 133 deletions

View File

@ -90,10 +90,17 @@ SplitView {
anchors.fill: parent anchors.fill: parent
anchors.topMargin: 50 anchors.topMargin: 50
// User profile
isOwner: ownerChecked.checked isOwner: ownerChecked.checked
isTokenMasterOwner: masterTokenOwnerChecked.checked isTokenMasterOwner: masterTokenOwnerChecked.checked
isAdmin: adminChecked.checked isAdmin: adminChecked.checked
tokensModel: editorModelChecked.checked ? emptyModel : mintedTokensModel
// Owner and TMaster related props:
isOwnerTokenDeployed: deployCheck.checked
isTMasterTokenDeployed: deployCheck.checked
// Models
assetsModel: AssetsModel {} assetsModel: AssetsModel {}
collectiblesModel: ListModel {} collectiblesModel: ListModel {}
@ -228,10 +235,10 @@ SplitView {
} }
CheckBox { CheckBox {
id: editorModelChecked id: deployCheck
checked: true checked: false
text: "No tokens minted yet" text: "Owner and TMaster tokens deployed"
} }
} }
} }

View File

@ -6,6 +6,8 @@ import AppLayouts.Communities.panels 1.0
import AppLayouts.Chat.stores 1.0 import AppLayouts.Chat.stores 1.0
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import SortFilterProxyModel 0.2
import Storybook 1.0 import Storybook 1.0
import Models 1.0 import Models 1.0
@ -48,15 +50,37 @@ SplitView {
id: mintedTokensModel id: mintedTokensModel
} }
SortFilterProxyModel {
id: privilegedTokensModel
sourceModel: mintedTokensModel
filters: ValueFilter {
roleName: "isPrivilegedToken"
value: true
}
}
anchors.fill: parent anchors.fill: parent
anchors.topMargin: 50 anchors.topMargin: 50
tokensModel: editorModelChecked.checked ? emptyModel : mintedTokensModel
isAdmin: adminChecked.checked // General:
isOwner: ownerChecked.checked
communityLogo: ModelsData.collectibles.doodles communityLogo: ModelsData.collectibles.doodles
communityColor: "#FFC4E9" communityColor: "#FFC4E9"
communityName: communityNameText.text communityName: "Doodles" // It cannot be changed since owner token and tMaster token in tokenModel used are related to the `Doodles` community
// Profile type:
isAdmin: adminChecked.checked
isOwner: ownerChecked.checked
isTokenMasterOwner: masterTokenOwnerChecked.checked isTokenMasterOwner: masterTokenOwnerChecked.checked
// Owner and TMaster related props:
isOwnerTokenDeployed: deployCheck.checked
isTMasterTokenDeployed: deployCheck.checked
// Models:
tokensModel: editorModelChecked.checked ? emptyModel :
privilegedModelChecked.checked ? privilegedTokensModel : mintedTokensModel
layer1Networks: NetworksModel.layer1Networks layer1Networks: NetworksModel.layer1Networks
layer2Networks: NetworksModel.layer2Networks layer2Networks: NetworksModel.layer2Networks
testNetworks: NetworksModel.testNetworks testNetworks: NetworksModel.testNetworks
@ -68,10 +92,10 @@ SplitView {
symbol: "MAI" symbol: "MAI"
} }
} }
onMintCollectible: logs.logEvent("CommunityMintTokensSettingsPanel::mintCollectible") onMintCollectible: logs.logEvent("CommunityMintTokensSettingsPanel::mintCollectible")
onMintAsset: logs.logEvent("CommunityMintTokensSettingsPanel::mintAssets") onMintAsset: logs.logEvent("CommunityMintTokensSettingsPanel::mintAssets")
onDeleteToken: logs.logEvent("CommunityMintTokensSettingsPanel::deleteToken: " + tokenKey) onDeleteToken: logs.logEvent("CommunityMintTokensSettingsPanel::deleteToken: " + tokenKey)
onSignMintTransactionOpened: feesTimer.restart() onSignMintTransactionOpened: feesTimer.restart()
} }
} }
@ -80,24 +104,11 @@ SplitView {
id: logsAndControlsPanel id: logsAndControlsPanel
SplitView.minimumHeight: 100 SplitView.minimumHeight: 100
SplitView.preferredHeight: 250 SplitView.preferredHeight: 300
logsView.logText: logs.logText logsView.logText: logs.logText
ColumnLayout { ColumnLayout {
Row {
Label {
text: "Community name: "
}
TextEdit {
id: communityNameText
text: "TEST COMMUNITY"
}
}
CheckBox { CheckBox {
id: ownerChecked id: ownerChecked
checked: true checked: true
@ -119,29 +130,47 @@ SplitView {
text: "Is admin? [Admis will be able to see token views, but NOT manage them, like creating new artwork or asset]" text: "Is admin? [Admis will be able to see token views, but NOT manage them, like creating new artwork or asset]"
} }
CheckBox { RowLayout {
RadioButton {
id: editorModelChecked id: editorModelChecked
checked: true checked: true
text: "No tokens minted yet" text: "No tokens minted yet"
} }
RadioButton {
id: privilegedModelChecked
text: "Owner token and TMaster token only"
}
RadioButton {
id: completeModelChecked
text: "Minted tokens list"
}
}
RowLayout { RowLayout {
Button {
RadioButton {
text: "Set all to 'In progress'" text: "Set all to 'In progress'"
onClicked: mintedTokensModel.changeAllMintingStates(1) onClicked: mintedTokensModel.changeAllMintingStates(1)
} }
Button {
text: "Set all to 'Deployed'"
onClicked: mintedTokensModel.changeAllMintingStates(2) RadioButton {
}
Button {
text: "Set all to 'Error'" text: "Set all to 'Error'"
onClicked: mintedTokensModel.changeAllMintingStates(0) onClicked: mintedTokensModel.changeAllMintingStates(0)
} }
RadioButton {
id: deployCheck
text: "Set all to 'Deployed'"
onClicked: mintedTokensModel.changeAllMintingStates(2)
}
} }
} }
} }

View File

@ -51,6 +51,9 @@ SplitView {
anchors.fill: parent anchors.fill: parent
anchors.margins: 50 anchors.margins: 50
model: filteredTokensModel model: filteredTokensModel
isOwner: true
isAdmin: false
onItemClicked: logs.logEvent("MintedTokensView::itemClicked", onItemClicked: logs.logEvent("MintedTokensView::itemClicked",
["tokenKey"], [tokenKey]) ["tokenKey"], [tokenKey])
} }

View File

@ -7,6 +7,48 @@ ListModel {
readonly property var data: [ readonly property var data: [
{ {
isPrivilegedToken: true,
isOwner: true,
contractUniqueKey: "0x15a23414a3",
tokenType: 2,
name: "Owner-Doodles",
image: ModelsData.collectibles.doodles,
deployState: 0,
symbol: "OWNDOO",
description: "Owner doodles desc",
supply: 1,
remainingSupply: 1,
infiniteSupply: false,
transferable: true,
remoteSelfDestruct: false,
chainId: 2,
chainName: "Optimism",
chainIcon: ModelsData.networks.optimism,
accountName: "Another account - generated"
},
{
isPrivilegedToken: true,
isOwner: false,
contractUniqueKey: "0x23124443",
tokenType: 2,
name: "TMaster-Doodles",
image: ModelsData.collectibles.doodles,
deployState: 0,
symbol: "TMDOO",
description: "Doodles Token master token description",
supply: 1,
remainingSupply: 1,
infiniteSupply: true,
transferable: false,
remoteSelfDestruct: true,
chainId: 2,
chainName: "Optimism",
chainIcon: ModelsData.networks.optimism,
accountName: "Another account - generated"
},
{
isPrivilegedToken: false,
isOwner: false,
contractUniqueKey: "0x1726362343", contractUniqueKey: "0x1726362343",
tokenType: 2, tokenType: 2,
name: "SuperRare artwork", name: "SuperRare artwork",
@ -25,6 +67,8 @@ ListModel {
accountName: "Status Account" accountName: "Status Account"
}, },
{ {
isPrivilegedToken: false,
isOwner: false,
contractUniqueKey: "0x847843", contractUniqueKey: "0x847843",
tokenType: 2, tokenType: 2,
name: "Kitty artwork", name: "Kitty artwork",
@ -43,6 +87,8 @@ ListModel {
accountName: "Status New Account" accountName: "Status New Account"
}, },
{ {
isPrivilegedToken: false,
isOwner: false,
contractUniqueKey: "0x1234525", contractUniqueKey: "0x1234525",
tokenType: 2, tokenType: 2,
name: "More artwork", name: "More artwork",
@ -61,6 +107,8 @@ ListModel {
accountName: "Other Account" accountName: "Other Account"
}, },
{ {
isPrivilegedToken: false,
isOwner: false,
contractUniqueKey: "0x38576852", contractUniqueKey: "0x38576852",
tokenType: 2, tokenType: 2,
name: "Crypto Punks artwork", name: "Crypto Punks artwork",
@ -80,6 +128,8 @@ ListModel {
tokenOwnersModel: root.tokenOwnersModel tokenOwnersModel: root.tokenOwnersModel
}, },
{ {
isPrivilegedToken: false,
isOwner: false,
contractUniqueKey: "0x38745623865", contractUniqueKey: "0x38745623865",
tokenType: 1, tokenType: 1,
name: "Unisocks", name: "Unisocks",
@ -97,6 +147,8 @@ ListModel {
accountName: "Status SNT Account" accountName: "Status SNT Account"
}, },
{ {
isPrivilegedToken: false,
isOwner: false,
contractUniqueKey: "0x872364871623", contractUniqueKey: "0x872364871623",
tokenType: 1, tokenType: 1,
name: "Dai", name: "Dai",

View File

@ -93,13 +93,19 @@ QtObject {
} }
} }
// OWNER AND TMASTER TOKENS related helpers:
readonly property string ownerTokenNameTag: "Owner-"
readonly property string tMasterTokenNameTag: "TMaster-"
readonly property string ownerTokenSymbolTag: "OWN"
readonly property string tMasterTokenSymbolTag: "TM"
// It generates a symbol from a given community name. // It generates a symbol from a given community name.
// It will be used to autogenerate the Owner and Token Master token symbols. // It will be used to autogenerate the Owner and Token Master token symbols.
function autogenerateSymbol(isOwner, communityName) { function communityNameToSymbol(isOwner, communityName) {
const shortName = communityName.substring(0, 3) const shortName = communityName.substring(0, 3)
if(isOwner) if(isOwner)
return "OWN" + shortName.toUpperCase() return ownerTokenSymbolTag + shortName.toUpperCase()
else else
return "TM" + shortName.toUpperCase() return tMasterTokenSymbolTag + shortName.toUpperCase()
} }
} }

View File

@ -13,14 +13,21 @@ StackView {
// id, name, image, color, owner properties expected // id, name, image, color, owner properties expected
required property var communityDetails required property var communityDetails
// User profiles
required property bool isOwner required property bool isOwner
required property bool isTokenMasterOwner required property bool isTokenMasterOwner
required property bool isAdmin required property bool isAdmin
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
// Token models: // Token models:
required property var assetsModel required property var assetsModel
required property var collectiblesModel required property var collectiblesModel
required property var tokensModel // Community minted tokens model
required property var membersModel required property var membersModel
@ -55,7 +62,7 @@ StackView {
QtObject { QtObject {
id: d id: d
readonly property bool isAdminOnly: root.isAdmin && !root.isOwner && !root.isTokenMasterOwner readonly property bool isAdminOnly: root.isAdmin && !root.isPrivilegedTokenOwnerProfile
signal selectToken(string key, int amount, int type) signal selectToken(string key, int amount, int type)
signal addAddresses(var addresses) signal addAddresses(var addresses)
@ -65,16 +72,34 @@ StackView {
implicitWidth: 0 implicitWidth: 0
title: qsTr("Airdrops") title: qsTr("Airdrops")
buttons: StatusButton { buttons: [
// TO BE REMOVED when Owner and TMaster backend is integrated. This is just to keep the airdrop flow available somehow
StatusButton {
text: qsTr("TEMP Airdrop")
onClicked: root.push(newAirdropView, StackView.Immediate)
StatusToolTip {
visible: parent.hovered
text: "TO BE REMOVED when Owner and TMaster backend is integrated. This is just to keep the airdrop flow available somehow"
orientation: StatusToolTip.Orientation.Bottom
y: parent.height + 12
maxWidth: 300
}
},
StatusButton {
objectName: "addNewItemButton" objectName: "addNewItemButton"
text: qsTr("New Airdrop") text: qsTr("New Airdrop")
enabled: !d.isAdminOnly && root.tokensModel.count > 0 // TODO: Replace to checker to ensure owner token is deployed enabled: !d.isAdminOnly && root.arePrivilegedTokensDeployed
onClicked: root.push(newAirdropView, StackView.Immediate) onClicked: root.push(newAirdropView, StackView.Immediate)
} }
]
contentItem: WelcomeSettingsView { contentItem: WelcomeSettingsView {
viewWidth: root.viewWidth viewWidth: root.viewWidth
image: Style.png("community/airdrops8_1") image: Style.png("community/airdrops8_1")
@ -85,7 +110,7 @@ StackView {
qsTr("Incentivise joining, retention, moderation and desired behaviour"), qsTr("Incentivise joining, retention, moderation and desired behaviour"),
qsTr("Require holding a token or NFT to obtain exclusive membership rights") qsTr("Require holding a token or NFT to obtain exclusive membership rights")
] ]
infoBoxVisible: d.isAdminOnly || ((root.isOwner || root.isTokenMasterOwner) && root.tokensModel.count === 0) // TODO: Replace to checker to ensure owner token is NOT deployed yet infoBoxVisible: d.isAdminOnly || (root.isPrivilegedTokenOwnerProfile && !root.arePrivilegedTokensDeployed)
infoBoxTitle: qsTr("Get started") infoBoxTitle: qsTr("Get started")
infoBoxText: d.isAdminOnly ? qsTr("Token airdropping can only be performed by admins that hodl the Communitys TokenMaster token. If you would like this permission, contact the Community founder (they will need to mint the Community Owner token before they can airdrop this to you)."): infoBoxText: d.isAdminOnly ? qsTr("Token airdropping can only be performed by admins that hodl the Communitys TokenMaster token. If you would like this permission, contact the Community founder (they will need to mint the Community Owner token before they can airdrop this to you)."):
qsTr("In order to Mint, Import and Airdrop community tokens, you first need to mint your Owner token which will give you permissions to access the token management features for your community.") qsTr("In order to Mint, Import and Airdrop community tokens, you first need to mint your Owner token which will give you permissions to access the token management features for your community.")

View File

@ -5,6 +5,7 @@ import QtQml 2.15
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import AppLayouts.Communities.controls 1.0 import AppLayouts.Communities.controls 1.0
import AppLayouts.Communities.helpers 1.0 import AppLayouts.Communities.helpers 1.0
@ -22,14 +23,25 @@ StackView {
id: root id: root
// General properties: // General properties:
required property bool isOwner property int viewWidth: 560 // by design
required property bool isTokenMasterOwner
required property bool isAdmin
required property string communityName required property string communityName
required property string communityLogo required property string communityLogo
required property color communityColor required property color communityColor
property int viewWidth: 560 // by design // 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
// It will monitorize if Owner and/or TMaster token items are included in the `tokensModel` despite the deployment state
property bool ownerOrTMasterTokenItemsExist: false
// Models: // Models:
property var tokensModel property var tokensModel
@ -77,51 +89,42 @@ StackView {
pop(initialItem, StackView.Immediate) pop(initialItem, StackView.Immediate)
} }
// This method will be called from the outsite from a different section like Airdrop or Permissions
function openNewTokenForm(isAssetView) { function openNewTokenForm(isAssetView) {
resetNavigation() resetNavigation()
if(d.isTokenOwnerDeployed) { if(root.isAdminOnly) {
// Admins can only see the initial tokens page. They cannot mint
root.push(mintedTokensViewComponent, StackView.Immediate)
return
}
if(root.arePrivilegedTokensInProgress || root.arePrivilegedTokensFailed) {
// If Owner and TMaster tokens deployment action has been started at least ones, but still without success
root.push(mintedTokensViewComponent, StackView.Immediate)
return
}
if(root.ownerOrTMasterTokenItemsExist) {
// Regular minting flow, selecting the specific tab
const properties = { isAssetView } const properties = { isAssetView }
root.push(newTokenViewComponent, properties, StackView.Immediate) root.push(newTokenViewComponent, properties, StackView.Immediate)
} else { return
root.push(ownerTokenViewComponent, StackView.Immediate)
} }
if(root.isOwner) {
// Owner and TMaster tokens to be deployed.
root.push(ownerTokenViewComponent, StackView.Immediate)
return
}
// Any other case, initial view
root.push(mintedTokensViewComponent, StackView.Immediate)
} }
property string previousPageName: depth > 1 ? qsTr("Back") : "" property string previousPageName: depth > 1 ? qsTr("Back") : ""
QtObject { initialItem: mintedTokensViewComponent
id: d
readonly property bool isTokenOwnerDeployed: root.tokensModel.count > 0 // TODO: Checker to ensure owner token is deployed
}
initialItem: SettingsPage {
implicitWidth: 0
title: qsTr("Tokens")
buttons: DisabledTooltipButton {
readonly property bool isAdminOnly: root.isAdmin && !root.isOwner && !root.isTokenMasterOwner
readonly property bool buttonEnabled: (root.isOwner || root.isTokenMasterOwner) && d.isTokenOwnerDeployed
buttonType: DisabledTooltipButton.Normal
aliasedObjectName: "addNewItemButton"
text: qsTr("Mint token")
enabled: isAdminOnly || buttonEnabled
interactive: buttonEnabled
onClicked: root.push(newTokenViewComponent, StackView.Immediate)
tooltipText: qsTr("In order to mint, you must hodl the TokenMaster token for %1").arg(root.communityName)
}
contentItem: MintedTokensView {
model: root.tokensModel
isOwner: root.isOwner
isAdmin: root.isAdmin
onItemClicked: root.push(tokenViewComponent, { tokenKey }, StackView.Immediate)
onMintOwnerTokenClicked: root.push(ownerTokenViewComponent, StackView.Immediate)
}
}
Component { Component {
id: tokenObjectComponent id: tokenObjectComponent
@ -130,6 +133,60 @@ StackView {
} }
// Mint tokens possible view contents: // Mint tokens possible view contents:
Component {
id: mintedTokensViewComponent
SettingsPage {
implicitWidth: 0
title: qsTr("Tokens")
buttons: [
// TO BE REMOVED when Owner and TMaster backend is integrated. This is just to keep the minting flow available somehow
StatusButton {
text: qsTr("TEMP Mint token")
onClicked: root.push(newTokenViewComponent, StackView.Immediate)
StatusToolTip {
visible: parent.hovered
text: "TO BE REMOVED when Owner and TMaster backend is integrated. This is just to keep the airdrop flow available somehow"
orientation: StatusToolTip.Orientation.Bottom
y: parent.height + 12
maxWidth: 300
}
},
DisabledTooltipButton {
readonly property bool buttonEnabled: root.isPrivilegedTokenOwnerProfile && root.arePrivilegedTokensDeployed
buttonType: DisabledTooltipButton.Normal
aliasedObjectName: "addNewItemButton"
text: qsTr("Mint token")
enabled: root.isAdminOnly || buttonEnabled
interactive: buttonEnabled
onClicked: root.push(newTokenViewComponent, StackView.Immediate)
tooltipText: 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
onItemClicked: root.push(tokenViewComponent, { tokenKey }, StackView.Immediate)
onMintOwnerTokenClicked: root.push(ownerTokenViewComponent, StackView.Immediate)
}
}
}
Component { Component {
id: ownerTokenViewComponent id: ownerTokenViewComponent
@ -155,6 +212,10 @@ StackView {
SettingsPage { SettingsPage {
id: ownerTokenPage id: ownerTokenPage
property int chainId
property string accountName
property string accountAddress
title: qsTr("Mint Owner token") title: qsTr("Mint Owner token")
contentItem: EditOwnerTokenView { contentItem: EditOwnerTokenView {
@ -166,9 +227,18 @@ StackView {
} }
viewWidth: root.viewWidth viewWidth: root.viewWidth
communityLogo: root.communityLogo communityLogo: root.communityLogo
communityColor: root.communityColor communityColor: root.communityColor
communityName: root.communityName 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
layer1Networks: root.layer1Networks layer1Networks: root.layer1Networks
layer2Networks: root.layer2Networks layer2Networks: root.layer2Networks
testNetworks: root.testNetworks testNetworks: root.testNetworks
@ -222,7 +292,7 @@ StackView {
property string referenceName: "" property string referenceName: ""
property string referenceSymbol: "" property string referenceSymbol: ""
title: optionsTab.currentItem == assetsTab title: optionsTab.currentItem === assetsTab
? qsTr("Mint asset") : qsTr("Mint collectible") ? qsTr("Mint asset") : qsTr("Mint collectible")
contentItem: ColumnLayout { contentItem: ColumnLayout {
@ -252,7 +322,7 @@ StackView {
Layout.preferredWidth: root.viewWidth Layout.preferredWidth: root.viewWidth
Layout.fillHeight: true Layout.fillHeight: true
currentIndex: optionsTab.currentItem == collectiblesTab ? 0 : 1 currentIndex: optionsTab.currentItem === collectiblesTab ? 0 : 1
CustomEditCommunityTokenView { CustomEditCommunityTokenView {
id: newCollectibleView id: newCollectibleView
@ -279,7 +349,7 @@ StackView {
layer1Networks: root.layer1Networks layer1Networks: root.layer1Networks
layer2Networks: root.layer2Networks layer2Networks: root.layer2Networks
testNetworks: root.testNetworks testNetworks: root.testNetworks
enabledNetworks: root.testNetworks enabledNetworks: root.enabledNetworks
allNetworks: root.allNetworks allNetworks: root.allNetworks
accounts: root.accounts accounts: root.accounts
tokensModel: root.tokensModel tokensModel: root.tokensModel
@ -363,10 +433,16 @@ StackView {
id: tokenViewPage id: tokenViewPage
readonly property alias token: view.token readonly property alias token: view.token
readonly property bool deploymentFailed: view.deployState === Constants.ContractTransactionStatus.Failed
property alias tokenOwnersModel: view.tokenOwnersModel property alias tokenOwnersModel: view.tokenOwnersModel
property alias airdropKey: view.airdropKey property alias airdropKey: view.airdropKey
// Owner and TMaster related props
readonly property bool isPrivilegedTokenItem: token.isPrivilegedToken
readonly property bool isOwnerTokenItem: token.isPrivilegedToken && token.isOwner
readonly property bool isTMasterTokenItem: token.isPrivilegedToken && !token.isOwner
title: view.name title: view.name
subtitle: view.symbol subtitle: view.symbol
@ -375,22 +451,17 @@ StackView {
text: qsTr("Delete") text: qsTr("Delete")
type: StatusBaseButton.Type.Danger type: StatusBaseButton.Type.Danger
visible: view.deployState === Constants.ContractTransactionStatus.Failed visible: (!tokenViewPage.isPrivilegedTokenItem) && !root.isAdminOnly && tokenViewPage.deploymentFailed
onClicked: deleteTokenAlertPopup.open() onClicked: deleteTokenAlertPopup.open()
}, },
StatusButton { StatusButton {
text: qsTr("Retry mint") function retryAssetOrCollectible() {
visible: view.deployState === Constants.ContractTransactionStatus.Failed
onClicked: {
// https://bugreports.qt.io/browse/QTBUG-91917 // https://bugreports.qt.io/browse/QTBUG-91917
var isAssetView = tokenViewPage.token.type === Constants.TokenType.ERC20 var isAssetView = tokenViewPage.token.type === Constants.TokenType.ERC20
// copy TokenObject // Copy TokenObject
var tokenObject = tokenObjectComponent.createObject( var tokenObject = tokenObjectComponent.createObject(null, view.token)
null, view.token)
// Then move on to the new token view, but token pre-filled: // Then move on to the new token view, but token pre-filled:
var properties = { var properties = {
@ -404,9 +475,33 @@ StackView {
var tokenView = root.push(newTokenViewComponent, properties, var tokenView = root.push(newTokenViewComponent, properties,
StackView.Immediate) StackView.Immediate)
// cleanup dynamically created TokenObject // Cleanup dynamically created TokenObject
tokenView.Component.destruction.connect(() => tokenObject.destroy()) tokenView.Component.destruction.connect(() => tokenObject.destroy())
} }
// Owner or TMaster token
function retryPrivilegedToken() {
var properties = {
chainId: view.token.chainId,
accountName: view.token.accountName,
accountAddress: view.token.accountAddress,
}
root.push(ownerTokenEditViewComponent, properties,
StackView.Immediate)
}
text: qsTr("Retry mint")
visible: (tokenViewPage.isPrivilegedTokenItem && root.isOwner && tokenViewPage.deploymentFailed) ||
(!tokenViewPage.isPrivilegedTokenItem && !root.isAdminOnly && tokenViewPage.deploymentFailed)
onClicked: {
if(tokenViewPage.isPrivilegedTokenItem)
retryPrivilegedToken()
else
retryAssetOrCollectible()
}
} }
] ]
@ -438,8 +533,7 @@ StackView {
readonly property TokenObject token: view.token readonly property TokenObject token: view.token
readonly property bool deployStateCompleted: readonly property bool deployStateCompleted: token.deployState === Constants.ContractTransactionStatus.Completed
token.deployState === Constants.ContractTransactionStatus.Completed
function closePopups() { function closePopups() {
remotelyDestructPopup.close() remotelyDestructPopup.close()
@ -448,6 +542,16 @@ StackView {
burnTokensPopup.close() burnTokensPopup.close()
} }
visible: {
if(tokenViewPage.isOwnerTokenItem)
// Always hidden
return false
if(tokenViewPage.isTMasterTokenItem)
// Only footer if owner profile
return root.isOwner
// Always present
return true
}
airdropEnabled: deployStateCompleted && airdropEnabled: deployStateCompleted &&
(token.infiniteSupply || (token.infiniteSupply ||
token.remainingTokens !== 0) token.remainingTokens !== 0)
@ -596,6 +700,9 @@ StackView {
tokenOwnersModel: model.tokenOwnersModel tokenOwnersModel: model.tokenOwnersModel
airdropKey: model.symbol // TO BE REMOVED: When airdrop backend is ready to use token key instead of symbol airdropKey: model.symbol // TO BE REMOVED: When airdrop backend is ready to use token key instead of symbol
token.isPrivilegedToken: model.isPrivilegedToken
token.isOwner: model.isOwner
token.color: root.communityColor
token.accountName: model.accountName token.accountName: model.accountName
token.artworkSource: model.image token.artworkSource: model.image
token.chainIcon: model.chainIcon token.chainIcon: model.chainIcon

View File

@ -23,14 +23,34 @@ Control {
QtObject { QtObject {
id: d id: d
readonly property int imageSize: size === PrivilegedTokenArtworkPanel.Size.Small ? 80 : 186 readonly property int imageSize: ({
readonly property int bgSize: size === PrivilegedTokenArtworkPanel.Size.Small ? 120 : 280 [PrivilegedTokenArtworkPanel.Size.Small]: 80,
readonly property int iconSize: size === PrivilegedTokenArtworkPanel.Size.Small ? 16 : 38 [PrivilegedTokenArtworkPanel.Size.Medium]: 109,
readonly property int iconMargins: size === PrivilegedTokenArtworkPanel.Size.Small ? 8 : 16 [PrivilegedTokenArtworkPanel.Size.Large]: 186
}[size])
readonly property int bgSize: ({
[PrivilegedTokenArtworkPanel.Size.Small]: 120,
[PrivilegedTokenArtworkPanel.Size.Medium]: 164,
[PrivilegedTokenArtworkPanel.Size.Large]: 280
}[size])
readonly property int iconSize: ({
[PrivilegedTokenArtworkPanel.Size.Small]: 14,
[PrivilegedTokenArtworkPanel.Size.Medium]: 16,
[PrivilegedTokenArtworkPanel.Size.Large]: 38
}[size])
readonly property int iconMargins: ({
[PrivilegedTokenArtworkPanel.Size.Small]: 8,
[PrivilegedTokenArtworkPanel.Size.Medium]: 12,
[PrivilegedTokenArtworkPanel.Size.Large]: 16
}[size])
} }
enum Size { enum Size {
Small, Small,
Medium,
Large Large
} }
@ -38,7 +58,7 @@ Control {
implicitHeight: implicitWidth implicitHeight: implicitWidth
background: Rectangle { background: Rectangle {
color: "transparent" color: Theme.palette.statusAppLayout.rightPanelBackgroundColor
radius: 8 radius: 8
border.color: Theme.palette.baseColor2 border.color: Theme.palette.baseColor2
} }

View File

@ -19,6 +19,8 @@ Control {
// Panel properties: // Panel properties:
property bool preview: false property bool preview: false
property bool accountBoxVisible: true
property bool networkBoxVisible: true
// Token object properties: // Token object properties:
/* required */ property TokenObject token // https://bugreports.qt.io/browse/QTBUG-84269 /* required */ property TokenObject token // https://bugreports.qt.io/browse/QTBUG-84269
@ -211,14 +213,14 @@ Control {
} }
CustomPreviewBox { CustomPreviewBox {
visible: !token.isPrivilegedToken visible: root.accountBoxVisible
label: qsTr("Account") label: qsTr("Account")
value: token.accountName value: token.accountName
} }
Rectangle { Rectangle {
visible: !token.isPrivilegedToken visible: root.networkBoxVisible
height: symbolBox.height height: symbolBox.height
width: rowChain.implicitWidth + 2 * Style.current.padding width: rowChain.implicitWidth + 2 * Style.current.padding
border.width: 1 border.width: 1

View File

@ -20,6 +20,7 @@ import utils 1.0
import AppLayouts.Communities.controls 1.0 import AppLayouts.Communities.controls 1.0
import AppLayouts.Communities.panels 1.0 import AppLayouts.Communities.panels 1.0
import AppLayouts.Communities.popups 1.0 import AppLayouts.Communities.popups 1.0
import AppLayouts.Communities.helpers 1.0
StatusSectionLayout { StatusSectionLayout {
id: root id: root
@ -315,12 +316,21 @@ StatusSectionLayout {
mintPanel.isFeeLoading = true mintPanel.isFeeLoading = true
} }
// General community props
communityName: root.community.name communityName: root.community.name
communityLogo: root.community.image communityLogo: root.community.image
communityColor: root.community.color communityColor: root.community.color
// User profile props
isOwner: root.isOwner isOwner: root.isOwner
isAdmin: root.isAdmin isAdmin: root.isAdmin
isTokenMasterOwner: false // TODO: Backend isTokenMasterOwner: false // TODO: Backend
// Owner and TMaster properties
isOwnerTokenDeployed: tokensModelChangesTracker.isOwnerTokenDeployed
isTMasterTokenDeployed: tokensModelChangesTracker.isTMasterTokenDeployed
// Models
tokensModel: root.community.communityTokens tokensModel: root.community.communityTokens
tokensModelWallet: root.rootStore.tokensModelWallet tokensModelWallet: root.rootStore.tokensModelWallet
layer1Networks: communityTokensStore.layer1Networks layer1Networks: communityTokensStore.layer1Networks
@ -382,10 +392,15 @@ StatusSectionLayout {
readonly property bool sectionEnabled: root.isOwner readonly property bool sectionEnabled: root.isOwner
communityDetails: d.communityDetails communityDetails: d.communityDetails
// Profile type
isOwner: root.isOwner isOwner: root.isOwner
isTokenMasterOwner: false // TODO: Backend isTokenMasterOwner: false // TODO: Backend
isAdmin: root.isAdmin isAdmin: root.isAdmin
tokensModel: root.community.communityTokens
// Owner and TMaster properties
isOwnerTokenDeployed: tokensModelChangesTracker.isOwnerTokenDeployed
isTMasterTokenDeployed: tokensModelChangesTracker.isTMasterTokenDeployed
readonly property CommunityTokensStore communityTokensStore: readonly property CommunityTokensStore communityTokensStore:
rootStore.communityTokensStore rootStore.communityTokensStore
@ -519,6 +534,64 @@ StatusSectionLayout {
} }
} }
StatusQUtils.ModelChangeTracker {
id: tokensModelChangesTracker
// Owner and TMaster token deployment states
property bool isOwnerTokenDeployed: false
property bool isTMasterTokenDeployed: false
// It will monitorize if Owner and/or TMaster token items are included in the `model` despite the deployment state
property bool ownerOrTMasterTokenItemsExist: false
function checkIfPrivilegedTokenItemsExist() {
return SQUtils.ModelUtils.contains(model, "name", PermissionsHelpers.ownerTokenNameTag + root.communityName) ||
SQUtils.ModelUtils.contains(model, "name", PermissionsHelpers.tMasterTokenNameTag + root.communityName)
}
function reviewTokenDeployState(tagType, isOwner) {
const index = SQUtils.ModelUtils.indexOf(model, "name", tagType + root.communityName)
if(index === -1)
return false
const token = SQUtils.ModelUtils.get(model, index)
// Some assertions:
if(!token.isPrivilegedToken)
return false
if(token.isOwner !== isOwner)
return false
// Deploy state check:
if(token.deployState !== Constants.ContractTransactionStatus.Completed)
return false
// Token deployed!!
return true
}
model: root.community.communityTokens
onRevisionChanged: {
// It will update property to know if Owner and TMaster token items have been added into the tokens list.
ownerOrTMasterTokenItemsExist = checkIfPrivilegedTokenItemsExist()
if(!ownerOrTMasterTokenItemsExist)
return
// It monitors the deployment:
if(!isOwnerTokenDeployed)
isOwnerTokenDeployed = reviewTokenDeployState(PermissionsHelpers.ownerTokenNameTag, true)
if(!isTMasterTokenDeployed)
isTMasterTokenDeployed = reviewTokenDeployState(PermissionsHelpers.tMasterTokenNameTag, false)
// Not necessary to track more changes since privileged tokens have been correctly deployed.
if(isOwnerTokenDeployed && isTMasterTokenDeployed)
tokensModelChangesTracker.enabled = false
}
}
MessageDialog { MessageDialog {
id: errorDialog id: errorDialog

View File

@ -44,7 +44,7 @@ StatusScrollView {
isOwner: true isOwner: true
artworkSource: root.communityLogo artworkSource: root.communityLogo
color: root.communityColor color: root.communityColor
symbol: PermissionsHelpers.autogenerateSymbol(isOwner, root.communityName) symbol: PermissionsHelpers.communityNameToSymbol(isOwner, root.communityName)
transferable: true transferable: true
remotelyDestruct: false remotelyDestruct: false
supply: 1 supply: 1
@ -56,7 +56,7 @@ StatusScrollView {
isPrivilegedToken: true isPrivilegedToken: true
artworkSource: root.communityLogo artworkSource: root.communityLogo
color: root.communityColor color: root.communityColor
symbol: PermissionsHelpers.autogenerateSymbol(isOwner, root.communityName) symbol: PermissionsHelpers.communityNameToSymbol(isOwner, root.communityName)
remotelyDestruct: true remotelyDestruct: true
description: qsTr("This is the %1 TokenMaster token. The hodler of this collectible has full admin rights for the %1 Community in Status and can mint and airdrop %1 Community tokens.").arg(root.communityName) description: qsTr("This is the %1 TokenMaster token. The hodler of this collectible has full admin rights for the %1 Community in Status and can mint and airdrop %1 Community tokens.").arg(root.communityName)
} }
@ -90,13 +90,15 @@ StatusScrollView {
font.pixelSize: d.titleSize font.pixelSize: d.titleSize
font.bold: true font.bold: true
text: qsTr("Owner-%1").arg(root.communityName) text: PermissionsHelpers.ownerTokenNameTag + root.communityName
} }
TokenInfoPanel { TokenInfoPanel {
Layout.fillWidth: true Layout.fillWidth: true
token: root.ownerToken token: root.ownerToken
accountBoxVisible: false
networkBoxVisible: false
} }
StatusModalDivider { StatusModalDivider {
@ -114,13 +116,15 @@ StatusScrollView {
font.pixelSize: d.titleSize font.pixelSize: d.titleSize
font.bold: true font.bold: true
text: qsTr("TMaster-%1").arg(root.communityName) text: PermissionsHelpers.tMasterTokenNameTag + root.communityName
} }
TokenInfoPanel { TokenInfoPanel {
Layout.fillWidth: true Layout.fillWidth: true
token: root.tMasterToken token: root.tMasterToken
accountBoxVisible: false
networkBoxVisible: false
} }
StatusModalDivider { StatusModalDivider {

View File

@ -11,16 +11,19 @@ import shared.controls 1.0
import AppLayouts.Wallet.views.collectibles 1.0 import AppLayouts.Wallet.views.collectibles 1.0
import AppLayouts.Communities.panels 1.0 import AppLayouts.Communities.panels 1.0
import AppLayouts.Communities.helpers 1.0
StatusScrollView { StatusScrollView {
id: root id: root
// User profile props
required property bool isOwner required property bool isOwner
required property bool isAdmin required property bool isAdmin
// General props
property int viewWidth: 560 // by design property int viewWidth: 560 // by design
property var model property var model
property string communityName
readonly property int count: assetsModel.count + collectiblesModel.count readonly property int count: assetsModel.count + collectiblesModel.count
signal itemClicked(string tokenKey, signal itemClicked(string tokenKey,
@ -38,20 +41,31 @@ StatusScrollView {
readonly property int delegateAssetsHeight: 64 readonly property int delegateAssetsHeight: 64
function getSubtitle(deployState, remainingSupply, supply, function getDeployStateInfo(deployState) {
isCollectible, isInfiniteSupply) {
if(deployState === Constants.ContractTransactionStatus.Failed) if(deployState === Constants.ContractTransactionStatus.Failed)
return qsTr("Minting failed") return qsTr("Minting failed")
if(deployState === Constants.ContractTransactionStatus.InProgress) if(deployState === Constants.ContractTransactionStatus.InProgress)
return qsTr("Minting...") return qsTr("Minting...")
if(isInfiniteSupply) return ""
return isCollectible ? qsTr("∞ remaining") : "" }
return isCollectible function getRemainingInfo(isOwnerToken, isPrivilegedToken,
? qsTr("%1 / %2 remaining").arg(remainingSupply).arg(supply) remainingSupply, supply, isInfiniteSupply) {
: "" // Owner token use case:
if(isOwnerToken)
return qsTr("1 of 1 (you hodl)")
// TMaster token use case:
if(isPrivilegedToken)
return "∞"
// Rest of collectible cases:
if(isInfiniteSupply)
return qsTr("∞ remaining")
return qsTr("%L1 / %L2 remaining").arg(remainingSupply).arg(supply)
} }
} }
@ -155,10 +169,7 @@ StatusScrollView {
components: [ components: [
StatusBaseText { StatusBaseText {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: d.getSubtitle(model.deployState, text: d.getDeployStateInfo(model.deployState)
model.remainingSupply,
model.supply, false,
model.infiniteSupply)
color: model.deployState === Constants.ContractTransactionStatus.Failed color: model.deployState === Constants.ContractTransactionStatus.Failed
? Theme.palette.dangerColor1 : Theme.palette.baseColor1 ? Theme.palette.dangerColor1 : Theme.palette.baseColor1
font.pixelSize: 13 font.pixelSize: 13
@ -214,16 +225,22 @@ StatusScrollView {
height: collectiblesGrid.cellHeight height: collectiblesGrid.cellHeight
width: collectiblesGrid.cellWidth width: collectiblesGrid.cellWidth
title: model.name ? model.name : "..." title: model.name ? model.name : "..."
subTitle: d.getSubtitle(model.deployState, subTitle: deployState === Constants.ContractTransactionStatus.Completed ?
d.getRemainingInfo(model.isOwner,
model.isPrivilegedToken,
model.remainingSupply, model.remainingSupply,
model.supply, true, model.supply,
model.infiniteSupply) model.infiniteSupply) :
d.getDeployStateInfo(model.deployState)
subTitleColor: model.deployState === Constants.ContractTransactionStatus.Failed subTitleColor: model.deployState === Constants.ContractTransactionStatus.Failed
? Theme.palette.dangerColor1 : Theme.palette.baseColor1 ? Theme.palette.dangerColor1 : Theme.palette.baseColor1
fallbackImageUrl: model.image ? model.image : "" fallbackImageUrl: model.image ? model.image : ""
backgroundColor: "transparent" backgroundColor: "transparent"
isLoading: false isLoading: false
navigationIconVisible: true navigationIconVisible: false
isPrivilegedToken: model.isPrivilegedToken
isOwner: model.isOwner
ornamentColor: model.color
onClicked: root.itemClicked(model.contractUniqueKey, onClicked: root.itemClicked(model.contractUniqueKey,
model.chainId, model.chainName, model.chainId, model.chainName,

View File

@ -134,7 +134,7 @@ StatusScrollView {
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
text: PermissionsHelpers.autogenerateSymbol(panel.isOwner, root.communityName) text: PermissionsHelpers.communityNameToSymbol(panel.isOwner, root.communityName)
font.pixelSize: Style.current.primaryTextFontSize font.pixelSize: Style.current.primaryTextFontSize
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1
} }

View File

@ -7,6 +7,8 @@ import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import AppLayouts.Communities.panels 1.0
import utils 1.0 import utils 1.0
Control { Control {
@ -22,6 +24,11 @@ Control {
property bool isLoading: false property bool isLoading: false
property bool navigationIconVisible: false property bool navigationIconVisible: false
// Special Owner and TMaster token properties
property bool isPrivilegedToken: false // Owner or TMaster tokens
property bool isOwner: false // Owner token
property color ornamentColor // Relevant color for these special tokens (community color)
signal clicked signal clicked
implicitHeight: 225 implicitHeight: 225
@ -43,6 +50,8 @@ Control {
Layout.margins: Style.current.halfPadding Layout.margins: Style.current.halfPadding
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: width Layout.preferredHeight: width
visible: !root.isPrivilegedToken
radius: 8 radius: 8
mediaUrl: root.mediaUrl mediaUrl: root.mediaUrl
mediaType: root.mediaType mediaType: root.mediaType
@ -59,6 +68,25 @@ Control {
} }
} }
PrivilegedTokenArtworkPanel {
Layout.alignment: Qt.AlignHCenter
Layout.margins: Style.current.halfPadding
Layout.fillWidth: true
Layout.preferredHeight: width
visible: root.isPrivilegedToken
size: PrivilegedTokenArtworkPanel.Size.Medium
artwork: root.fallbackImageUrl
color: root.ornamentColor
isOwner: root.isOwner
Loader {
anchors.fill: parent
active: root.isLoading
sourceComponent: LoadingComponent {radius: image.radius}
}
}
RowLayout { RowLayout {
Layout.leftMargin: Style.current.halfPadding Layout.leftMargin: Style.current.halfPadding
Layout.rightMargin: Layout.leftMargin Layout.rightMargin: Layout.leftMargin
@ -90,6 +118,7 @@ Control {
id: subTitleItem id: subTitleItem
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
Layout.topMargin: 3
Layout.leftMargin: Style.current.halfPadding Layout.leftMargin: Style.current.halfPadding
Layout.rightMargin: Layout.leftMargin Layout.rightMargin: Layout.leftMargin
Layout.fillWidth: !root.isLoading Layout.fillWidth: !root.isLoading

View File

@ -49,6 +49,8 @@ QtObject {
{ {
// NOTE for backend team: `ownerToken` and `tMasterToken` can be used to do an assertion before the deployment process starts, since // NOTE for backend team: `ownerToken` and `tMasterToken` can be used to do an assertion before the deployment process starts, since
// the objects have been created to display the token details to the user and must be the same than backend builds. // the objects have been created to display the token details to the user and must be the same than backend builds.
// TODO: Backend will need to check if the ownerToken or tMasterToken have a valid tokenKey, so it means a deployment retry,
// otherwise, it is a new deployment.
console.log("TODO: Backend Owner and Token Master token deployment!") console.log("TODO: Backend Owner and Token Master token deployment!")
} }