diff --git a/storybook/PagesModel.qml b/storybook/PagesModel.qml index 3cd4da9cc2..b2f5d30edb 100644 --- a/storybook/PagesModel.qml +++ b/storybook/PagesModel.qml @@ -85,6 +85,10 @@ ListModel { title: "EditNetworkView" section: "Views" } + ListElement { + title: "EditOwnerTokenView" + section: "Views" + } ListElement { title: "StatusCommunityCard" section: "Panels" diff --git a/storybook/figma.json b/storybook/figma.json index 175d4f724e..446b21d3de 100644 --- a/storybook/figma.json +++ b/storybook/figma.json @@ -239,5 +239,9 @@ ], "OverviewSettingsChart": [ "https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba⎜Desktop?type=design&node-id=31281-635619&mode=design&t=RYpVRgwqCjp8fUEX-0" + ], + "EditOwnerTokenView": [ + "https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?type=design&node-id=34794-590207&mode=design&t=ZnwK9yenS5oSgwws-0" + ] } diff --git a/storybook/pages/EditOwnerTokenViewPage.qml b/storybook/pages/EditOwnerTokenViewPage.qml new file mode 100644 index 0000000000..103d08e341 --- /dev/null +++ b/storybook/pages/EditOwnerTokenViewPage.qml @@ -0,0 +1,95 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core.Theme 0.1 +import StatusQ.Components 0.1 + +import AppLayouts.Communities.views 1.0 + +import Storybook 1.0 +import Models 1.0 + +import utils 1.0 + +SplitView { + + Logs { id: logs } + + SplitView { + orientation: Qt.Vertical + SplitView.fillWidth: true + + Item { + SplitView.fillWidth: true + SplitView.fillHeight: true + + EditOwnerTokenView { + anchors.fill: parent + anchors.margins: 50 + + communityName: communityName.text + communityLogo: doodles.checked ? ModelsData.collectibles.doodles : ModelsData.collectibles.mana + communityColor: color1.checked ? "#FFC4E9" : "#f44336" + + layer1Networks: NetworksModel.layer1Networks + layer2Networks: NetworksModel.layer2Networks + testNetworks: NetworksModel.testNetworks + enabledNetworks: NetworksModel.enabledNetworks + allNetworks: enabledNetworks + accounts: WalletAccountsModel {} + + onMintClicked: logs.logEvent("EditOwnerTokenView::onMintClicked") + } + } + + + LogsAndControlsPanel { + id: logsAndControlsPanel + + SplitView.minimumHeight: 100 + SplitView.preferredHeight: 150 + + logsView.logText: logs.logText + + ColumnLayout { + + RowLayout { + Label { + text: "Community name:" + } + + TextInput { + id: communityName + text: "Doodles" + } + } + + RowLayout { + RadioButton { + id: color1 + + text: "Light pink" + checked: true + } + + RadioButton { + text: "Orange" + } + } + + RowLayout { + RadioButton { + id: doodles + text: "Doodles" + checked: true + } + + RadioButton { + text: "Mana" + } + } + } + } + } +} diff --git a/ui/app/AppLayouts/Communities/helpers/PermissionsHelpers.qml b/ui/app/AppLayouts/Communities/helpers/PermissionsHelpers.qml index aaa6b2b318..e6b11d437b 100644 --- a/ui/app/AppLayouts/Communities/helpers/PermissionsHelpers.qml +++ b/ui/app/AppLayouts/Communities/helpers/PermissionsHelpers.qml @@ -82,4 +82,14 @@ QtObject { return "" } } + + // It generates a symbol from a given community name. + // It will be used to autogenerate the Owner and Token Master token symbols. + function autogenerateSymbol(isOwner, communityName) { + const shortName = communityName.substring(0, 3) + if(isOwner) + return "OWN" + shortName.toUpperCase() + else + return "TM" + shortName.toUpperCase() + } } diff --git a/ui/app/AppLayouts/Communities/helpers/TokenObject.qml b/ui/app/AppLayouts/Communities/helpers/TokenObject.qml index 3ab111b93e..1bd1b2d445 100644 --- a/ui/app/AppLayouts/Communities/helpers/TokenObject.qml +++ b/ui/app/AppLayouts/Communities/helpers/TokenObject.qml @@ -10,6 +10,11 @@ import utils 1.0 QtObject { property int type: Constants.TokenType.ERC20 + // Special token (Owner and TMaster tokens): + property bool isPrivilegedToken: false + property bool isOwner: false + property color color + // Unique identifier: property string key @@ -19,7 +24,7 @@ QtObject { property string description property bool infiniteSupply: true property int supply: 1 - property int remainingTokens + property int remainingTokens: supply // Artwork related properties: property url artworkSource diff --git a/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml b/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml index 64bb0341c4..ea768ec0f8 100644 --- a/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml @@ -50,6 +50,7 @@ StackView { signal mintCollectible(var collectibleItem) signal mintAsset(var assetItem) + signal mintOwnerToken(var ownerToken, var tMasterToken) signal signMintTransactionOpened(int chainId, string accountAddress, int tokenType) @@ -138,11 +139,66 @@ StackView { title: qsTr("Mint Owner token") contentItem: OwnerTokenWelcomeView { + viewWidth: root.viewWidth communityLogo: root.communityLogo communityColor: root.communityColor communityName: root.communityName - onNextClicked: root.push(newTokenViewComponent, StackView.Immediate) // TEMP: It will navigate to new token owner flow. Now, to current minting flow. + onNextClicked: root.push(ownerTokenEditViewComponent, StackView.Immediate) + } + } + } + + Component { + id: ownerTokenEditViewComponent + + SettingsPage { + id: ownerTokenPage + + 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 + layer1Networks: root.layer1Networks + layer2Networks: root.layer2Networks + testNetworks: root.testNetworks + enabledNetworks: root.testNetworks + allNetworks: root.allNetworks + accounts: root.accounts + + onMintClicked: signMintPopup.open() + + SignTokenTransactionsPopup { + id: signMintPopup + + anchors.centerIn: Overlay.overlay + title: qsTr("Sign transaction - Mint %1 tokens").arg(signMintPopup.tokenName) + tokenName: editOwnerTokenView.communityName + accountName: editOwnerTokenView.ownerToken.accountName + networkName: editOwnerTokenView.ownerToken.chainName + feeText: root.feeText + errorText: root.errorText + isFeeLoading: root.isFeeLoading + + onOpened: { + root.setFeeLoading() + root.signMintTransactionOpened(editOwnerTokenView.ownerToken.chainId, + editOwnerTokenView.ownerToken.accountAddress, + Constants.TokenType.ERC721) + } + onCancelClicked: close() + onSignTransactionClicked: editOwnerTokenView.signMintTransaction() + } } } } diff --git a/ui/app/AppLayouts/Communities/panels/PrivilegedTokenArtworkPanel.qml b/ui/app/AppLayouts/Communities/panels/PrivilegedTokenArtworkPanel.qml index edf30aad5a..102220b9bd 100644 --- a/ui/app/AppLayouts/Communities/panels/PrivilegedTokenArtworkPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/PrivilegedTokenArtworkPanel.qml @@ -11,7 +11,8 @@ import utils 1.0 Control { id: root - required property bool isOwner + // https://bugreports.qt.io/browse/QTBUG-84269 + /*required*/ property bool isOwner property bool showTag: false property int size: PrivilegedTokenArtworkPanel.Size.Small diff --git a/ui/app/AppLayouts/Communities/panels/TokenInfoPanel.qml b/ui/app/AppLayouts/Communities/panels/TokenInfoPanel.qml new file mode 100644 index 0000000000..a4501e2096 --- /dev/null +++ b/ui/app/AppLayouts/Communities/panels/TokenInfoPanel.qml @@ -0,0 +1,272 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtGraphicalEffects 1.0 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Components 0.1 +import StatusQ.Core.Utils 0.1 as StatusQUtils + +import AppLayouts.Communities.helpers 1.0 + +import utils 1.0 +import shared.panels 1.0 + +Control { + id: root + + // Panel properties: + property bool preview: false + + // Token object properties: + /* required */ property TokenObject token // https://bugreports.qt.io/browse/QTBUG-84269 + readonly property bool isAssetPanel: token.type === Constants.TokenType.ERC20 + + QtObject { + id: d + + readonly property int imageSelectorRectSize: root.isAssetPanel ? 104 : 280 + readonly property string infiniteSymbol: "∞" + readonly property int burnState: token.burnState + + function startAnimation(isBurn) { + totalbox.highlighted = true + + if(isBurn) + remainingBox.highlighted = true + } + + onBurnStateChanged: if(burnState === Constants.ContractTransactionStatus.Completed) d.startAnimation(true) + } + + implicitWidth: 560 // by design + + contentItem: ColumnLayout { + id: mainLayout + + spacing: Style.current.padding + + // General artwork representation: + Rectangle { + visible: !token.isPrivilegedToken + Layout.preferredHeight: d.imageSelectorRectSize + Layout.preferredWidth: Layout.preferredHeight + + radius: root.isAssetPanel ? Layout.preferredWidth / 2 : 8 + color:Theme.palette.baseColor2 + + Image { + id: image + + readonly property rect imageCropRect: token.artworkCropRect + + anchors.fill: parent + fillMode: Image.PreserveAspectFit + visible: false + source: token.artworkSource + sourceClipRect: imageCropRect ? imageCropRect : undefined + } + + OpacityMask { + anchors.fill: image + source: image + maskSource: parent + } + } + + // Special artwork representation for `Owner and Master Token` tokens: + PrivilegedTokenArtworkPanel { + visible: token.isPrivilegedToken + size: PrivilegedTokenArtworkPanel.Size.Large + artwork: token.artworkSource + color: token.color + isOwner: token.isOwner + } + + Flow { + spacing: Style.current.halfPadding + Layout.fillWidth: true + + component CustomPreviewBox: Rectangle { + id: previewBox + + property string label + property string value + property bool isLoading: false + property bool highlighted: false + + radius: 8 + border.color: Theme.palette.baseColor2 + implicitWidth: Math.min(boxContent.implicitWidth + Style.current.padding, mainLayout.width) + implicitHeight: boxContent.implicitHeight + Style.current.padding + states: [ + State { + when: !previewBox.highlighted + PropertyChanges { target: previewBox; color: "transparent" } + }, + State { + when: previewBox.highlighted + PropertyChanges { target: previewBox; color: Theme.palette.primaryColor3 } + } + ] + + onHighlightedChanged: if(highlighted) animation.start() + + ColumnLayout { + id: boxContent + anchors.centerIn: parent + spacing: 2 + + StatusBaseText { + Layout.fillWidth: true + text: previewBox.label + elide: Text.ElideRight + font.pixelSize: Style.current.additionalTextSize + color: Theme.palette.baseColor1 + } + + RowLayout { + spacing: 3 + + StatusBaseText { + text: StatusQUtils.Emoji.fromCodePoint("1f525") // :fire: emoji + font.pixelSize: Style.current.tertiaryTextFontSize + visible: previewBox.isLoading + color: Theme.palette.directColor1 + } + + StatusBaseText { + Layout.maximumWidth: mainLayout.width - Style.current.padding + text: previewBox.value + elide: Text.ElideRight + font.pixelSize: Theme.primaryTextFontSize + color: Theme.palette.directColor1 + } + + StatusLoadingIndicator { + Layout.preferredHeight: Theme.primaryTextFontSize + Layout.preferredWidth: Layout.preferredHeight + Layout.leftMargin: 6 + Layout.rightMargin: 3 + visible: previewBox.isLoading + color: Theme.palette.primaryColor1 + } + } + } + + Timer { + id: animation + + interval: 1500 + onRunningChanged: if(!running) previewBox.highlighted = false + } + } + + CustomPreviewBox { + id: symbolBox + + label: qsTr("Symbol") + value: token.symbol + } + + CustomPreviewBox { + id: totalbox + + label: qsTr("Total") + value: token.infiniteSupply ? d.infiniteSymbol : LocaleUtils.numberToLocaleString(token.supply) + isLoading: !token.infiniteSupply && + ((!root.isAssetPanel && token.remotelyDestructState === Constants.ContractTransactionStatus.InProgress) || + (d.burnState === Constants.ContractTransactionStatus.InProgress)) + } + + CustomPreviewBox { + id: remainingBox + + readonly property int remainingTokens: root.preview ? token.supply : token.remainingTokens + + label: qsTr("Remaining") + value: token.infiniteSupply ? d.infiniteSymbol : LocaleUtils.numberToLocaleString(remainingTokens) + isLoading: !token.infiniteSupply && (d.burnState === Constants.ContractTransactionStatus.InProgress) + } + + CustomPreviewBox { + visible: root.isAssetPanel + label: qsTr("DP") + value: token.decimals + } + + CustomPreviewBox { + visible: !root.isAssetPanel + label: qsTr("Transferable") + value: token.transferable ? qsTr("Yes") : qsTr("No") + } + + CustomPreviewBox { + visible: !root.isAssetPanel + + label: qsTr("Destructible") + value: token.remotelyDestruct ? qsTr("Yes") : qsTr("No") + } + + CustomPreviewBox { + visible: !token.isPrivilegedToken + + label: qsTr("Account") + value: token.accountName + } + + Rectangle { + visible: !token.isPrivilegedToken + height: symbolBox.height + width: rowChain.implicitWidth + 2 * Style.current.padding + border.width: 1 + radius: 8 + border.color: Theme.palette.baseColor2 + color: "transparent" + + RowLayout { + id: rowChain + + anchors.centerIn: parent + spacing: Style.current.padding + + SVGImage { + Layout.alignment: Qt.AlignVCenter + + height: 24 + width: height + source: token.chainIcon ? Style.svg(token.chainIcon) : undefined + } + + StatusBaseText { + Layout.alignment: Qt.AlignVCenter + + text: token.chainName + font.pixelSize: 13 + font.weight: Font.Medium + color: Theme.palette.baseColor1 + } + } + } + } + + StatusBaseText { + Layout.fillWidth: true + + text: token.description + wrapMode: TextEdit.WordWrap + font.pixelSize: Theme.primaryTextFontSize + lineHeight: 1.2 + } + } + + Connections { + target: token + + function onRemotelyDestructStateChanged() { + if(token.remotelyDestructState === Constants.ContractTransactionStatus.Completed) d.startAnimation(false) + } + } +} diff --git a/ui/app/AppLayouts/Communities/panels/qmldir b/ui/app/AppLayouts/Communities/panels/qmldir index fb9e10ea8c..1c82571efc 100644 --- a/ui/app/AppLayouts/Communities/panels/qmldir +++ b/ui/app/AppLayouts/Communities/panels/qmldir @@ -28,6 +28,7 @@ SortableTokenHoldersPanel 1.0 SortableTokenHoldersPanel.qml TagsPanel 1.0 TagsPanel.qml TokenHoldersPanel 1.0 TokenHoldersPanel.qml TokenHoldersProxyModel 1.0 TokenHoldersProxyModel.qml +TokenInfoPanel 1.0 TokenInfoPanel.qml WarningPanel 1.0 WarningPanel.qml WelcomeBannerPanel 1.0 WelcomeBannerPanel.qml EditSettingsPanel 1.0 EditSettingsPanel.qml diff --git a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml index 8f724cb483..c27fa7b34f 100644 --- a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml @@ -326,6 +326,10 @@ StatusSectionLayout { onMintAsset: communityTokensStore.deployAsset(root.community.id, assetItem) + onMintOwnerToken: + communityTokensStore.deployOwnerToken( + root.community.id, ownerToken, tMasterToken) + onSignRemoteDestructTransactionOpened: communityTokensStore.computeSelfDestructFee( remotelyDestructTokensList, tokenKey) diff --git a/ui/app/AppLayouts/Communities/views/CommunityTokenView.qml b/ui/app/AppLayouts/Communities/views/CommunityTokenView.qml index 9410642e9a..bda526a94f 100644 --- a/ui/app/AppLayouts/Communities/views/CommunityTokenView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunityTokenView.qml @@ -60,19 +60,7 @@ StatusScrollView { QtObject { id: d - readonly property int imageSelectorRectSize: root.isAssetView ? 104 : 280 readonly property int iconSize: 20 - readonly property string infiniteSymbol: "∞" - readonly property int burnState: root.token.burnState - - function startAnimation(isBurn) { - totalbox.highlighted = true - - if(isBurn) - remainingBox.highlighted = true - } - - onBurnStateChanged: if(burnState === Constants.ContractTransactionStatus.Completed) d.startAnimation(true) } padding: 0 @@ -109,202 +97,10 @@ StatusScrollView { } } - Rectangle { - Layout.preferredHeight: d.imageSelectorRectSize - Layout.preferredWidth: Layout.preferredHeight - - radius: root.isAssetView ? Layout.preferredWidth / 2 : 8 - color:Theme.palette.baseColor2 - clip: true - - Image { - id: image - - readonly property rect imageCropRect: root.artworkCropRect - - anchors.fill: parent - fillMode: Image.PreserveAspectFit - visible: false - source: root.artworkSource - sourceClipRect: imageCropRect ? imageCropRect : undefined - } - - OpacityMask { - anchors.fill: image - source: image - maskSource: parent - } - } - - Flow { - spacing: Style.current.halfPadding + TokenInfoPanel { Layout.fillWidth: true - component CustomPreviewBox: Rectangle { - id: previewBox - - property string label - property string value - property bool isLoading: false - property bool highlighted: false - - radius: 8 - border.color: Theme.palette.baseColor2 - implicitWidth: Math.min(boxContent.implicitWidth + Style.current.padding, mainLayout.width) - implicitHeight: boxContent.implicitHeight + Style.current.padding - states: [ - State { - when: !previewBox.highlighted - PropertyChanges { target: previewBox; color: "transparent" } - }, - State { - when: previewBox.highlighted - PropertyChanges { target: previewBox; color: Theme.palette.primaryColor3 } - } - ] - - onHighlightedChanged: if(highlighted) animation.start() - - ColumnLayout { - id: boxContent - anchors.centerIn: parent - spacing: 2 - - StatusBaseText { - Layout.fillWidth: true - text: previewBox.label - elide: Text.ElideRight - font.pixelSize: 13 - color: Theme.palette.baseColor1 - } - - RowLayout { - spacing: 3 - - StatusBaseText { - text: StatusQUtils.Emoji.fromCodePoint("1f525") // :fire: emoji - font.pixelSize: Theme.tertiaryTextFontSize - visible: previewBox.isLoading - color: Theme.palette.directColor1 - } - - StatusBaseText { - Layout.maximumWidth: mainLayout.width - Style.current.padding - text: previewBox.value - elide: Text.ElideRight - font.pixelSize: Theme.primaryTextFontSize - color: Theme.palette.directColor1 - } - - StatusLoadingIndicator { - Layout.preferredHeight: Theme.primaryTextFontSize - Layout.preferredWidth: Layout.preferredHeight - Layout.leftMargin: 6 - Layout.rightMargin: 3 - visible: previewBox.isLoading - color: Theme.palette.primaryColor1 - } - } - } - - Timer { - id: animation - - interval: 1500 - onRunningChanged: if(!running) previewBox.highlighted = false - } - } - - CustomPreviewBox { - id: symbolBox - - label: qsTr("Symbol") - value: root.symbol - } - - CustomPreviewBox { - id: totalbox - - label: qsTr("Total") - value: root.infiniteSupply ? d.infiniteSymbol : LocaleUtils.numberToLocaleString(root.supply) - isLoading: !root.infiniteSupply && - ((!root.isAssetView && root.remotelyDestructState === Constants.ContractTransactionStatus.InProgress) || - (d.burnState === Constants.ContractTransactionStatus.InProgress)) - } - - CustomPreviewBox { - id: remainingBox - - label: qsTr("Remaining") - value: root.infiniteSupply ? d.infiniteSymbol : LocaleUtils.numberToLocaleString(root.remainingTokens) - isLoading: !root.infiniteSupply && (d.burnState === Constants.ContractTransactionStatus.InProgress) - } - - CustomPreviewBox { - visible: root.isAssetView - label: qsTr("DP") - value: root.decimals - } - - CustomPreviewBox { - visible: !root.isAssetView - label: qsTr("Transferable") - value: root.transferable ? qsTr("Yes") : qsTr("No") - } - - CustomPreviewBox { - visible: !root.isAssetView - - label: qsTr("Destructible") - value: root.remotelyDestruct ? qsTr("Yes") : qsTr("No") - } - - CustomPreviewBox { - label: qsTr("Account") - value: root.accountName - } - - Rectangle { - height: symbolBox.height - width: rowChain.implicitWidth + 2 * Style.current.padding - border.width: 1 - radius: 8 - border.color: Theme.palette.baseColor2 - color: "transparent" - - RowLayout { - id: rowChain - - anchors.centerIn: parent - spacing: Style.current.padding - - SVGImage { - Layout.alignment: Qt.AlignVCenter - - height: 24 - width: height - source: Style.svg(root.chainIcon) - } - - StatusBaseText { - Layout.alignment: Qt.AlignVCenter - - text: root.chainName - font.pixelSize: 13 - font.weight: Font.Medium - color: Theme.palette.baseColor1 - } - } - } - } - - StatusBaseText { - Layout.fillWidth: true - - text: root.description - wrapMode: TextEdit.WordWrap - font.pixelSize: Theme.primaryTextFontSize - lineHeight: 1.2 + token: root.token } RowLayout { @@ -324,7 +120,7 @@ StatusScrollView { wrapMode: Text.Wrap font.pixelSize: Style.current.primaryTextFontSize color: Theme.palette.baseColor1 - text: qsTr("Make sure you’re happy with your token before minting it as it can’t be edited later") + text: qsTr("Review token details before minting it as they can’t be edited later") } } @@ -354,12 +150,4 @@ StatusScrollView { onRemoteDestructRequested: root.remoteDestructRequested(address) } } - - Connections { - target: root.token - - function onRemotelyDestructStateChanged() { - if(root.remotelyDestructState === Constants.ContractTransactionStatus.Completed) d.startAnimation(false) - } - } } diff --git a/ui/app/AppLayouts/Communities/views/EditOwnerTokenView.qml b/ui/app/AppLayouts/Communities/views/EditOwnerTokenView.qml new file mode 100644 index 0000000000..393090b7e6 --- /dev/null +++ b/ui/app/AppLayouts/Communities/views/EditOwnerTokenView.qml @@ -0,0 +1,300 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Components 0.1 +import StatusQ.Core.Utils 0.1 as SQUtils +import StatusQ.Popups 0.1 + +import utils 1.0 + +import AppLayouts.Communities.panels 1.0 +import AppLayouts.Communities.helpers 1.0 + +import AppLayouts.Wallet.controls 1.0 + +import SortFilterProxyModel 0.2 + +StatusScrollView { + id: root + + property int viewWidth: 560 // by design + + // Community info: + property string communityName + property url communityLogo + property color communityColor + + // Network related properties: + property var layer1Networks + property var layer2Networks + property var testNetworks + property var enabledNetworks + property var allNetworks + + // Wallet account expected roles: address, name, color, emoji, walletType + property var accounts + + // Privileged tokens: + readonly property TokenObject ownerToken: TokenObject { + type: Constants.TokenType.ERC721 + isPrivilegedToken: true + isOwner: true + artworkSource: root.communityLogo + color: root.communityColor + symbol: PermissionsHelpers.autogenerateSymbol(isOwner, root.communityName) + transferable: true + remotelyDestruct: false + supply: 1 + infiniteSupply: false + description: qsTr("This is the %1 Owner token. The hodler of this collectible has ultimate control over %1 Community token administration.").arg(root.communityName) + } + readonly property TokenObject tMasterToken: TokenObject { + type: Constants.TokenType.ERC721 + isPrivilegedToken: true + artworkSource: root.communityLogo + color: root.communityColor + symbol: PermissionsHelpers.autogenerateSymbol(isOwner, root.communityName) + 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) + } + + signal mintClicked + + QtObject { + id: d + + readonly property int titleSize: 17 + readonly property int iconSize: 20 + } + + padding: 0 + contentWidth: mainLayout.width + contentHeight: mainLayout.height + + Component.onCompleted: networkSelector.setChain(ownerToken.chainId) + + ColumnLayout { + id: mainLayout + + width: root.viewWidth + spacing: Style.current.padding + + // Owner token defintion: + StatusBaseText { + Layout.maximumWidth: root.viewWidth + + elide: Text.ElideMiddle + font.pixelSize: d.titleSize + font.bold: true + + text: qsTr("Owner-%1").arg(root.communityName) + } + + TokenInfoPanel { + Layout.fillWidth: true + + token: root.ownerToken + } + + StatusModalDivider { + Layout.fillWidth: true + + topPadding: Style.current.padding + bottomPadding: Style.current.padding + } + + // TMaster token definition: + StatusBaseText { + Layout.maximumWidth: root.viewWidth + + elide: Text.ElideMiddle + font.pixelSize: d.titleSize + font.bold: true + + text: qsTr("TMaster-%1").arg(root.communityName) + } + + TokenInfoPanel { + Layout.fillWidth: true + + token: root.tMasterToken + } + + StatusModalDivider { + Layout.fillWidth: true + + topPadding: Style.current.padding + bottomPadding: Style.current.padding + } + + // TO BE REMOVED: It will be removed with the new fees panel + CustomLabelDescriptionComponent { + + label: qsTr("Select account") + description: qsTr("This account will be where you receive your Owner token and will also be the account that pays the token minting gas fees.") + } + + // TO BE REMOVED: It will be removed with the new fees panel + StatusEmojiAndColorComboBox { + id: accountBox + + readonly property string address: SQUtils.ModelUtils.get(root.accounts, currentIndex, "address") + readonly property string initAccountName: ownerToken.accountName + readonly property int initIndex: SQUtils.ModelUtils.indexOf(root.accounts, "name", initAccountName) + + Layout.fillWidth: true + Layout.topMargin: -Style.current.halfPadding + + currentIndex: (initIndex !== -1) ? initIndex : 0 + model: SortFilterProxyModel { + sourceModel: root.accounts + proxyRoles: [ + ExpressionRole { + name: "color" + + function getColor(colorId) { + return Utils.getColorForId(colorId) + } + + // Direct call for singleton function is not handled properly by + // SortFilterProxyModel that's why helper function is used instead. + expression: { return getColor(model.colorId) } + } + ] + filters: ValueFilter { + roleName: "walletType" + value: Constants.watchWalletType + inverted: true + } + } + type: StatusComboBox.Type.Secondary + size: StatusComboBox.Size.Small + implicitHeight: 44 + defaultAssetName: "filled-account" + + onAddressChanged: { + ownerToken.accountAddress = address + tMasterToken.accountAddress = address + } + control.onDisplayTextChanged: { + ownerToken.accountName = control.displayText + tMasterToken.accountName = control.displayText + } + } + + CustomNetworkFilterRowComponent { + id: networkSelector + + label: qsTr("Select network") + description: qsTr("The network on which these tokens will be minted.") + } + + RowLayout { + Layout.fillWidth: true + Layout.topMargin: Style.current.halfPadding + + StatusIcon { + Layout.preferredWidth: d.iconSize + Layout.preferredHeight: d.iconSize + Layout.alignment: Qt.AlignTop + + color: Theme.palette.baseColor1 + icon: "info" + } + + StatusBaseText { + Layout.fillWidth: true + + wrapMode: Text.Wrap + font.pixelSize: Style.current.primaryTextFontSize + color: Theme.palette.baseColor1 + lineHeight: 1.2 + text: qsTr("Make sure you’re happy with the blockchain network selected before minting these tokens as they can’t be moved to a different network later.") + } + } + + StatusButton { + Layout.preferredHeight: 44 + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: Style.current.padding + Layout.bottomMargin: Style.current.padding + text: qsTr("Mint") + + onClicked: root.mintClicked() + } + } + + component CustomLabelDescriptionComponent: ColumnLayout { + id: labelDescComponent + + property string label + property string description + + Layout.fillWidth: true + + StatusBaseText { + text: labelDescComponent.label + color: Theme.palette.directColor1 + font.pixelSize: Theme.primaryTextFontSize + } + + StatusBaseText { + Layout.fillWidth: true + Layout.fillHeight: true + text: labelDescComponent.description + color: Theme.palette.baseColor1 + font.pixelSize: Theme.primaryTextFontSize + lineHeight: 1.2 + wrapMode: Text.WordWrap + } + } + + component CustomNetworkFilterRowComponent: ColumnLayout { + id: networkComponent + + property string label + property string description + + function setChain(chainId) { netFilter.setChain(chainId) } + + Layout.fillWidth: true + Layout.topMargin: Style.current.padding + spacing: 8 + + CustomLabelDescriptionComponent { + label: networkComponent.label + description: networkComponent.description + } + + NetworkFilter { + id: netFilter + + Layout.fillWidth: true + + allNetworks: root.allNetworks + layer1Networks: root.layer1Networks + layer2Networks: root.layer2Networks + testNetworks: root.testNetworks + enabledNetworks: root.enabledNetworks + + multiSelection: false + + onToggleNetwork: (network) => + { + // Set Owner Token network properties: + ownerToken.chainId = network.chainId + ownerToken.chainName = network.chainName + ownerToken.chainIcon = network.iconUrl + + // Set TMaster Token network properties: + tMasterToken.chainId = network.chainId + tMasterToken.chainName = network.chainName + tMasterToken.chainIcon = network.iconUrl + } + } + } +} diff --git a/ui/app/AppLayouts/Communities/views/OwnerTokenWelcomeView.qml b/ui/app/AppLayouts/Communities/views/OwnerTokenWelcomeView.qml index d7883fc215..b520835003 100644 --- a/ui/app/AppLayouts/Communities/views/OwnerTokenWelcomeView.qml +++ b/ui/app/AppLayouts/Communities/views/OwnerTokenWelcomeView.qml @@ -6,6 +6,7 @@ import StatusQ.Controls 0.1 import StatusQ.Core.Theme 0.1 import AppLayouts.Communities.panels 1.0 +import AppLayouts.Communities.helpers 1.0 import utils 1.0 @@ -24,19 +25,6 @@ StatusScrollView { contentWidth: mainLayout.width contentHeight: mainLayout.height - QtObject { - id: d - - function generateSymbol(isOwner) { - // TODO: Add a kind of verification for not repeating symbols - const shortName = root.communityName.substring(0, 3) - if(isOwner) - return "OWN" + shortName.toUpperCase() - else - return "TM" + shortName.toUpperCase() - } - } - ColumnLayout { id: mainLayout @@ -146,7 +134,7 @@ StatusScrollView { Layout.alignment: Qt.AlignBottom - text: d.generateSymbol(panel.isOwner) + text: PermissionsHelpers.autogenerateSymbol(panel.isOwner, root.communityName) font.pixelSize: Style.current.primaryTextFontSize color: Theme.palette.baseColor1 } diff --git a/ui/app/AppLayouts/Communities/views/qmldir b/ui/app/AppLayouts/Communities/views/qmldir index 8697923373..1148c73ce0 100644 --- a/ui/app/AppLayouts/Communities/views/qmldir +++ b/ui/app/AppLayouts/Communities/views/qmldir @@ -6,6 +6,7 @@ CommunityTokenView 1.0 CommunityTokenView.qml EditAirdropView 1.0 EditAirdropView.qml EditPermissionView 1.0 EditPermissionView.qml EditCommunityTokenView 1.0 EditCommunityTokenView.qml +EditOwnerTokenView 1.0 EditOwnerTokenView.qml HoldingsSelectionModel 1.0 HoldingsSelectionModel.qml JoinCommunityView 1.0 JoinCommunityView.qml MintedTokensView 1.0 MintedTokensView.qml diff --git a/ui/imports/shared/stores/CommunityTokensStore.qml b/ui/imports/shared/stores/CommunityTokensStore.qml index 0b00031bb2..d7f9d5b92b 100644 --- a/ui/imports/shared/stores/CommunityTokensStore.qml +++ b/ui/imports/shared/stores/CommunityTokensStore.qml @@ -45,6 +45,13 @@ QtObject { assetItem.infiniteSupply, assetItem.decimals, assetItem.chainId, jsonArtworkFile) } + function deployOwnerToken(communityId, ownerToken, tMasterToken) + { + // 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. + console.log("TODO: Backend Owner and Token Master token deployment!") + } + function deleteToken(communityId, contractUniqueKey) { console.log("TODO: Delete token bakend!") }