diff --git a/storybook/pages/CollectiblesViewPage.qml b/storybook/pages/CollectiblesViewPage.qml index a868e8d32c..cddaf5d3a1 100644 --- a/storybook/pages/CollectiblesViewPage.qml +++ b/storybook/pages/CollectiblesViewPage.qml @@ -112,6 +112,7 @@ SplitView { qsTr("%1 community collectibles are now visible").arg(communityName), "", "checkmark-circle", false, Constants.ephemeralNotificationType.success, "") } + ownedAccountsModel: WalletAccountsModel {} networkFilters: d.networksChainsCurrentlySelected addressFilters: d.addressesSelected filterVisible: ctrlFilterVisible.checked diff --git a/ui/app/AppLayouts/Wallet/WalletLayout.qml b/ui/app/AppLayouts/Wallet/WalletLayout.qml index 70d0b9375d..d37b0d24c6 100644 --- a/ui/app/AppLayouts/Wallet/WalletLayout.qml +++ b/ui/app/AppLayouts/Wallet/WalletLayout.qml @@ -304,7 +304,7 @@ Item { const ownership = StatusQUtils.ModelUtils.get(tokenItem.ownership, 0) Global.openTransferOwnershipPopup(tokenItem.communityId, - tokenItem.communityName, + footer.communityName, tokenItem.communityImage, { "key": tokenItem.tokenId, @@ -316,14 +316,20 @@ Item { "tokenAddress": tokenItem.contractAddress }, root.sendModalPopup) - } else { - // Common send modal popup: - root.sendModalPopup.preSelectedSendType = Constants.SendType.Transfer - root.sendModalPopup.preSelectedHoldingID = walletStore.currentViewedHoldingID - root.sendModalPopup.preSelectedHoldingType = walletStore.currentViewedHoldingType - root.sendModalPopup.onlyAssets = false - root.sendModalPopup.open() + return } + + if (isHoldingSelected) { + const tokenItem = walletStore.currentViewedCollectible + const ownership = StatusQUtils.ModelUtils.get(tokenItem.ownership, 0) + root.sendModalPopup.preSelectedAccountAddress = ownership.accountAddress + } + // Common send modal popup: + root.sendModalPopup.preSelectedSendType = Constants.SendType.Transfer + root.sendModalPopup.preSelectedHoldingID = walletStore.currentViewedHoldingID + root.sendModalPopup.preSelectedHoldingType = walletStore.currentViewedHoldingType + root.sendModalPopup.onlyAssets = false + root.sendModalPopup.open() } onLaunchBridgeModal: { root.sendModalPopup.preSelectedSendType = Constants.SendType.Bridge diff --git a/ui/app/AppLayouts/Wallet/panels/WalletFooter.qml b/ui/app/AppLayouts/Wallet/panels/WalletFooter.qml index 348382f6b9..21607d69ad 100644 --- a/ui/app/AppLayouts/Wallet/panels/WalletFooter.qml +++ b/ui/app/AppLayouts/Wallet/panels/WalletFooter.qml @@ -2,9 +2,11 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 import QtQuick.Layouts 1.13 +import StatusQ 0.1 import StatusQ.Popups 0.1 import StatusQ.Controls 0.1 import StatusQ.Core.Theme 0.1 +import StatusQ.Core.Utils 0.1 as SQUtils import utils 1.0 import shared.controls 1.0 @@ -37,6 +39,22 @@ Rectangle { (walletStore.currentViewedHoldingType === Constants.TokenType.ERC721 || walletStore.currentViewedHoldingType === Constants.TokenType.ERC1155) readonly property bool isCollectibleSoulbound: isCollectibleViewed && !!walletStore.currentViewedCollectible && walletStore.currentViewedCollectible.soulbound + + readonly property bool isCollectibleOwned: d.owningAccount.available + + readonly property bool hideCollectibleTransferActions: isCollectibleViewed && !isCollectibleOwned + + property string collectibleOwnerAddress + Binding on collectibleOwnerAddress { + when: d.isCollectibleViewed && !!root.walletStore.currentViewedCollectible && !!root.walletStore.currentViewedCollectible.ownership.ModelCount.count + value: SQUtils.ModelUtils.get(root.walletStore.currentViewedCollectible.ownership, 0).accountAddress + } + + readonly property ModelEntry owningAccount: ModelEntry { + sourceModel: d.isCollectibleViewed ? root.walletStore.nonWatchAccounts : null + key: "address" + value: d.collectibleOwnerAddress ?? "" + } } StatusModalDivider { @@ -55,11 +73,14 @@ Rectangle { text: root.isCommunityOwnershipTransfer ? qsTr("Send Owner token to transfer %1 Community ownership").arg(root.communityName) : qsTr("Send") interactive: !d.isCollectibleSoulbound && networkConnectionStore.sendBuyBridgeEnabled onClicked: { - root.transactionStore.setSenderAccount(root.walletStore.selectedAddress) + if (!!root.walletStore.selectedAddress) + root.transactionStore.setSenderAccount(root.walletStore.selectedAddress) root.launchSendModal() } tooltip.text: d.isCollectibleSoulbound ? qsTr("Soulbound collectibles cannot be sent to another wallet") : networkConnectionStore.sendBuyBridgeToolTipText - visible: !walletStore.overview.isWatchOnlyAccount && walletStore.overview.canSend && !root.walletStore.showAllAccounts + visible: !walletStore.overview.isWatchOnlyAccount + && walletStore.overview.canSend + && !d.hideCollectibleTransferActions } StatusFlatButton { @@ -78,7 +99,11 @@ Rectangle { interactive: !d.isCollectibleSoulbound && networkConnectionStore.sendBuyBridgeEnabled onClicked: root.launchBridgeModal() tooltip.text: d.isCollectibleSoulbound ? qsTr("Soulbound collectibles cannot be bridged to another wallet") : networkConnectionStore.sendBuyBridgeToolTipText - visible: !walletStore.overview.isWatchOnlyAccount && !root.isCommunityOwnershipTransfer && walletStore.overview.canSend && !root.walletStore.showAllAccounts + visible: !walletStore.overview.isWatchOnlyAccount + && !root.isCommunityOwnershipTransfer + && walletStore.overview.canSend + && !root.walletStore.showAllAccounts + && !d.hideCollectibleTransferActions } StatusFlatButton { @@ -94,7 +119,7 @@ Rectangle { id: swap interactive: !d.isCollectibleSoulbound && networkConnectionStore.sendBuyBridgeEnabled - visible: Global.featureFlags.swapEnabled && !walletStore.overview.isWatchOnlyAccount + visible: Global.featureFlags.swapEnabled && !walletStore.overview.isWatchOnlyAccount && !d.hideCollectibleTransferActions tooltip.text: d.isCollectibleSoulbound ? qsTr("Soulbound collectibles cannot be swapped") : networkConnectionStore.sendBuyBridgeToolTipText icon.name: "swap" text: qsTr("Swap") diff --git a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml index 9affd78ec8..54f492b262 100644 --- a/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml +++ b/ui/app/AppLayouts/Wallet/views/CollectiblesView.qml @@ -28,6 +28,7 @@ import SortFilterProxyModel 0.2 ColumnLayout { id: root + required property var ownedAccountsModel required property var controller required property string addressFilters required property string networkFilters @@ -466,10 +467,17 @@ ColumnLayout { onClicked: root.collectibleClicked(model.chainId, model.contractAddress, model.tokenId, model.symbol, model.tokenType) onRightClicked: { + let ownedByUser = false + const ownedByAccount = ModelUtils.get(model.ownership, 0) + if (!!ownedByAccount && !!ownedByAccount.accountAddress) { + ownedByUser = ModelUtils.contains(root.ownedAccountsModel, "address", ownedByAccount.accountAddress, Qt.CaseInsensitive) + } + Global.openMenu(tokenContextMenu, this, {symbol: model.symbol, tokenName: model.name, tokenImage: model.imageUrl, communityId: model.communityId, communityName: model.communityName, - communityImage: model.communityImage, tokenType: model.tokenType}) + communityImage: model.communityImage, tokenType: model.tokenType, + soulbound: model.soulbound, ownedByUser: ownedByUser}) } onSwitchToCommunityRequested: (communityId) => root.switchToCommunityRequested(communityId) } @@ -478,6 +486,7 @@ ColumnLayout { Component { id: tokenContextMenu StatusMenu { + id: tokenMenu onClosed: destroy() property string symbol @@ -487,14 +496,24 @@ ColumnLayout { property string communityName property string communityImage property int tokenType + property bool ownedByUser + property bool soulbound - StatusAction { - enabled: root.sendEnabled - visibleOnDisabled: true - icon.name: "send" - text: qsTr("Send") - onTriggered: root.sendRequested(symbol, tokenType) + // Show send button for owned collectibles + // Disable send button for owned soulbound collectibles + Instantiator { + model: tokenMenu.ownedByUser ? 1 : 0 + delegate: StatusAction { + enabled: root.sendEnabled && !tokenMenu.soulbound + visibleOnDisabled: true + icon.name: "send" + text: qsTr("Send") + onTriggered: root.sendRequested(symbol, tokenType) + } + onObjectAdded: tokenMenu.insertAction(0, object) + onObjectRemoved: tokenMenu.removeAction(0) } + StatusAction { icon.name: "receive" text: qsTr("Receive") diff --git a/ui/app/AppLayouts/Wallet/views/RightTabView.qml b/ui/app/AppLayouts/Wallet/views/RightTabView.qml index a374680656..ad7fcc401a 100644 --- a/ui/app/AppLayouts/Wallet/views/RightTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/RightTabView.qml @@ -248,6 +248,7 @@ RightTabBaseView { Component { id: collectiblesView CollectiblesView { + ownedAccountsModel: RootStore.nonWatchAccounts controller: RootStore.collectiblesStore.collectiblesController networkFilters: RootStore.networkFilters addressFilters: RootStore.addressFilters @@ -265,14 +266,38 @@ RightTabBaseView { stack.currentIndex = 1 } onSendRequested: (symbol, tokenType) => { - root.sendModal.preSelectedHoldingID = symbol - root.sendModal.preSelectedHoldingType = tokenType - root.sendModal.preSelectedSendType = tokenType === Constants.TokenType.ERC721 ? - Constants.SendType.ERC721Transfer: - Constants.SendType.ERC1155Transfer - root.sendModal.onlyAssets = false - root.sendModal.open() - } + const collectible = ModelUtils.getByKey(controller.sourceModel, "symbol", symbol) + if (collectible.communityPrivilegesLevel === Constants.TokenPrivilegesLevel.Owner) { + const ownerAccountItem = ModelUtils.get(collectible.ownership, 0) + Global.openTransferOwnershipPopup(collectible.communityId, + collectible.communityName, + collectible.communityImage, + { + "key": collectible.tokenId, + "privilegesLevel": collectible.communityPrivilegesLevel, + "chainId": collectible.chainId, + "name": collectible.name, + "artworkSource": collectible.communityImage, + "accountAddress": ownerAccountItem.accountAddress, + "tokenAddress": collectible.contractAddress + }, + root.sendModal) + return + + } + + if (!!collectible) { + const ownerAccountItem = ModelUtils.get(collectible.ownership, 0) + root.sendModal.preSelectedAccountAddress = ownerAccountItem.accountAddress + } + root.sendModal.preSelectedHoldingID = symbol + root.sendModal.preSelectedHoldingType = tokenType + root.sendModal.preSelectedSendType = tokenType === Constants.TokenType.ERC721 ? + Constants.SendType.ERC721Transfer: + Constants.SendType.ERC1155Transfer + root.sendModal.onlyAssets = false + root.sendModal.open() + } onReceiveRequested: (symbol) => root.launchShareAddressModal() onSwitchToCommunityRequested: (communityId) => Global.switchToCommunity(communityId) onManageTokensRequested: Global.changeAppSectionBySectionType(Constants.appSection.profile, Constants.settingsSubsection.wallet,