feat(@desktop/wallet): Link out from collectible details view to opensea
fixes #13918
This commit is contained in:
parent
1851838e64
commit
f45a39bfcf
|
@ -1,4 +1,4 @@
|
|||
import NimQml, strutils, uri, stew/shims/strformat, strutils, stint, re
|
||||
import NimQml, strutils, uri, stew/shims/strformat, strutils, stint, re, httpclient
|
||||
import stew/byteutils
|
||||
import ./utils/qrcodegen
|
||||
import ./utils/time_utils
|
||||
|
@ -9,6 +9,8 @@ import ../../app_service/service/visual_identity/service as procs_from_visual_id
|
|||
|
||||
include ../../app_service/service/accounts/utils
|
||||
|
||||
const URL_STATUS_OK* = "200 OK"
|
||||
|
||||
QtObject:
|
||||
type Utils* = ref object of QObject
|
||||
|
||||
|
@ -181,4 +183,12 @@ QtObject:
|
|||
return value
|
||||
|
||||
proc addTimestampToURL*(self: Utils, url: string): string {.slot.} =
|
||||
return time_utils.addTimestampToURL(url)
|
||||
return time_utils.addTimestampToURL(url)
|
||||
|
||||
proc isValidURL*(self: Utils, url: string): bool {.slot.} =
|
||||
var client = newHttpClient()
|
||||
defer: client.close()
|
||||
try:
|
||||
return client.head(url).status == URL_STATUS_OK
|
||||
except:
|
||||
return false
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import
|
||||
json, tables, sequtils, httpclient, net
|
||||
json, tables, sequtils, net
|
||||
import json, strutils, stew/shims/strformat, tables, chronicles, unicode, times
|
||||
import
|
||||
json_serialization, chronicles, libp2p/[multihash, multicodec, cid], stint, nimcrypto
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import NimQml, Tables, json, sequtils, chronicles, strutils, sets, stint
|
||||
|
||||
import httpclient
|
||||
|
||||
import app/core/[main]
|
||||
import app/core/tasks/[qt, threadpool]
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml, Tables, json, sequtils, sugar, chronicles, stew/shims/strformat, stint, httpclient
|
||||
import NimQml, Tables, json, sequtils, sugar, chronicles, stew/shims/strformat, stint
|
||||
import net, strutils, os, times, algorithm, options
|
||||
import web3/ethtypes
|
||||
|
||||
|
|
|
@ -24,6 +24,10 @@ ColumnLayout {
|
|||
property string networkIconURL
|
||||
property string networkExplorerName
|
||||
|
||||
property bool collectibleLinkEnabled
|
||||
property bool collectionLinkEnabled
|
||||
property bool explorerLinkEnabled
|
||||
|
||||
signal collectionTagClicked()
|
||||
signal openCollectibleExternally()
|
||||
signal openCollectibleOnExplorer()
|
||||
|
@ -58,15 +62,15 @@ ColumnLayout {
|
|||
size: StatusBaseButton.Size.Small
|
||||
text: root.networkExplorerName
|
||||
icon.name: "external"
|
||||
asset.emoji: d.effectiveEmoji
|
||||
onClicked: root.openCollectibleOnExplorer()
|
||||
visible: root.explorerLinkEnabled
|
||||
}
|
||||
StatusButton {
|
||||
size: StatusBaseButton.Size.Small
|
||||
text: "OpenSea"
|
||||
icon.name: "external"
|
||||
asset.emoji: d.effectiveEmoji
|
||||
onClicked: root.openCollectibleExternally()
|
||||
visible: root.collectibleLinkEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,11 +82,13 @@ ColumnLayout {
|
|||
id: collectionTag
|
||||
asset.name: !!root.communityImage ? root.communityImage: !sensor.containsMouse ? root.isCollection ? "tiny/folder" : "tiny/profile" : "tiny/external"
|
||||
asset.isImage: !!root.communityImage
|
||||
enabled: root.collectionLinkEnabled
|
||||
MouseArea {
|
||||
id: sensor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: root.collectionLinkEnabled
|
||||
cursorShape: root.collectionLinkEnabled ? Qt.PointingHandCursor: undefined
|
||||
enabled: root.collectionLinkEnabled
|
||||
onClicked: {
|
||||
root.collectionTagClicked()
|
||||
}
|
||||
|
|
|
@ -493,7 +493,7 @@ QtObject {
|
|||
return root.mainModuleInst.addressWasShown(address)
|
||||
}
|
||||
|
||||
function getExplorerUrl(networkShortName, contractAddress, tokenId) {
|
||||
function getExplorerDomain(networkShortName) {
|
||||
let link = Constants.networkExplorerLinks.etherscan
|
||||
if (networkShortName === Constants.networkShortChainNames.mainnet) {
|
||||
if (root.areTestNetworksEnabled) {
|
||||
|
@ -503,7 +503,6 @@ QtObject {
|
|||
link = Constants.networkExplorerLinks.goerliEtherscan
|
||||
}
|
||||
}
|
||||
return "%1/nft/%2/%3".arg(link).arg(contractAddress).arg(tokenId)
|
||||
}
|
||||
if (networkShortName === Constants.networkShortChainNames.arbitrum) {
|
||||
link = Constants.networkExplorerLinks.arbiscan
|
||||
|
@ -514,7 +513,6 @@ QtObject {
|
|||
link = Constants.networkExplorerLinks.goerliArbiscan
|
||||
}
|
||||
}
|
||||
return "%1/token/%2?a=%3".arg(link).arg(contractAddress).arg(tokenId)
|
||||
} else if (networkShortName === Constants.networkShortChainNames.optimism) {
|
||||
link = Constants.networkExplorerLinks.optimism
|
||||
if (root.areTestNetworksEnabled) {
|
||||
|
@ -524,6 +522,16 @@ QtObject {
|
|||
link = Constants.networkExplorerLinks.goerliOptimism
|
||||
}
|
||||
}
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
function getExplorerUrl(networkShortName, contractAddress, tokenId) {
|
||||
let link = getExplorerDomain(networkShortName)
|
||||
if (networkShortName === Constants.networkShortChainNames.mainnet) {
|
||||
return "%1/nft/%2/%3".arg(link).arg(contractAddress).arg(tokenId)
|
||||
}
|
||||
else {
|
||||
return "%1/token/%2?a=%3".arg(link).arg(contractAddress).arg(tokenId)
|
||||
}
|
||||
}
|
||||
|
@ -537,4 +545,53 @@ QtObject {
|
|||
}
|
||||
return qsTr("Etherscan Explorer")
|
||||
}
|
||||
|
||||
function getOpenSeaNetworkName(networkShortName) {
|
||||
let networkName = Constants.openseaExplorerLinks.ethereum
|
||||
if (networkShortName === Constants.networkShortChainNames.mainnet) {
|
||||
if (root.areTestNetworksEnabled) {
|
||||
if (!root.isGoerliEnabled) {
|
||||
networkName = Constants.openseaExplorerLinks.sepoliaEthereum
|
||||
} else {
|
||||
networkName = Constants.openseaExplorerLinks.goerliEthereum
|
||||
}
|
||||
}
|
||||
}
|
||||
if (networkShortName === Constants.networkShortChainNames.arbitrum) {
|
||||
networkName = Constants.openseaExplorerLinks.arbitrum
|
||||
if (root.areTestNetworksEnabled) {
|
||||
if (!root.isGoerliEnabled) {
|
||||
networkName = Constants.openseaExplorerLinks.sepoliaArbitrum
|
||||
} else {
|
||||
networkName = Constants.openseaExplorerLinks.goerliArbitrum
|
||||
}
|
||||
}
|
||||
} else if (networkShortName === Constants.networkShortChainNames.optimism) {
|
||||
networkName = Constants.openseaExplorerLinks.optimism
|
||||
if (root.areTestNetworksEnabled) {
|
||||
if (!root.isGoerliEnabled) {
|
||||
networkName = Constants.openseaExplorerLinks.sepoliaOptimism
|
||||
} else {
|
||||
networkName = Constants.openseaExplorerLinks.goerliOptimism
|
||||
}
|
||||
}
|
||||
}
|
||||
return networkName
|
||||
}
|
||||
|
||||
function getOpenseaDomainName() {
|
||||
return root.areTestNetworksEnabled ? Constants.openseaExplorerLinks.testnetLink : Constants.openseaExplorerLinks.mainnetLink
|
||||
}
|
||||
|
||||
function getOpenSeaCollectionUrl(networkShortName, contractAddress) {
|
||||
let networkName = getOpenSeaNetworkName(networkShortName)
|
||||
let baseLink = root.areTestNetworksEnabled ? Constants.openseaExplorerLinks.testnetLink : Constants.openseaExplorerLinks.mainnetLink
|
||||
return "%1/assets/%2/%3".arg(baseLink).arg(networkName).arg(contractAddress)
|
||||
}
|
||||
|
||||
function getOpenSeaCollectibleUrl(networkShortName, contractAddress, tokenId) {
|
||||
let networkName = getOpenSeaNetworkName(networkShortName)
|
||||
let baseLink = root.areTestNetworksEnabled ? Constants.openseaExplorerLinks.testnetLink : Constants.openseaExplorerLinks.mainnetLink
|
||||
return "%1/assets/%2/%3/%4".arg(baseLink).arg(networkName).arg(contractAddress).arg(tokenId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,13 @@ Item {
|
|||
|
||||
readonly property var communityDetails: isCommunityCollectible ? root.communitiesStore.getCommunityDetailsAsJson(collectible.communityId) : null
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property string collectibleLink: root.walletRootStore.getOpenSeaCollectibleUrl(collectible.networkShortName, collectible.contractAddress, collectible.tokenId)
|
||||
readonly property string collectionLink: root.walletRootStore.getOpenSeaCollectionUrl(collectible.networkShortName, collectible.contractAddress)
|
||||
readonly property string blockExplorerLink: root.walletRootStore.getExplorerUrl(collectible.networkShortName, collectible.contractAddress, collectible.tokenId)
|
||||
}
|
||||
|
||||
CollectibleDetailsHeader {
|
||||
id: collectibleHeader
|
||||
anchors.top: parent.top
|
||||
|
@ -50,22 +57,22 @@ Item {
|
|||
networkColor: collectible.networkColor
|
||||
networkIconURL: collectible.networkIconUrl
|
||||
networkExplorerName: root.walletRootStore.getExplorerNameForNetwork(collectible.networkShortName)
|
||||
collectibleLinkEnabled: Utils.getUrlStatus(d.collectibleLink)
|
||||
collectionLinkEnabled: (!!communityDetails && communityDetails.name) || Utils.getUrlStatus(d.collectionLink)
|
||||
explorerLinkEnabled: Utils.getUrlStatus(d.blockExplorerLink)
|
||||
onCollectionTagClicked: {
|
||||
if (root.isCommunityCollectible) {
|
||||
Global.switchToCommunity(collectible.communityId)
|
||||
}
|
||||
/* TODO for non community token link out to collection on opensea
|
||||
https://github.com/status-im/status-desktop/issues/13918 */
|
||||
|
||||
else {
|
||||
Global.openLinkWithConfirmation(d.collectionLink, root.walletRootStore.getOpenseaDomainName())
|
||||
}
|
||||
}
|
||||
onOpenCollectibleExternally: {
|
||||
/* TODO add link out to opensea
|
||||
https://github.com/status-im/status-desktop/issues/13918 */
|
||||
}
|
||||
onOpenCollectibleOnExplorer: Global.openLink(root.walletRootStore.getExplorerUrl(collectible.networkShortName, collectible.contractAddress, collectible.tokenId))
|
||||
onOpenCollectibleExternally: Global.openLinkWithConfirmation(d.collectibleLink, root.walletRootStore.getOpenseaDomainName())
|
||||
onOpenCollectibleOnExplorer: Global.openLinkWithConfirmation(d.blockExplorerLink, root.walletRootStore.getExplorerDomain(networkShortName))
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Column {
|
||||
id: collectibleBody
|
||||
anchors.top: collectibleHeader.bottom
|
||||
anchors.topMargin: 25
|
||||
|
@ -82,8 +89,8 @@ Item {
|
|||
readonly property real visibleImageHeight: (collectibleimage.visible ? collectibleimage.height : privilegedCollectibleImage.height)
|
||||
readonly property real visibleImageWidth: (collectibleimage.visible ? collectibleimage.width : privilegedCollectibleImage.width)
|
||||
|
||||
Layout.preferredHeight: collectibleImageDetails.visibleImageHeight
|
||||
Layout.preferredWidth: parent.width
|
||||
height: collectibleImageDetails.visibleImageHeight
|
||||
width: parent.width
|
||||
spacing: 24
|
||||
|
||||
// Special artwork representation for community `Owner and Master Token` token types:
|
||||
|
@ -132,36 +139,28 @@ Item {
|
|||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
StatusScrollView {
|
||||
id: descriptionScrollView
|
||||
StatusBaseText {
|
||||
id: descriptionText
|
||||
width: parent.width
|
||||
height: collectibleImageDetails.height - collectibleName.height - parent.spacing
|
||||
|
||||
contentWidth: availableWidth
|
||||
|
||||
padding: 0
|
||||
|
||||
StatusBaseText {
|
||||
id: descriptionText
|
||||
width: descriptionScrollView.availableWidth
|
||||
|
||||
text: collectible.description
|
||||
textFormat: Text.MarkdownText
|
||||
color: Theme.palette.directColor4
|
||||
font.pixelSize: 15
|
||||
lineHeight: 22
|
||||
lineHeightMode: Text.FixedHeight
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
clip: true
|
||||
text: collectible.description
|
||||
textFormat: Text.MarkdownText
|
||||
color: Theme.palette.directColor4
|
||||
font.pixelSize: 15
|
||||
lineHeight: 22
|
||||
lineHeightMode: Text.FixedHeight
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusTabBar {
|
||||
id: collectiblesDetailsTab
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.current.xlPadding
|
||||
width: parent.width
|
||||
topPadding: Style.current.xlPadding
|
||||
visible: collectible.traits.count > 0
|
||||
|
||||
StatusTabButton {
|
||||
|
@ -178,14 +177,15 @@ Item {
|
|||
|
||||
StatusScrollView {
|
||||
id: scrollView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
contentWidth: availableWidth
|
||||
|
||||
Loader {
|
||||
id: tabLoader
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
sourceComponent: {
|
||||
switch (collectiblesDetailsTab.currentIndex) {
|
||||
case 0: return traitsView
|
||||
|
|
|
@ -916,6 +916,23 @@ QtObject {
|
|||
readonly property string txPath: "tx"
|
||||
}
|
||||
|
||||
readonly property QtObject openseaExplorerLinks: QtObject {
|
||||
readonly property string mainnetLink: "https://opensea.io"
|
||||
readonly property string testnetLink: "https://testnets.opensea.io"
|
||||
|
||||
readonly property string ethereum: "ethereum"
|
||||
readonly property string arbitrum: "arbitrum"
|
||||
readonly property string optimism: "optimism"
|
||||
|
||||
readonly property string goerliEthereum: "goerli"
|
||||
readonly property string goerliArbitrum: "arbitrum-goerli"
|
||||
readonly property string goerliOptimism: "optimism-goerli"
|
||||
|
||||
readonly property string sepoliaEthereum: "sepolia"
|
||||
readonly property string sepoliaArbitrum: "arbitrum-sepolia"
|
||||
readonly property string sepoliaOptimism: "optimism-sepolia"
|
||||
}
|
||||
|
||||
readonly property string api_request: "api-request"
|
||||
readonly property string web3SendAsyncReadOnly: "web3-send-async-read-only"
|
||||
readonly property string web3DisconnectAccount: "web3-disconnect-account"
|
||||
|
|
|
@ -991,4 +991,8 @@ QtObject {
|
|||
|
||||
return communitiesModuleInst.isDisplayNameDupeOfCommunityMember(displayName)
|
||||
}
|
||||
|
||||
function getUrlStatus(url) {
|
||||
return globalUtilsInst.isValidURL(url)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue