feat: Unfurl transaction deep link

This commit is contained in:
Emil Sawicki 2024-10-14 06:31:51 +02:00
parent aefe804dc4
commit 3a4e53d8eb
12 changed files with 416 additions and 11 deletions

View File

@ -35,6 +35,8 @@ type
StatusCommunityChannelCommunityPreview
StatusCommunityChannelCommunityPreviewIcon
StatusCommunityChannelCommunityPreviewBanner
# Status transaction
StatusTransactionPreview
QtObject:
type
@ -105,6 +107,8 @@ QtObject:
ModelRole.StatusCommunityChannelCommunityPreview.int:"statusCommunityChannelCommunityPreview",
ModelRole.StatusCommunityChannelCommunityPreviewIcon.int:"statusCommunityChannelCommunityPreviewIcon",
ModelRole.StatusCommunityChannelCommunityPreviewBanner.int:"statusCommunityChannelCommunityPreviewBanner",
# Transaction
ModelRole.StatusTransactionPreview.int:"statusTransactionPreview",
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -165,6 +169,9 @@ QtObject:
of ModelRole.StatusCommunityChannelCommunityPreviewBanner:
if (let community = item.linkPreview.getChannelCommunity(); community) != nil:
result = newQVariant(community.getBanner())
of ModelRole.StatusTransactionPreview:
if item.linkPreview.statusTransactionPreview != nil:
result = newQVariant(item.linkPreview.statusTransactionPreview)
else:
result = newQVariant()

View File

@ -1,6 +1,6 @@
import json, stew/shims/strformat, tables
import ./status_link_preview, ./standard_link_preview
import ./status_contact_link_preview, ./status_community_link_preview, ./status_community_channel_link_preview
import ./status_contact_link_preview, ./status_community_link_preview, ./status_community_channel_link_preview, ./status_transaction_link_preview
import ../../contacts/dto/contact_details
import ../../community/dto/community
include ../../../common/json_utils
@ -13,6 +13,7 @@ type
StatusContactPreview
StatusCommunityPreview
StatusCommunityChannelPreview
StatusTransactionPreview
LinkPreview* = ref object
url*: string
@ -21,6 +22,7 @@ type
statusContactPreview*: StatusContactLinkPreview
statusCommunityPreview*: StatusCommunityLinkPreview
statusCommunityChannelPreview*: StatusCommunityChannelLinkPreview
statusTransactionPreview*: StatusTransactionLinkPreview
proc delete*(self: LinkPreview) =
if self.standardPreview != nil:
@ -31,6 +33,8 @@ proc delete*(self: LinkPreview) =
self.statusCommunityPreview.delete
if self.statusCommunityChannelPreview != nil:
self.statusCommunityChannelPreview.delete
if self.statusTransactionPreview != nil:
self.statusTransactionPreview.delete
proc initLinkPreview*(url: string): LinkPreview =
result = LinkPreview()
@ -57,19 +61,24 @@ proc toLinkPreview*(jsonObj: JsonNode, standard: bool): LinkPreview =
elif jsonObj.getProp("channel", node):
result.previewType = PreviewType.StatusCommunityChannelPreview
result.statusCommunityChannelPreview = toStatusCommunityChannelLinkPreview(node)
elif jsonObj.getProp("transaction", node):
result.previewType = PreviewType.StatusTransactionPreview
result.statusTransactionPreview = toStatusTransactionLinkPreview(node)
proc `$`*(self: LinkPreview): string =
let standardPreview = if self.standardPreview != nil: $self.standardPreview else: ""
let contactPreview = if self.statusContactPreview != nil: $self.statusContactPreview else: ""
let communityPreview = if self.statusCommunityPreview != nil: $self.statusCommunityPreview else: ""
let channelPreview = if self.statusCommunityChannelPreview != nil: $self.statusCommunityChannelPreview else: ""
let transactionPreview = if self.statusTransactionPreview != nil: $self.statusTransactionPreview else: ""
result = fmt"""LinkPreview(
url: {self.url},
previewType: {self.previewType},
standardPreview: {standardPreview},
contactPreview: {contactPreview},
communityPreview: {communityPreview},
channelPreview: {channelPreview}
channelPreview: {channelPreview},
transactionPreview: {transactionPreview}
)"""
proc `%`*(self: LinkPreview): JsonNode =
@ -78,7 +87,8 @@ proc `%`*(self: LinkPreview): JsonNode =
"standardPreview": if self.standardPreview != nil: %self.standardPreview else: newJNull(),
"contactPreview": if self.statusContactPreview != nil: %self.statusContactPreview else: newJNull(),
"communityPreview": if self.statusCommunityPreview != nil: %self.statusCommunityPreview else: newJNull(),
"channelPreview": if self.statusCommunityChannelPreview != nil: %self.statusCommunityChannelPreview else: newJNull()
"channelPreview": if self.statusCommunityChannelPreview != nil: %self.statusCommunityChannelPreview else: newJNull(),
"transactionPreview": if self.statusTransactionPreview != nil: %self.statusTransactionPreview else: newJNull()
}
proc empty*(self: LinkPreview): bool =
@ -91,6 +101,8 @@ proc empty*(self: LinkPreview): bool =
return self.statusCommunityPreview == nil or self.statusCommunityPreview.empty()
of PreviewType.StatusCommunityChannelPreview:
return self.statusCommunityChannelPreview == nil or self.statusCommunityChannelPreview.empty()
of PreviewType.StatusTransactionPreview:
return self.statusTransactionPreview == nil or self.statusTransactionPreview.empty()
else:
return true
@ -119,6 +131,11 @@ proc extractLinkPreviewsLists*(input: seq[LinkPreview]): (seq[StandardLinkPrevie
statusLinkPreview.url = preview.url
statusLinkPreview.channel = preview.statusCommunityChannelPreview
status.add(statusLinkPreview)
of PreviewType.StatusTransactionPreview:
let statusLinkPreview = StatusLinkPreview()
statusLinkPreview.url = preview.url
statusLinkPreview.transaction = preview.statusTransactionPreview
status.add(statusLinkPreview)
else:
discard

View File

@ -2,6 +2,7 @@ import json, chronicles
import status_contact_link_preview
import status_community_link_preview
import status_community_channel_link_preview
import status_transaction_link_preview
include ../../../common/json_utils
@ -10,6 +11,7 @@ type StatusLinkPreview* = ref object
contact*: StatusContactLinkPreview
community*: StatusCommunityLinkPreview
channel*: StatusCommunityChannelLinkPreview
transaction*: StatusTransactionLinkPreview
proc toStatusLinkPreview*(jsonObj: JsonNode): StatusLinkPreview =
result = StatusLinkPreview()
@ -27,6 +29,10 @@ proc toStatusLinkPreview*(jsonObj: JsonNode): StatusLinkPreview =
if jsonObj.getProp("channel", channel):
result.channel = toStatusCommunityChannelLinkPreview(contact)
var transaction: JsonNode
if jsonObj.getProp("transaction", transaction):
result.transaction = toStatusTransactionLinkPreview(transaction)
proc `%`*(self: StatusLinkPreview): JsonNode =
var obj = %*{
"url": self.url
@ -37,4 +43,6 @@ proc `%`*(self: StatusLinkPreview): JsonNode =
obj["community"] = %*self.community
if self.channel != nil:
obj["channel"] = %*self.channel
if self.transaction != nil:
obj["transaction"] = %*self.transaction
return obj

View File

@ -0,0 +1,110 @@
import json, stew/shims/strformat, NimQml, chronicles
include ../../../common/json_utils
QtObject:
type StatusTransactionLinkPreview* = ref object of QObject
txType: int
amount: string
asset: string
toAsset: string
address: string
chainId: int
proc setup*(self: StatusTransactionLinkPreview) =
self.QObject.setup()
proc delete*(self: StatusTransactionLinkPreview) =
self.QObject.delete()
proc newStatusTransactionLinkPreview*(txType: int, amount: string, asset: string, toAsset: string, address: string, chainId: int): StatusTransactionLinkPreview =
new(result, delete)
result.setup()
result.txType = txType
result.amount = amount
result.asset = asset
result.toAsset = toAsset
result.address = address
result.chainId = chainId
proc txTypeChanged*(self: StatusTransactionLinkPreview) {.signal.}
proc getTxType*(self: StatusTransactionLinkPreview): int {.slot.} =
result = self.txType
QtProperty[int] txType:
read = getTxType
notify = txTypeChanged
proc amountChanged*(self: StatusTransactionLinkPreview) {.signal.}
proc getAmount*(self: StatusTransactionLinkPreview): string {.slot.} =
result = self.amount
QtProperty[string] amount:
read = getAmount
notify = amountChanged
proc assetChanged*(self: StatusTransactionLinkPreview) {.signal.}
proc getAsset*(self: StatusTransactionLinkPreview): string {.slot.} =
result = self.asset
QtProperty[string] asset:
read = getAsset
notify = assetChanged
proc toAssetChanged*(self: StatusTransactionLinkPreview) {.signal.}
proc getToAsset*(self: StatusTransactionLinkPreview): string {.slot.} =
result = self.toAsset
QtProperty[string] toAsset:
read = getToAsset
notify = toAssetChanged
proc addressChanged*(self: StatusTransactionLinkPreview) {.signal.}
proc getAddress*(self: StatusTransactionLinkPreview): string {.slot.} =
result = self.address
QtProperty[string] address:
read = getAddress
notify = addressChanged
proc chainIdChanged*(self: StatusTransactionLinkPreview) {.signal.}
proc getChainId*(self: StatusTransactionLinkPreview): int {.slot.} =
result = self.chainId
QtProperty[int] chainId:
read = getChainId
notify = chainIdChanged
proc toStatusTransactionLinkPreview*(jsonObj: JsonNode): StatusTransactionLinkPreview =
var txType: int
var amount: string
var asset: string
var toAsset: string
var address: string
var chainId: int
discard jsonObj.getProp("txType", txType)
discard jsonObj.getProp("amount", amount)
discard jsonObj.getProp("asset", asset)
discard jsonObj.getProp("toAsset", toAsset)
discard jsonObj.getProp("address", address)
discard jsonObj.getProp("chainId", chainId)
result = newStatusTransactionLinkPreview(txType, amount, asset, toAsset, address, chainId)
proc `$`*(self: StatusTransactionLinkPreview): string =
result = fmt"""StatusTransactionLinkPreview(
txType: {self.txType},
amount: {self.amount},
asset: {self.asset},
toAsset: {self.toAsset},
address: {self.address},
chainId: {self.chainId}
)"""
proc `%`*(self: StatusTransactionLinkPreview): JsonNode =
return %* {
"txType": self.txType,
"amount": self.amount,
"asset": self.asset,
"toAsset": self.toAsset,
"address": self.address,
"chainId": self.chainId
}
proc empty*(self: StatusTransactionLinkPreview): bool =
return self.amount.len == 0 and self.asset.len == 0 and self.toAsset.len == 0 and self.address.len == 0

View File

@ -90,6 +90,14 @@ SplitView {
color: "orchid"
}
}
transactionData {
txType: txTypeInput.currentValue
address: addressInput.text
amount: amountInput.text
asset: symbolInput.text
toAsset: toSymbolInput.text
chainId: networkInput.currentValue
}
}
}
@ -138,6 +146,16 @@ SplitView {
descriptionInput.text = "Channel description goes here. If blank it will enable multi line title."
}
}
RadioButton {
text: qsTr("Transaction")
checked: previewCard.type === Constants.LinkPreviewType.StatusTransaction
onToggled: {
previewCard.type = Constants.LinkPreviewType.StatusTransaction
titleInput.text = "Transaction"
descriptionInput.text = "Transaction description goes here. If blank it will enable multi line title."
}
}
}
ColumnLayout {
visible: previewCard.type !== Constants.LinkPreviewType.StatusContact
@ -264,6 +282,76 @@ SplitView {
}
}
ColumnLayout {
visible: previewCard.type === Constants.LinkPreviewType.StatusTransaction
Label {
text: "Transaction type"
}
ComboBox {
id: txTypeInput
Layout.fillHeight: true
Layout.fillWidth: true
textRole: "text"
valueRole: "value"
model: [
{ value: Constants.SendType.Transfer, text: "Send" },
{ value: Constants.SendType.Bridge, text: "Bridge" },
{ value: Constants.SendType.Swap, text: "Swap" }
]
}
Label {
text: "Address"
}
TextField {
id: addressInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "0x60f80121c31a0d46b5279700f9df786054aa5ee5"
}
Label {
text: "Amount"
}
TextField {
id: amountInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "0.1"
}
Label {
text: "Symbol"
}
TextField {
id: symbolInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "ETH"
}
Label {
text: "toSymbol"
}
TextField {
id: toSymbolInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "DAI"
}
Label {
text: "Network"
}
ComboBox {
id: networkInput
Layout.fillHeight: true
Layout.fillWidth: true
textRole: "text"
valueRole: "value"
model: [
{ value: Constants.chains.mainnetChainId, text: "Mainet" },
{ value: Constants.chains.optimismChainId, text: "Optimism" },
{ value: Constants.chains.arbitrumChainId, text: "Arbitrum" }
]
}
}
Label {
Layout.fillWidth: true
text: "Logo"

View File

@ -57,16 +57,35 @@ SplitView {
color: "orchid"
}
}
transactionData {
txType: txTypeInput.currentValue
address: addressInput.text
amount: amountInput.text
asset: symbolInput.text
toAsset: toSymbolInput.text
chainId: networkInput.currentValue
}
}
}
Pane {
SplitView.preferredWidth: 300
SplitView.fillHeight: true
ColumnLayout {
spacing: 24
Flickable {
anchors.fill: parent
contentHeight: dataColumn.height
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AlwaysOn }
ColumnLayout {
id: dataColumn
Label {
text: "State"
}
ComboBox {
id: stateInput
Layout.fillHeight: true
Layout.fillWidth: true
model: ["invalid", "loading", "loading failed", "loaded"]
}
Label {
text: "Preview type"
}
@ -74,7 +93,7 @@ SplitView {
id: previewTypeInput
Layout.fillHeight: true
Layout.fillWidth: true
model: ["unknown", "standard", "user profile", "community", "channel"]
model: ["unknown", "standard", "user profile", "community", "channel", "transaction"]
}
Label {
text: "Community name"
@ -145,13 +164,74 @@ SplitView {
text: "https://rarible.com/public/favicon.png"
}
Label {
text: "State"
text: "\nTransaction preview:\n"
font.bold: true
}
Label {
text: "Transaction type"
}
ComboBox {
id: stateInput
id: txTypeInput
Layout.fillHeight: true
Layout.fillWidth: true
model: ["invalid", "loading", "loading failed", "loaded"]
textRole: "text"
valueRole: "value"
model: [
{ value: Constants.SendType.Transfer, text: "Send" },
{ value: Constants.SendType.Bridge, text: "Bridge" },
{ value: Constants.SendType.Swap, text: "Swap" }
]
}
Label {
text: "Address"
}
TextField {
id: addressInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "0x60f80121c31a0d46b5279700f9df786054aa5ee5"
}
Label {
text: "Amount"
}
TextField {
id: amountInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "0.1"
}
Label {
text: "Symbol"
}
TextField {
id: symbolInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "ETH"
}
Label {
text: "toSymbol"
}
TextField {
id: toSymbolInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "DAI"
}
Label {
text: "Network"
}
ComboBox {
id: networkInput
Layout.fillHeight: true
Layout.fillWidth: true
textRole: "text"
valueRole: "value"
model: [
{ value: Constants.chains.mainnetChainId, text: "Mainet" },
{ value: Constants.chains.optimismChainId, text: "Optimism" },
{ value: Constants.chains.arbitrumChainId, text: "Arbitrum" }
]
}
}
}

View File

@ -17,6 +17,7 @@ CalloutCard {
readonly property LinkData linkData: LinkData { }
readonly property UserData userData: UserData { }
readonly property TransactionData transactionData: TransactionData { }
readonly property CommunityData communityData: CommunityData { }
readonly property ChannelData channelData: ChannelData { }
@ -284,7 +285,50 @@ CalloutCard {
PropertyChanges { target: footerLoader; active: false; visible: !root.userData.bio; Layout.fillHeight: true }
PropertyChanges { target: title; text: root.userData.name }
PropertyChanges { target: description; text: root.userData.bio; Layout.minimumHeight: 32; visible: true }
},
State {
name: "transaction"
when: root.type === Constants.LinkPreviewType.StatusTransaction
PropertyChanges { target: root; implicitHeight: 187 }
PropertyChanges { target: bannerImageLoader; visible: false }
PropertyChanges { target: footerLoader; active: false; visible: false }
PropertyChanges {
target: title
text: {
switch(root.transactionData.txType) {
case Constants.SendType.Bridge:
return qsTr("Bridge transaction")
case Constants.SendType.Swap:
return qsTr("Swap transaction")
default:
return qsTr("Send transaction")
}
}
}
PropertyChanges {
target: description
text: {
let description = "\n"
if (!!root.transactionData.amount) {
description += qsTr("Amount: %1\n").arg(root.transactionData.amount)
}
if (!!root.transactionData.asset) {
description += qsTr("Asset: %1\n").arg(root.transactionData.asset)
}
if (!!root.transactionData.toAsset) {
description += qsTr("To Asset: %1\n").arg(root.transactionData.asset)
}
if (!!root.transactionData.address) {
description += qsTr("Recipient: %1\n").arg(root.transactionData.address)
}
if (root.transactionData.chainId > 0) {
description += qsTr("Network: %1").arg(root.transactionData.chainId)
}
return description
}
}
}
]
QtObject {

View File

@ -25,6 +25,7 @@ CalloutCard {
readonly property UserData userData: UserData { }
readonly property CommunityData communityData: CommunityData { }
readonly property ChannelData channelData: ChannelData { }
readonly property TransactionData transactionData: TransactionData { }
required property int previewState
required property int type
@ -147,7 +148,28 @@ CalloutCard {
asset.charactersLen: 2
asset.color: Theme.palette.miscColor9
}
},
State {
name: "loadedTransaction"
when: root.previewState === LinkPreviewMiniCard.State.Loaded && root.type === Constants.LinkPreviewType.StatusTransaction
extend: "loaded"
PropertyChanges { target: titleText; text: qsTr("Transaction") }
PropertyChanges {
target: subtitleText; visible: true;
text: {
switch(root.transactionData.txType) {
case Constants.SendType.Bridge:
return qsTr("Bridge")
case Constants.SendType.Swap:
return qsTr("Swap")
default:
return qsTr("Send")
}
}
}
PropertyChanges { target: favIcon; visible: false }
}
]
contentItem: Item {

View File

@ -0,0 +1,10 @@
import QtQuick 2.15
QtObject {
property int txType
property string asset
property string toAsset
property string amount
property string address
property int chainId
}

View File

@ -48,6 +48,7 @@ LinkPreviewCard {
required property var statusCommunityChannelCommunityPreview
required property var statusCommunityChannelCommunityPreviewIcon
required property var statusCommunityChannelCommunityPreviewBanner
required property var statusTransactionPreview
//View properties
type: root.previewType
@ -91,4 +92,12 @@ LinkPreviewCard {
color: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.color : ""
}
}
transactionData {
txType: statusTransactionPreview ? statusTransactionPreview.txType : ""
asset: statusTransactionPreview ? statusTransactionPreview.asset : ""
toAsset: statusTransactionPreview ? statusTransactionPreview.toAsset : ""
amount: statusTransactionPreview ? statusTransactionPreview.amount : ""
chainId: statusTransactionPreview ? statusTransactionPreview.chainId : ""
address: statusTransactionPreview ? statusTransactionPreview.address : ""
}
}

View File

@ -42,6 +42,7 @@ LinkPreviewMiniCard {
required property var statusCommunityChannelCommunityPreview
required property var statusCommunityChannelCommunityPreviewIcon
required property var statusCommunityChannelCommunityPreviewBanner
required property var statusTransactionPreview
previewState: !root.unfurled ? LinkPreviewMiniCard.State.Loading : root.unfurled && !root.empty ? LinkPreviewMiniCard.State.Loaded : LinkPreviewMiniCard.State.LoadingFailed
type: root.previewType
@ -83,4 +84,12 @@ LinkPreviewMiniCard {
color: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.color : ""
}
}
transactionData {
txType: statusTransactionPreview ? statusTransactionPreview.txType : ""
asset: statusTransactionPreview ? statusTransactionPreview.asset : ""
toAsset: statusTransactionPreview ? statusTransactionPreview.toAsset : ""
amount: statusTransactionPreview ? statusTransactionPreview.amount : ""
chainId: statusTransactionPreview ? statusTransactionPreview.chainId : ""
address: statusTransactionPreview ? statusTransactionPreview.address : ""
}
}

View File

@ -1411,7 +1411,8 @@ QtObject {
Standard = 1,
StatusContact = 2,
StatusCommunity = 3,
StatusCommunityChannel = 4
StatusCommunityChannel = 4,
StatusTransaction = 5
}
enum StandardLinkPreviewType {