From aaa99371246133d80ba2527a4f4bf05cb62249bb Mon Sep 17 00:00:00 2001 From: Noelia Date: Wed, 24 Jan 2024 12:49:46 +0100 Subject: [PATCH] feat(FirstTokenReceivedPopup): Created new popup `FirstTokenReceived` - Created new popup component. - Created related `storybook` page. - Integration of the popup into the app. Closes #12366 --- .../pages/FirstTokenReceivedPopupPage.qml | 258 ++++++++++++++++++ .../popups/FirstTokenReceivedPopup.qml | 146 ++++++++++ ui/app/AppLayouts/Communities/popups/qmldir | 1 + ui/app/mainui/Popups.qml | 27 +- ui/app/mainui/ToastsManager.qml | 28 +- .../shared/stores/CommunityTokensStore.qml | 9 +- ui/imports/utils/Global.qml | 8 + 7 files changed, 470 insertions(+), 7 deletions(-) create mode 100644 storybook/pages/FirstTokenReceivedPopupPage.qml create mode 100644 ui/app/AppLayouts/Communities/popups/FirstTokenReceivedPopup.qml diff --git a/storybook/pages/FirstTokenReceivedPopupPage.qml b/storybook/pages/FirstTokenReceivedPopupPage.qml new file mode 100644 index 0000000000..2914f4af8b --- /dev/null +++ b/storybook/pages/FirstTokenReceivedPopupPage.qml @@ -0,0 +1,258 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import Storybook 1.0 +import Models 1.0 + +import AppLayouts.Communities.popups 1.0 +import AppLayouts.Communities.helpers 1.0 + +import utils 1.0 + +SplitView { + Logs { id: logs } + + SplitView { + orientation: Qt.Vertical + SplitView.fillWidth: true + + Item { + SplitView.fillWidth: true + SplitView.fillHeight: true + + PopupBackground { + anchors.fill: parent + } + + Button { + anchors.centerIn: parent + text: "Reopen" + + onClicked: dialog.open() + } + + FirstTokenReceivedPopup { + id: dialog + + anchors.centerIn: parent + closePolicy: Popup.NoAutoClose + visible: true + modal: false + + communityId: "123" + communityName: communityNameText.text + communityLogo: ModelsData.collectibles.doodles + communitiesStore: QtObject { + function navigateToCommunity(id) { + logs.logEvent("FirstTokenReceivedPopup::onNavigateToCommunity: " + id) + } + } + + tokenSymbol: tokenSymbolText.text + tokenName: tokenNameText.text + tokenAmount: tokenAmountText.text + tokenType: Constants.TokenType.ERC20 + tokenImage: ModelsData.assets.eth + + onHideClicked: logs.logEvent("FirstTokenReceivedPopup::onHideClicked") + } + } + + LogsAndControlsPanel { + id: logsAndControlsPanel + + SplitView.minimumHeight: 100 + SplitView.preferredHeight: 150 + + logsView.logText: logs.logText + } + } + + Pane { + SplitView.minimumWidth: 300 + SplitView.preferredWidth: 300 + + Column { + spacing: 12 + + Label { + text: "Community Name" + font.bold: true + } + + TextInput { + id: communityNameText + + text: "Doodles" + + } + + Label { + text: "Community Logo" + font.bold: true + } + + Column { + + RadioButton { + id: doodleLogo + + text: "Doodle" + checked: true + + onCheckedChanged: dialog.communityLogo = ModelsData.collectibles.doodles + } + + RadioButton { + id: manaLogo + + text: "Mana" + + onCheckedChanged: dialog.communityLogo = ModelsData.collectibles.mana + } + + RadioButton { + id: superRareLogo + + text: "Status" + + onCheckedChanged: dialog.communityLogo = ModelsData.collectibles.custom + } + } + + Label { + text: "Token amount" + font.bold: true + } + + TextInput { + id: tokenAmountText + + text: "1" + } + + Label { + text: "Token name" + font.bold: true + } + + TextInput { + id: tokenNameText + + text: "DoodleCoin" + } + + Label { + text: "Token symbol" + font.bold: true + } + + TextInput { + id: tokenSymbolText + + text: "DOO" + } + + Label { + text: "Token Type" + font.bold: true + } + + Column { + + RadioButton { + id: assetType + + text: "Asset" + checked: true + + onCheckedChanged: dialog.tokenType = Constants.TokenType.ERC20 + } + + RadioButton { + id: collectibleType + + text: "Collectible" + + onCheckedChanged: dialog.tokenType = Constants.TokenType.ERC721 + } + } + + Label { + text: "Token Image" + font.bold: true + } + + Column { + + visible: assetType.checked + + onVisibleChanged: { + + if(visible) { + eth.checked = true + dialog.tokenImage = ModelsData.assets.eth + } + } + RadioButton { + id: eth + + text: "Eth" + checked: true + + onCheckedChanged: dialog.tokenImage = ModelsData.assets.eth + } + + RadioButton { + text: "SuperRare" + + onCheckedChanged: dialog.tokenImage = ModelsData.banners.superRare + } + + RadioButton { + text: "SNT" + + onCheckedChanged: dialog.tokenImage = ModelsData.assets.snt + } + } + + Column { + + visible: collectibleType.checked + + onVisibleChanged: { + if(visible) { + superrare.checked = true + dialog.tokenImage = ModelsData.banners.superRare + } + } + RadioButton { + id: superrare + + text: "SuperRare" + checked: true + + onCheckedChanged: dialog.tokenImage = ModelsData.banners.superRare + } + + RadioButton { + text: "Coinbase" + + onCheckedChanged: dialog.tokenImage = ModelsData.banners.coinbase + } + + RadioButton { + text: "Dragonereum" + + onCheckedChanged: dialog.tokenImage = ModelsData.banners.dragonereum + } + } + } + } +} + +// category: Popups + +// https://www.figma.com/file/FkFClTCYKf83RJWoifWgoX/Wallet-v2?type=design&node-id=18700%3A276658&mode=design&t=QzoyErtcBX8A54G7-1 +// https://www.figma.com/file/FkFClTCYKf83RJWoifWgoX/Wallet-v2?type=design&node-id=20765%3A244128&mode=design&t=X279c9Ix6QKMKWjM-1 diff --git a/ui/app/AppLayouts/Communities/popups/FirstTokenReceivedPopup.qml b/ui/app/AppLayouts/Communities/popups/FirstTokenReceivedPopup.qml new file mode 100644 index 0000000000..8c2715ab0e --- /dev/null +++ b/ui/app/AppLayouts/Communities/popups/FirstTokenReceivedPopup.qml @@ -0,0 +1,146 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQml.Models 2.15 +import QtQuick.Layouts 1.15 + +import StatusQ.Core 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Popups.Dialog 0.1 +import StatusQ.Components 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Core.Utils 0.1 + +import AppLayouts.Communities.panels 1.0 + +import utils 1.0 + +StatusDialog { + id: root + + // Community related props: + required property var communitiesStore + required property string communityId + required property string communityName + required property string communityLogo + + // Token related props: + required property string tokenName + required property string tokenSymbol + required property string tokenImage + required property string tokenAmount + required property int tokenType // ERC20 or ERC721 + readonly property bool isAssetType : tokenType === Constants.TokenType.ERC20 + + signal hideClicked + + QtObject { + id: d + + readonly property string contentText: root.isAssetType ? qsTr("Congratulations on receiving your first community asset:
%1 %2 (%3) minted by %4. Community assets are assets that have been minted by a community. As these assets cannot be verified, always double check their origin and validity before interacting with them. If in doubt, ask a trusted member or admin of the relevant community.").arg(root.tokenAmount).arg(root.tokenName).arg(root.tokenSymbol).arg(root.communityName) + : qsTr("Congratulations on receiving your first community collectible:
%1 %2 minted by %3. Community collectibles are collectibles that have been minted by a community. As these collectibles cannot be verified, always double check their origin and validity before interacting with them. If in doubt, ask a trusted member or admin of the relevant community.").arg(root.tokenAmount).arg(root.tokenName).arg(root.communityName) + + } + + width: 521 // by design + padding: 0 + + contentItem: StatusScrollView { + id: scrollView + + contentWidth: availableWidth + padding: Style.current.padding + + ColumnLayout { + spacing: Style.current.padding + width: scrollView.availableWidth + + StatusRoundedImage { + Layout.alignment: Qt.AlignHCenter + Layout.margins: Style.current.padding + Layout.preferredWidth: 68 + Layout.preferredHeight: Layout.preferredWidth + + radius: root.isAssetType ? width / 2 : 8 + image.source: root.tokenImage + showLoadingIndicator: false + image.fillMode: Image.PreserveAspectCrop + } + + StatusBaseText { + Layout.fillWidth: true + + text: d.contentText + textFormat: Text.RichText + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + lineHeight: 1.2 + } + + // Navigate to community button + StatusListItem { + Layout.fillWidth: true + Layout.bottomMargin: Style.current.halfPadding + + title: root.communityName + border.color: Theme.palette.baseColor2 + asset.name: root.communityLogo + asset.isImage: true + asset.isLetterIdenticon: !asset.name + components: [ + RowLayout { + StatusIcon { + Layout.alignment: Qt.AlignVCenter + + icon: "arrow-right" + color: Theme.palette.primaryColor1 + } + + StatusBaseText { + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: Style.current.padding + + text: qsTr("Visit Community") + font.pixelSize: Style.current.additionalTextSize + color: Theme.palette.primaryColor1 + } + } + ] + + onClicked: { + root.close() + root.communitiesStore.navigateToCommunity(root.communityId) + } + } + } + } + + header: StatusDialogHeader { + headline.title: root.isAssetType ? qsTr("You received your first community asset") : qsTr("You received your first community collectible") + actions.closeButton.onClicked: root.close() + } + + footer: StatusDialogFooter { + spacing: Style.current.padding + rightButtons: ObjectModel { + StatusFlatButton { + id: hideBtn + + visible: false // TODO: #13293 + + text: root.isAssetType ? qsTr("Hide this asset") : qsTr("Hide this collectible") + + onClicked: { + root.close() + root.hideClicked() + } + } + + StatusButton { + id: acceptBtn + + text: qsTr("Got it!") + + onClicked: root.close() + } + } + } +} diff --git a/ui/app/AppLayouts/Communities/popups/qmldir b/ui/app/AppLayouts/Communities/popups/qmldir index 11a660f258..53640c5169 100644 --- a/ui/app/AppLayouts/Communities/popups/qmldir +++ b/ui/app/AppLayouts/Communities/popups/qmldir @@ -9,6 +9,7 @@ EnableShardingPopup 1.0 EnableShardingPopup.qml ExportControlNodePopup 1.0 ExportControlNodePopup.qml FinaliseOwnershipDeclinePopup 1.0 FinaliseOwnershipDeclinePopup.qml FinaliseOwnershipPopup 1.0 FinaliseOwnershipPopup.qml +FirstTokenReceivedPopup 1.0 FirstTokenReceivedPopup.qml HoldingsDropdown 1.0 HoldingsDropdown.qml ImportControlNodePopup 1.0 ImportControlNodePopup.qml InDropdown 1.0 InDropdown.qml diff --git a/ui/app/mainui/Popups.qml b/ui/app/mainui/Popups.qml index 0c37ad8899..8169ceb428 100644 --- a/ui/app/mainui/Popups.qml +++ b/ui/app/mainui/Popups.qml @@ -78,6 +78,7 @@ QtObject { Global.openTransferOwnershipPopup.connect(openTransferOwnershipPopup) Global.openFinaliseOwnershipPopup.connect(openFinaliseOwnershipPopup) Global.openDeclineOwnershipPopup.connect(openDeclineOwnershipPopup) + Global.openFirstTokenReceivedPopup.connect(openFirstTokenReceivedPopup) } property var currentPopup @@ -318,6 +319,20 @@ QtObject { openPopup(declineOwnershipPopup, { communityName: communityName, communityId: communityId }) } + function openFirstTokenReceivedPopup(communityId, communityName, communityLogo, tokenSymbol, tokenName, tokenAmount, tokenType, tokenImage) { + openPopup(firstTokenReceivedPopup, + { + communityId: communityId, + communityName: communityName, + communityLogo: communityLogo, + tokenSymbol: tokenSymbol, + tokenName: tokenName, + tokenAmount: tokenAmount, + tokenType: tokenType, + tokenImage: tokenImage + }) + } + readonly property list _components: [ Component { id: removeContactConfirmationDialog @@ -933,7 +948,17 @@ QtObject { onDeclineClicked: root.ownershipDeclined() } - } + }, // End of components related to transfer community ownership flow. + + Component { + id: firstTokenReceivedPopup + + FirstTokenReceivedPopup { + communitiesStore: root.communitiesStore + + onHideClicked: console.warn("TODO: OPEN HIDE POPUP") + } + } ] } diff --git a/ui/app/mainui/ToastsManager.qml b/ui/app/mainui/ToastsManager.qml index dfa3773c6c..98ef668cba 100644 --- a/ui/app/mainui/ToastsManager.qml +++ b/ui/app/mainui/ToastsManager.qml @@ -129,7 +129,7 @@ QtObject { } // Community token received in the user wallet: - function onCommunityTokenReceived(name, image, communityId, communityName, balance, chainId, txHash, isFirst, tokenType, walletAccountName) { + function onCommunityTokenReceived(name, image, communityId, communityName, balance, chainId, txHash, isFirst, tokenType, walletAccountName, symbol) { // Some error control: if(tokenType !== Constants.TokenType.ERC20 && tokenType !== Constants.TokenType.ERC721) { @@ -139,9 +139,14 @@ QtObject { var data = { communityId: communityId, + communityName: communityName, chainId: chainId, txHash: txHash, - tokenType: tokenType + tokenType: tokenType, + tokenName: name, + tokenSymbol: symbol, + tokenImage: image, + tokenAmount: balance } if(isFirst) { @@ -216,7 +221,24 @@ QtObject { console.warn("Unexpected transaction hash while trying to navigate to the details page: " + txHash) return case ToastsManager.ActionType.OpenFirstCommunityTokenPopup: - console.warn("TODO: #12366") + if(actionData) { + var data = JSON.parse(actionData) + var communityId = data.communityId + var communityName = data.communityName + var tokenType = data.tokenType + var tokenName = data.tokenName + var tokenSymbol = data.tokenSymbol + var tokenImage = data.tokenImage + var tokenAmount = data.tokenAmount + Global.openFirstTokenReceivedPopup(communityId, + communityName, + rootChatStore.getCommunityDetailsAsJson(communityId).image, + tokenSymbol, + tokenName, + tokenAmount, + tokenType, + tokenImage); + } return default: console.warn("ToastsManager: Action type is not defined") diff --git a/ui/imports/shared/stores/CommunityTokensStore.qml b/ui/imports/shared/stores/CommunityTokensStore.qml index 4954067639..276225e322 100644 --- a/ui/imports/shared/stores/CommunityTokensStore.qml +++ b/ui/imports/shared/stores/CommunityTokensStore.qml @@ -38,7 +38,8 @@ QtObject { string communityId, string communityName, string balance, int chainId, string txHash, bool isFirst, - int tokenType, string walletAccountName) + int tokenType, string walletAccountName, + string symbol) // Minting tokens: function deployCollectible(communityId, collectibleItem) @@ -136,16 +137,18 @@ QtObject { root.ownerTokenReceived(communityId, communityName) } - function onCommunityTokenReceived(name, image, communityId, communityName, communityColor /*Unused, can be removed*/, balance, chainId, txHash/*, isFirst, tokenType, walletAccountName*/) { + function onCommunityTokenReceived(name, image, communityId, communityName, communityColor /*Unused, can be removed*/, balance, chainId, txHash/*, isFirst, tokenType, walletAccountName, symbol*/) { // TODO BACKEND: #13250 // ** `isFirst` property will be true if it's the first time the user receives a community asset and a community collectible // ** `tokenType` property will determine if the received minted token is an ERC20 or an ERC720 // ** `walletAccountName` property will provide the wallet account name where the token was received + // ** `symbol` property will provide the token symbol var isFirst = false var tokenType = Constants.TokenType.ERC20 var walletAccountName = "Status account" - root.communityTokenReceived(name, image, communityId, communityName, balance, chainId, txHash, isFirst, tokenType, walletAccountName) + var symbol = "NON" + root.communityTokenReceived(name, image, communityId, communityName, balance, chainId, txHash, isFirst, tokenType, walletAccountName, symbol) } function onSetSignerStateChanged(communityId, communityName, status, url) { diff --git a/ui/imports/utils/Global.qml b/ui/imports/utils/Global.qml index 5a368d4ecd..33bcb29b40 100644 --- a/ui/imports/utils/Global.qml +++ b/ui/imports/utils/Global.qml @@ -58,6 +58,14 @@ QtObject { var sendModalPopup) signal openFinaliseOwnershipPopup(string communityId) signal openDeclineOwnershipPopup(string communityId, string communityName) + signal openFirstTokenReceivedPopup(string communityId, + string communityName, + string communityLogo, + string tokenSymbol, + string tokenName, + string tokenAmount, + int tokenType, + string tokenImage) signal openLink(string link) signal openLinkWithConfirmation(string link, string domain)