diff --git a/storybook/pages/NetworkFilterPage.qml b/storybook/pages/NetworkFilterPage.qml index 1f4a94dabf..8ca8ce71d3 100644 --- a/storybook/pages/NetworkFilterPage.qml +++ b/storybook/pages/NetworkFilterPage.qml @@ -11,6 +11,10 @@ SplitView { id: root Logs { id: logs } + readonly property string ethereumName : "Ethereum Mainnet" + readonly property string optimismName : "Optimism" + readonly property string arbitrumName : "Arbitrum" + SplitView { orientation: Qt.Vertical @@ -41,7 +45,18 @@ SplitView { multiSelection: multiSelectionCheckBox.checked - onToggleNetwork: logs.logEvent("onToggleNetwork: " + network.chainName) + onToggleNetwork: { + + logs.logEvent("onToggleNetwork: " + network.chainName) + if(network.chainName === root.ethereumName) + ethRadioBtn.checked = true + + else if(network.chainName === root.optimismName) + optRadioBtn.checked = true + + else if(network.chainName === root.arbitrumName) + arbRadioBtn.checked = true + } } } } @@ -80,37 +95,21 @@ SplitView { RadioButton { id: ethRadioBtn - text: "Ethereum Mainnet" + text: root.ethereumName onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.ethNet) } RadioButton { - text: "Optimism" + id: optRadioBtn + + text: root.optimismName onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.optimismNet) } RadioButton { - text: "Arbitrum" + id: arbRadioBtn + + text: root.arbitrumName onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.arbitrumNet) } - RadioButton { - text: "Hermez" - onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.hermezNet) - } - RadioButton { - text: "Testnet" - onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.testnetNet) - } - RadioButton { - text: "Custom" - onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.customNet) - } - RadioButton { - text: "Undefined" - onCheckedChanged: if(checked) networkFilter.setChain() - } - RadioButton { - text: "Not existing network id" - onCheckedChanged: if(checked) networkFilter.setChain(77) - } } } } diff --git a/storybook/src/Models/MintedTokensModel.qml b/storybook/src/Models/MintedTokensModel.qml index 2528d36e42..30790097ca 100644 --- a/storybook/src/Models/MintedTokensModel.qml +++ b/storybook/src/Models/MintedTokensModel.qml @@ -23,9 +23,9 @@ ListModel { infiniteSupply: false, transferable: true, remoteSelfDestruct: false, - chainId: 2, - chainName: "Optimism", - chainIcon: ModelsData.networks.optimism, + chainId: 3, + chainName: "Arbitrum", + chainIcon: ModelsData.networks.arbitrum, accountName: "Another account - generated", tokenOwnersModel: root.emptyModel }, @@ -43,9 +43,9 @@ ListModel { infiniteSupply: true, transferable: false, remoteSelfDestruct: true, - chainId: 2, - chainName: "Optimism", - chainIcon: ModelsData.networks.optimism, + chainId: 3, + chainName: "Arbitrum", + chainIcon: ModelsData.networks.arbitrum, accountName: "Another account - generated", tokenOwnersModel: root.tokenOwnersModel }, diff --git a/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml b/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml index 5d9b82a0d5..2a42be64e1 100644 --- a/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml @@ -12,6 +12,7 @@ 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 @@ -271,15 +272,37 @@ StackView { SettingsPage { id: newTokenPage + readonly property int ownerTokenChainId: SQUtils.ModelUtils.get(root.tokensModel, "privilegesLevel", Constants.TokenPrivilegesLevel.Owner).chainId ?? 0 + readonly property var chainModel: NetworkModelHelpers.getLayerNetworkModelByChainId(root.layer1Networks, + root.layer2Networks, + ownerTokenChainId) ?? root.layer2Networks + readonly property int chainIndex: NetworkModelHelpers.getChainIndexByChainId(root.layer1Networks, + root.layer2Networks, + ownerTokenChainId) + readonly property string chainName: NetworkModelHelpers.getChainName(chainModel, chainIndex) + readonly property string chainIcon: NetworkModelHelpers.getChainIconUrl(chainModel, 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: newTokenPage.ownerTokenChainId + chainName: newTokenPage.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: newTokenPage.ownerTokenChainId + chainName: newTokenPage.chainName + chainIcon: newTokenPage.chainIcon } + + property bool isAssetView: false property int validationMode: StatusInput.ValidationMode.OnlyWhenDirty property string referenceName: "" @@ -343,8 +366,6 @@ StackView { viewWidth: root.viewWidth layer1Networks: root.layer1Networks layer2Networks: root.layer2Networks - enabledNetworks: root.enabledNetworks - allNetworks: root.allNetworks accounts: root.accounts tokensModel: root.tokensModel tokensModelWallet: root.tokensModelWallet diff --git a/ui/app/AppLayouts/Communities/views/EditCommunityTokenView.qml b/ui/app/AppLayouts/Communities/views/EditCommunityTokenView.qml index 5152e4fd77..3c5a7c220a 100644 --- a/ui/app/AppLayouts/Communities/views/EditCommunityTokenView.qml +++ b/ui/app/AppLayouts/Communities/views/EditCommunityTokenView.qml @@ -38,8 +38,6 @@ StatusScrollView { // Network related properties: property var layer1Networks property var layer2Networks - property var enabledNetworks - property var allNetworks // Account expected roles: address, name, color, emoji, walletType property var accounts @@ -77,10 +75,6 @@ StatusScrollView { contentWidth: mainLayout.width contentHeight: mainLayout.height - Component.onCompleted: { - networkSelector.setChain(root.token.chainId) - } - ColumnLayout { id: mainLayout @@ -122,8 +116,8 @@ StatusScrollView { validationMode: root.validationMode minLengthValidator.errorMessage: qsTr("Please name your token name (use A-Z and 0-9, hyphens and underscores only)") regexValidator.errorMessage: d.hasEmoji(text) ? - qsTr("Your token name is too cool (use A-Z and 0-9, hyphens and underscores only)") : - qsTr("Your token name contains invalid characters (use A-Z and 0-9, hyphens and underscores only)") + qsTr("Your token name is too cool (use A-Z and 0-9, hyphens and underscores only)") : + qsTr("Your token name contains invalid characters (use A-Z and 0-9, hyphens and underscores only)") extraValidator.validate: function (value) { // If minting failed, we can retry same deployment, so same name allowed const allowRepeatedName = root.token.deployState === Constants.ContractTransactionStatus.Failed @@ -145,7 +139,7 @@ StatusScrollView { label: qsTr("Description") text: root.token.description charLimit: 280 - placeholderText: root.isAssetView ? qsTr("Describe your asset") : qsTr("Describe your collectible") + placeholderText: root.isAssetView ? qsTr("Describe your asset (will be shown in hodler’s wallets)") : qsTr("Describe your collectible (will be shown in hodler’s wallets)") input.multiline: true input.verticalAlignment: Qt.AlignTop input.placeholder.verticalAlignment: Qt.AlignTop @@ -169,7 +163,7 @@ StatusScrollView { validationMode: root.validationMode minLengthValidator.errorMessage: qsTr("Please enter your token symbol (use A-Z only)") regexValidator.errorMessage: d.hasEmoji(text) ? qsTr("Your token symbol is too cool (use A-Z only)") : - qsTr("Your token symbol contains invalid characters (use A-Z only)") + qsTr("Your token symbol contains invalid characters (use A-Z only)") regexValidator.regularExpression: Constants.regularExpressions.capitalOnly extraValidator.validate: function (value) { // If minting failed, we can retry same deployment, so same symbol allowed @@ -180,10 +174,11 @@ StatusScrollView { // Otherwise, no repeated names allowed: return (!SQUtils.ModelUtils.contains(root.tokensModel, "symbol", symbolInput.text) && - !SQUtils.ModelUtils.contains(root.tokensModelWallet, "symbol", symbolInput.text)) + !SQUtils.ModelUtils.contains(root.tokensModelWallet, "symbol", symbolInput.text)) } extraValidator.errorMessage: SQUtils.ModelUtils.contains(root.tokensModelWallet, "symbol", symbolInput.text) ? - qsTr("This token symbol is already in use") : qsTr("You have used this token symbol before") + qsTr("This token symbol is already in use") : + qsTr("You have used this token symbol before") onTextChanged: { const cursorPos = input.edit.cursorPosition @@ -194,11 +189,53 @@ StatusScrollView { } } - CustomNetworkFilterRowComponent { - id: networkSelector + StatusBaseText { + text: qsTr("Network") + color: Theme.palette.directColor1 + font.pixelSize: Theme.primaryTextFontSize + } - label: qsTr("Select network") - description: qsTr("The network on which this token will be minted") + Rectangle { + Layout.preferredHeight: 44 + Layout.fillWidth: true + radius: 8 + color: "transparent" + border.color: Theme.palette.directColor7 + + RowLayout { + id: networkRow + + anchors.verticalCenter: parent.verticalCenter + spacing: 16 + + StatusSmartIdenticon { + Layout.alignment: Qt.AlignVCenter + Layout.leftMargin: Style.current.padding + + asset.height: 24 + asset.width: asset.height + asset.isImage: true + asset.name: Style.svg(token.chainIcon) + active: true + visible: active + } + + StatusBaseText { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + Layout.rightMargin: Style.current.padding + + font.pixelSize: 13 + font.weight: Font.Medium + elide: Text.ElideRight + lineHeight: 24 + lineHeightMode: Text.FixedHeight + verticalAlignment: Text.AlignVCenter + text: token.chainName + color: Theme.palette.baseColor1 + visible: !!text + } + } } CustomSwitchRowComponent { @@ -221,12 +258,12 @@ StatusScrollView { visible: !unlimitedSupplyChecker.checked label: qsTr("Total finite supply") text: SQUtils.AmountsArithmetic.toNumber(root.token.supply, - root.token.multiplierIndex) + root.token.multiplierIndex) placeholderText: qsTr("e.g. 300") minLengthValidator.errorMessage: qsTr("Please enter a total finite supply") regexValidator.errorMessage: d.hasEmoji(text) ? qsTr("Your total finite supply is too cool (use 0-9 only)") : - qsTr("Your total finite supply contains invalid characters (use 0-9 only)") + qsTr("Your total finite supply contains invalid characters (use 0-9 only)") regexValidator.regularExpression: Constants.regularExpressions.numerical extraValidator.validate: function (value) { return parseInt(value) > 0 && parseInt(value) <= 999999999 } extraValidator.errorMessage: qsTr("Enter a number between 1 and 999,999,999") @@ -274,7 +311,7 @@ StatusScrollView { validationMode: StatusInput.ValidationMode.Always minLengthValidator.errorMessage: qsTr("Please enter how many decimals your token should have") regexValidator.errorMessage: d.hasEmoji(text) ? qsTr("Your decimal amount is too cool (use 0-9 only)") : - qsTr("Your decimal amount contains invalid characters (use 0-9 only)") + qsTr("Your decimal amount contains invalid characters (use 0-9 only)") regexValidator.regularExpression: Constants.regularExpressions.numerical extraValidator.validate: function (value) { return parseInt(value) > 0 && parseInt(value) <= 10 } extraValidator.errorMessage: qsTr("Enter a number between 1 and 10") @@ -308,8 +345,8 @@ StatusScrollView { function onAccountAddressChanged() { const idx = SQUtils.ModelUtils.indexOf( - feesBox.accountsSelector.model, "address", - root.token.accountAddress) + feesBox.accountsSelector.model, "address", + root.token.accountAddress) feesBox.accountsSelector.currentIndex = idx } @@ -414,41 +451,4 @@ StatusScrollView { id: switch_ } } - - component CustomNetworkFilterRowComponent: RowLayout { - id: networkComponent - - property string label - property string description - - function setChain(chainId) { netFilter.setChain(chainId) } - - Layout.fillWidth: true - Layout.topMargin: Style.current.padding - spacing: 32 - - CustomLabelDescriptionComponent { - label: networkComponent.label - description: networkComponent.description - } - - NetworkFilter { - id: netFilter - - Layout.preferredWidth: 160 - - allNetworks: root.allNetworks - layer1Networks: root.layer1Networks - layer2Networks: root.layer2Networks - enabledNetworks: root.enabledNetworks - - multiSelection: false - - onToggleNetwork: (network) => { - root.token.chainId = network.chainId - root.token.chainName = network.chainName - root.token.chainIcon = network.iconUrl - } - } - } } diff --git a/ui/app/AppLayouts/Communities/views/EditOwnerTokenView.qml b/ui/app/AppLayouts/Communities/views/EditOwnerTokenView.qml index be20767f84..99076a95e8 100644 --- a/ui/app/AppLayouts/Communities/views/EditOwnerTokenView.qml +++ b/ui/app/AppLayouts/Communities/views/EditOwnerTokenView.qml @@ -141,9 +141,7 @@ StatusScrollView { 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.") } @@ -198,7 +196,7 @@ StatusScrollView { id: networkSelector label: qsTr("Select network") - description: qsTr("The network on which these tokens will be minted.") + description: qsTr("The network you select will be where all your community’s tokens reside. Once set, this setting can’t be changed and tokens can’t move to other networks.") } FeesBox { @@ -217,30 +215,6 @@ StatusScrollView { showAccountsSelector: false } - 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 @@ -308,8 +282,14 @@ StatusScrollView { layer1Networks: root.layer1Networks layer2Networks: root.layer2Networks enabledNetworks: root.enabledNetworks - multiSelection: false + control.topPadding: 10 + control.background: Rectangle { + height: 44 + radius: 8 + color: "transparent" + border.color: Theme.palette.directColor7 + } onToggleNetwork: (network) => { // Set Owner Token network properties: diff --git a/ui/app/AppLayouts/Communities/views/MintedTokensView.qml b/ui/app/AppLayouts/Communities/views/MintedTokensView.qml index bfa8aa92fc..e5fc18b834 100644 --- a/ui/app/AppLayouts/Communities/views/MintedTokensView.qml +++ b/ui/app/AppLayouts/Communities/views/MintedTokensView.qml @@ -169,8 +169,8 @@ StatusScrollView { delegate: StatusListItem { height: 64 width: mainLayout.width - title: model.name - subTitle: model.symbol + title: model.name ?? "" + subTitle: model.symbol ?? "" asset.name: model.image ? model.image : "" asset.isImage: true components: [ diff --git a/ui/app/AppLayouts/Wallet/controls/NetworkFilter.qml b/ui/app/AppLayouts/Wallet/controls/NetworkFilter.qml index 9a3eb5af43..c1934216d8 100644 --- a/ui/app/AppLayouts/Wallet/controls/NetworkFilter.qml +++ b/ui/app/AppLayouts/Wallet/controls/NetworkFilter.qml @@ -7,6 +7,8 @@ import StatusQ.Core.Utils 0.1 import StatusQ.Components 0.1 import StatusQ.Controls 0.1 +import AppLayouts.Wallet.helpers 1.0 + import utils 1.0 import "../views" @@ -29,27 +31,12 @@ StatusComboBox { function setChain(chainId) { if(!multiSelection && !!d.currentModel && d.currentModel.count > 0) { - // Find given chain id: - var chainIdExists = false - if(chainId) { - if(!!root.layer1Networks && ModelUtils.contains(root.layer1Networks, "chainId", chainId)) { - d.currentModel = root.layer1Networks - chainIdExists = true - } else if(!!root.layer2Networks && ModelUtils.contains(root.layer2Networks, "chainId", chainId)) { - d.currentModel = root.layer2Networks - chainIdExists = true - } - } - - // Set chain: - if(chainIdExists) { - d.currentIndex = ModelUtils.indexOf(d.currentModel, "chainId", chainId) - } - else { - // Default value if not specified - d.currentModel = root.layer2Networks - d.currentIndex = 0 - } + d.currentModel = NetworkModelHelpers.getLayerNetworkModelByChainId(root.layer1Networks, + root.layer2Networks, + chainId) ?? root.layer2Networks + d.currentIndex = NetworkModelHelpers.getChainIndexByChainId(root.layer1Networks, + root.layer2Networks, + chainId) // Notify change: root.toggleNetwork(ModelUtils.get(d.currentModel, d.currentIndex)) @@ -59,8 +46,8 @@ StatusComboBox { QtObject { id: d - readonly property string selectedChainName: ModelUtils.get(d.currentModel, d.currentIndex, "chainName") ?? "" - readonly property string selectedIconUrl: ModelUtils.get(d.currentModel, d.currentIndex, "iconUrl") ?? "" + readonly property string selectedChainName: NetworkModelHelpers.getChainName(d.currentModel, d.currentIndex) + readonly property string selectedIconUrl: NetworkModelHelpers.getChainIconUrl(d.currentModel, d.currentIndex) readonly property bool allSelected: (!!root.enabledNetworks && !!root.allNetworks) ? root.enabledNetworks.count === root.allNetworks.count : false readonly property bool noneSelected: (!!root.enabledNetworks) ? root.enabledNetworks.count === 0 : false diff --git a/ui/app/AppLayouts/Wallet/helpers/NetworkModelHelpers.qml b/ui/app/AppLayouts/Wallet/helpers/NetworkModelHelpers.qml new file mode 100644 index 0000000000..eb8510b2e0 --- /dev/null +++ b/ui/app/AppLayouts/Wallet/helpers/NetworkModelHelpers.qml @@ -0,0 +1,49 @@ +pragma Singleton + +import QtQml 2.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Utils 0.1 +import StatusQ.Internal 0.1 as Internal + +import AppLayouts.Communities.controls 1.0 + +QtObject { + + // Given a specific network model and an index inside the model, it gets the chain name. + function getChainName(model, index) { + return ModelUtils.get(model, index, "chainName") ?? "" + } + + // Given a specific network model and an index inside the model, it gets the chain icon url. + function getChainIconUrl(model, index) { + return ModelUtils.get(model, index, "iconUrl") ?? "" + } + + // Given a layer1 network model and layer2 network model, it looks for the provided chainId and returns + // the layer network model that contains the specific chain. If not found, returns undefined. + function getLayerNetworkModelByChainId(layer1NetworksModel, layer2NetworksModel, chainId) { + if(chainId) { + if(!!layer1NetworksModel && ModelUtils.contains(layer1NetworksModel, "chainId", chainId)) + return layer1NetworksModel + + else if(!!layer2NetworksModel && ModelUtils.contains(layer2NetworksModel, "chainId", chainId)) + return layer2NetworksModel + } + + // Default value if chainId is not part of any provided layer network model + return undefined + } + + // Given a layer1 network model and layer2 network model, it looks for the provided chainId and returns + // the index of the the specific chain. If not found, returns 0 value. + function getChainIndexByChainId(layer1NetworksModel, layer2NetworksModel, chainId) { + const currentModel = getLayerNetworkModelByChainId(layer1NetworksModel, layer2NetworksModel, chainId) + + if(!!currentModel) + return ModelUtils.indexOf(currentModel, "chainId", chainId) + + // Default value if no model specified + return 0 + } +} diff --git a/ui/app/AppLayouts/Wallet/helpers/qmldir b/ui/app/AppLayouts/Wallet/helpers/qmldir new file mode 100644 index 0000000000..f342082841 --- /dev/null +++ b/ui/app/AppLayouts/Wallet/helpers/qmldir @@ -0,0 +1 @@ +singleton NetworkModelHelpers 1.0 NetworkModelHelpers.qml