From 717c243e02f9cfe75d07449a965447d0b428c6b0 Mon Sep 17 00:00:00 2001 From: Noelia Date: Thu, 14 Sep 2023 12:02:51 +0200 Subject: [PATCH] feat(TransferOwnershipPopup): Updated transfer ownership popup according to new design - Applied new design to transfer ownership popup. - Added support in storybook. - Added navigation to transfer ownership popup from community settings / owner token footer. - Added navigation to transfer ownership popup from community settings / overview. - Linked wallet send modal to the transactions popup with the needed pre-populated data. Closes #12171 --- .../pages/TransferOwnershipPopupPage.qml | 114 +++++++++++++ ui/app/AppLayouts/Chat/ChatLayout.qml | 2 + .../panels/MintTokensFooterPanel.qml | 16 ++ .../panels/MintTokensSettingsPanel.qml | 19 ++- .../panels/OverviewSettingsPanel.qml | 24 +++ .../popups/TransferOwnershipPopup.qml | 156 +++++++++++------- .../views/CommunitySettingsView.qml | 7 + ui/app/mainui/AppMain.qml | 7 + ui/app/mainui/Popups.qml | 12 ++ ui/imports/utils/Global.qml | 5 + 10 files changed, 296 insertions(+), 66 deletions(-) create mode 100644 storybook/pages/TransferOwnershipPopupPage.qml diff --git a/storybook/pages/TransferOwnershipPopupPage.qml b/storybook/pages/TransferOwnershipPopupPage.qml new file mode 100644 index 0000000000..ce27f17890 --- /dev/null +++ b/storybook/pages/TransferOwnershipPopupPage.qml @@ -0,0 +1,114 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 + +import Storybook 1.0 +import Models 1.0 + +import AppLayouts.Communities.popups 1.0 +import AppLayouts.Communities.helpers 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() + } + + TransferOwnershipPopup { + id: dialog + + anchors.centerIn: parent + + communityName: communityNameText.text + communityLogo: ModelsData.collectibles.doodles + token: TokenObject { + name: "Owner-CatsComm" + symbol: "OWNCAT" + accountAddress: "0x012304123" + chainId: 1 + artworkSource: ModelsData.collectibles.doodles + } + + onCancelClicked: logs.logEvent("TransferOwnershipPopup::onCancelClicked") + } + } + + 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 + } + + 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 + } + } + } +} + +// category: Popups diff --git a/ui/app/AppLayouts/Chat/ChatLayout.qml b/ui/app/AppLayouts/Chat/ChatLayout.qml index dd6feb756d..0bf612fca1 100644 --- a/ui/app/AppLayouts/Chat/ChatLayout.qml +++ b/ui/app/AppLayouts/Chat/ChatLayout.qml @@ -23,6 +23,7 @@ StackLayout { property var communitiesStore property var sectionItemModel + property var sendModalPopup readonly property bool isOwner: sectionItemModel.memberRole === Constants.memberRole.owner readonly property bool isAdmin: sectionItemModel.memberRole === Constants.memberRole.admin @@ -167,6 +168,7 @@ StackLayout { id: communitySettingsView rootStore: root.rootStore walletAccountsModel: WalletStore.RootStore.nonWatchAccounts + sendModalPopup: root.sendModalPopup chatCommunitySectionModule: root.rootStore.chatCommunitySectionModule community: sectionItemModel diff --git a/ui/app/AppLayouts/Communities/panels/MintTokensFooterPanel.qml b/ui/app/AppLayouts/Communities/panels/MintTokensFooterPanel.qml index b849081ac7..92b98ece86 100644 --- a/ui/app/AppLayouts/Communities/panels/MintTokensFooterPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/MintTokensFooterPanel.qml @@ -11,17 +11,24 @@ import utils 1.0 Control { id: root + property string communityName + property alias airdropEnabled: airdropButton.enabled property alias retailEnabled: retailButton.enabled property alias remotelyDestructEnabled: remotelyDestructButton.enabled property alias burnEnabled: burnButton.enabled + property alias sendOwnershipEnabled: sendOwnershipButton.enabled + + property alias airdropVisible: airdropButton.visible property alias remotelyDestructVisible: remotelyDestructButton.visible property alias burnVisible: burnButton.visible + property alias sendOwnershipVisible: sendOwnershipButton.visible signal airdropClicked signal retailClicked signal remotelyDestructClicked signal burnClicked + signal sendOwnershipClicked height: 61 // by design spacing: Style.current.padding @@ -36,6 +43,15 @@ Control { anchors.centerIn: parent spacing: root.spacing + StatusFlatButton { + id: sendOwnershipButton + + icon.name: "send" + text: qsTr("Send Owner token to transfer %1 Community ownership").arg(root.communityName) + + onClicked: root.sendOwnershipClicked() + } + StatusFlatButton { id: airdropButton diff --git a/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml b/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml index 2a42be64e1..3d91c674c1 100644 --- a/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/MintTokensSettingsPanel.qml @@ -29,6 +29,7 @@ StackView { required property string communityName required property string communityLogo required property color communityColor + property var sendModalPopup // User profile props: required property bool isOwner @@ -655,10 +656,8 @@ StackView { burnTokensPopup.close() } + communityName: root.communityName visible: { - if(tokenViewPage.isOwnerTokenItem) - // Always hidden - return false if(tokenViewPage.isTMasterTokenItem) // Only footer if owner profile return root.isOwner @@ -673,10 +672,13 @@ StackView { !!view.tokenOwnersModel && view.tokenOwnersModel.count > 0 - burnEnabled: deployStateCompleted + burnEnabled: deployStateCompleted + sendOwnershipEnabled: deployStateCompleted - remotelyDestructVisible: token.remotelyDestruct - burnVisible: !token.infiniteSupply + sendOwnershipVisible: tokenViewPage.isOwnerTokenItem + airdropVisible: !tokenViewPage.isOwnerTokenItem + remotelyDestructVisible: !tokenViewPage.isOwnerTokenItem && token.remotelyDestruct + burnVisible: !tokenViewPage.isOwnerTokenItem && !token.infiniteSupply onAirdropClicked: root.airdropToken( view.airdropKey, @@ -685,6 +687,11 @@ StackView { onRemotelyDestructClicked: remotelyDestructPopup.open() onBurnClicked: burnTokensPopup.open() + onSendOwnershipClicked: Global.openTransferOwnershipPopup(root.communityName, + root.communityLogo, + tokenViewPage.token, + root.accounts, + root.sendModalPopup) // helper properties to pass data through popups property var walletsAndAmounts diff --git a/ui/app/AppLayouts/Communities/panels/OverviewSettingsPanel.qml b/ui/app/AppLayouts/Communities/panels/OverviewSettingsPanel.qml index 6680c9ac82..c7ca8eda5d 100644 --- a/ui/app/AppLayouts/Communities/panels/OverviewSettingsPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/OverviewSettingsPanel.qml @@ -6,17 +6,22 @@ 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 AppLayouts.Communities.layouts 1.0 import AppLayouts.Communities.panels 1.0 +import AppLayouts.Communities.popups 1.0 +import AppLayouts.Communities.helpers 1.0 import shared.popups 1.0 + import utils 1.0 StackLayout { id: root + required property bool isOwner property string communityId property string name property string description @@ -32,6 +37,7 @@ StackLayout { property bool requestToJoinEnabled property bool pinMessagesEnabled property string previousPageName: (currentIndex === 1) ? qsTr("Overview") : "" + property var sendModalPopup property bool archiveSupporVisible: true property bool editable: false @@ -39,6 +45,9 @@ StackLayout { property bool isControlNode: false property int loginType: Constants.LoginType.Password property bool communitySettingsDisabled + property var tokensModel + property var accounts // Wallet accounts model. Expected roles: address, name, color, emoji, walletType + readonly property var ownerToken: SQUtils.ModelUtils.getByKey(root.tokensModel, "privilegesLevel", Constants.TokenPrivilegesLevel.Owner) property string overviewChartData: "" @@ -98,6 +107,21 @@ StackLayout { Item { Layout.fillWidth: true } + StatusButton { + Layout.preferredHeight: 38 + Layout.alignment: Qt.AlignTop + objectName: "communityOverviewSettingsTransferOwnershipButton" + visible: root.isOwner && !!root.ownerToken && root.ownerToken.deployState === Constants.ContractTransactionStatus.Completed + text: qsTr("Transfer ownership") + size: StatusBaseButton.Size.Small + + onClicked: Global.openTransferOwnershipPopup(root.name, + root.logoImageData, + root.ownerToken, + root.accounts, + root.sendModalPopup) + } + StatusButton { Layout.preferredHeight: 38 Layout.alignment: Qt.AlignTop diff --git a/ui/app/AppLayouts/Communities/popups/TransferOwnershipPopup.qml b/ui/app/AppLayouts/Communities/popups/TransferOwnershipPopup.qml index d1443fa302..d70cafc6f4 100644 --- a/ui/app/AppLayouts/Communities/popups/TransferOwnershipPopup.qml +++ b/ui/app/AppLayouts/Communities/popups/TransferOwnershipPopup.qml @@ -1,93 +1,129 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.14 -import QtQml.Models 2.14 +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQml.Models 2.15 +import QtGraphicalEffects 1.0 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 -import StatusQ.Core.Utils 0.1 import StatusQ.Controls 0.1 import StatusQ.Popups 0.1 import StatusQ.Popups.Dialog 0.1 +import StatusQ.Core.Utils 0.1 +import StatusQ.Components 0.1 + +import shared.popups 1.0 import utils 1.0 -import shared.controls 1.0 -import shared 1.0 StatusDialog { id: root - property string privateKey - property var store + // Community related props: + property string communityName + property string communityLogo - title: qsTr("Transfer ownership") + // Transaction related props: + property var token + property var accounts + property var sendModalPopup + + signal cancelClicked + + width: 640 // by design padding: Style.current.padding + contentItem: ColumnLayout { + spacing: Style.current.bigPadding - width: 480 - - ColumnLayout { - id: layout - anchors.left: parent.left - anchors.right: parent.right - - spacing: Style.current.padding - - StatusInput { - id: pKeyInput - + component CustomText : StatusBaseText { Layout.fillWidth: true - readonly property string elidedPkey: Utils.getElidedCommunityPK(root.privateKey) - - label: qsTr("Community private key") - - input.text: elidedPkey - input.edit.readOnly: true - input.edit.onActiveFocusChanged: { - pKeyInput.input.text = pKeyInput.input.edit.focus ? root.privateKey : elidedPkey - } - input.rightComponent: StatusButton { - anchors.right: parent.right - anchors.rightMargin: Style.current.halfPadding - anchors.verticalCenter: parent.verticalCenter - borderColor: Theme.palette.primaryColor1 - size: StatusBaseButton.Size.Tiny - text: qsTr("Copy") - objectName: "copyCommunityPrivateKeyButton" - onClicked: { - text = qsTr("Copied") - Utils.copyToClipboard(root.privateKey) - } - } + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + font.pixelSize: Style.current.primaryTextFontSize + color: Theme.palette.directColor1 } - StatusBaseText { - Layout.fillWidth: true + CustomText { + Layout.topMargin: Style.current.halfPadding - text: qsTr("You should keep it safe and only share it with people you trust to take ownership of your community") - wrapMode: Text.WordWrap - font.pixelSize: 13 - color: Theme.palette.baseColor1 + text: qsTr("Are you sure you want to transfer ownership of %1? All ownership rights you currently hold for %1 will be transferred to the new owner.").arg(root.communityName) } - StatusBaseText { - Layout.fillWidth: true + CustomText { + text: qsTr("To transfer ownership of %1:").arg(root.communityName) + font.bold: true + } - text: qsTr("You can also use this key to import your community on another device") - wrapMode: Text.WordWrap - font.pixelSize: 13 - color: Theme.palette.baseColor1 + CustomText { + text: qsTr("1. Send the %1 Owner token (%2) to the new owner’s address").arg(root.communityName).arg(token.name) + } + + CustomText { + text: qsTr("2. Ask the new owner to setup the control node for %1 on their desktop device").arg(root.communityName) + } + + StatusMenuSeparator { + Layout.fillWidth: true + } + + CustomText { + text: qsTr("I acknowledge that...") + } + + StatusCheckBox { + id: ackCheckBox + + Layout.topMargin: -Style.current.halfPadding + Layout.bottomMargin: Style.current.halfPadding + + font.pixelSize: Style.current.primaryTextFontSize + text: qsTr("My ownership rights will be removed and transferred to the recipient") + } + } + + header: StatusDialogHeader { + headline.title: qsTr("Transfer ownership of %1").arg(root.communityName) + actions.closeButton.onClicked: root.close() + leftComponent: StatusSmartIdenticon { + asset.name: root.communityLogo + asset.isImage: !!asset.name } } footer: StatusDialogFooter { - leftButtons: ObjectModel { - StatusBackButton { + spacing: Style.current.padding + rightButtons: ObjectModel { + StatusFlatButton { + text: qsTr("Cancel") + onClicked: { - root.close() + root.cancelClicked() + close() + } + } + + StatusButton { + enabled: ackCheckBox.checked + text: qsTr("Send %1 owner token").arg(root.communityName) + type: StatusBaseButton.Type.Danger + + onClicked: { + // Pre-populated dialog with the relevant Owner token info: + root.sendModalPopup.sendType = Constants.SendType.Transfer + root.sendModalPopup.selectedAccount = ModelUtils.getByKey(root.accounts, "address", token.accountAddress) + root.sendModalPopup.preSelectedHolding = { + uid : token.key, + chainId: token.chainId, + name: token.name, + imageUrl: token.artworkSource, + collectionUid: "", + collectionName: "" + } + root.sendModalPopup.preSelectedHoldingType = Constants.HoldingType.Collectible + root.sendModalPopup.open() + close() } } } } } - diff --git a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml index fe8b4ddc1e..60a01b8484 100644 --- a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml @@ -35,6 +35,7 @@ StatusSectionLayout { property var community property var transactionStore: TransactionStore {} property bool communitySettingsDisabled + property var sendModalPopup required property var walletAccountsModel // name, address, emoji, color @@ -161,6 +162,7 @@ StatusSectionLayout { readonly property string sectionIcon: "show" readonly property bool sectionEnabled: true + isOwner: root.isOwner communityId: root.community.id name: root.community.name description: root.community.description @@ -182,6 +184,10 @@ StatusSectionLayout { communitySettingsDisabled: root.communitySettingsDisabled overviewChartData: rootStore.overviewChartData + sendModalPopup: root.sendModalPopup + tokensModel: root.community.communityTokens + accounts: root.walletAccountsModel + onCollectCommunityMetricsMessagesCount: { rootStore.collectCommunityMetricsMessagesCount(intervals) } @@ -305,6 +311,7 @@ StatusSectionLayout { communityName: root.community.name communityLogo: root.community.image communityColor: root.community.color + sendModalPopup: root.sendModalPopup // User profile props isOwner: root.isOwner diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index 90ce479386..39aec3d3b2 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -1240,6 +1240,7 @@ Item { } } + sendModalPopup: sendModal emojiPopup: statusEmojiPopup.item stickersPopup: statusStickersPopupLoader.item sectionItemModel: model @@ -1337,6 +1338,7 @@ Item { this.active = false } property var selectedAccount + property var preSelectedHolding property string preSelectedHoldingID property int preSelectedHoldingType property int sendType: -1 @@ -1348,6 +1350,8 @@ Item { sendModal.sendType = -1 sendModal.preSelectedHoldingID = "" sendModal.preSelectedHoldingType = Constants.HoldingType.Unknown + sendModal.preSelectedHolding = undefined + sendModal.selectedAccount = undefined } } onLoaded: { @@ -1361,6 +1365,9 @@ Item { item.preSelectedHoldingID = sendModal.preSelectedHoldingID item.preSelectedHoldingType = sendModal.preSelectedHoldingType } + if(!!preSelectedHolding) { + item.preSelectedHolding = preSelectedHolding + } } } diff --git a/ui/app/mainui/Popups.qml b/ui/app/mainui/Popups.qml index 37d72e27cf..ea676971cd 100644 --- a/ui/app/mainui/Popups.qml +++ b/ui/app/mainui/Popups.qml @@ -65,6 +65,7 @@ QtObject { Global.openExportControlNodePopup.connect(openExportControlNodePopup) Global.openImportControlNodePopup.connect(openImportControlNodePopup) Global.openEditSharedAddressesFlow.connect(openEditSharedAddressesPopup) + Global.openTransferOwnershipPopup.connect(openTransferOwnershipPopup) } property var currentPopup @@ -285,6 +286,10 @@ QtObject { openPopup(importControlNodePopup, { community }) } + function openTransferOwnershipPopup(communityName, communityLogo, token, accounts, sendModalPopup) { + openPopup(transferOwnershipPopup, { communityName, communityLogo, token, accounts, sendModalPopup }) + } + readonly property list _components: [ Component { id: removeContactConfirmationDialog @@ -739,6 +744,13 @@ QtObject { editSharedAddressesPopup.communityId, sharedAddresses, airdropAddress) onClosed: destroy() } + }, + + Component { + id: transferOwnershipPopup + TransferOwnershipPopup { + onClosed: destroy() + } } ] } diff --git a/ui/imports/utils/Global.qml b/ui/imports/utils/Global.qml index 0ab900df75..de6907faf6 100644 --- a/ui/imports/utils/Global.qml +++ b/ui/imports/utils/Global.qml @@ -49,6 +49,11 @@ QtObject { signal openExportControlNodePopup(var community) signal openImportControlNodePopup(var community) signal contactRenamed(string publicKey) + signal openTransferOwnershipPopup(string communityName, + string communityLogo, + var token, + var accounts, + var sendModalPopup) signal openLink(string link)