feat(@desktop/wallet): Link out from collectible details view to opensea

fixes #13918
This commit is contained in:
Khushboo Mehta 2024-03-26 16:00:16 +01:00 committed by Khushboo-dev-cpp
parent 1851838e64
commit f45a39bfcf
9 changed files with 141 additions and 49 deletions

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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()
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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"

View File

@ -991,4 +991,8 @@ QtObject {
return communitiesModuleInst.isDisplayNameDupeOfCommunityMember(displayName)
}
function getUrlStatus(url) {
return globalUtilsInst.isValidURL(url)
}
}