feat: Transaction deep link sharing

This commit is contained in:
Emil Sawicki 2024-10-14 06:36:03 +02:00
parent 5d12440562
commit 2000e112e6
31 changed files with 279 additions and 6 deletions

View File

@ -5,6 +5,7 @@ const DEFAULT_FLAG_DAPPS_ENABLED = false
const DEFAULT_FLAG_SWAP_ENABLED = true const DEFAULT_FLAG_SWAP_ENABLED = true
const DEFAULT_FLAG_CONNECTOR_ENABLED* = false const DEFAULT_FLAG_CONNECTOR_ENABLED* = false
const DEFAULT_FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED = true const DEFAULT_FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED = true
const DEFAULT_FLAG_TRANSACTION_DEEP_LINK_ENABLED = false
proc boolToEnv*(defaultValue: bool): string = proc boolToEnv*(defaultValue: bool): string =
return if defaultValue: "1" else: "0" return if defaultValue: "1" else: "0"
@ -15,6 +16,7 @@ QtObject:
swapEnabled: bool swapEnabled: bool
connectorEnabled: bool connectorEnabled: bool
sendViaPersonalChatEnabled: bool sendViaPersonalChatEnabled: bool
transactionDeepLinkEnabled: bool
proc setup(self: FeatureFlags) = proc setup(self: FeatureFlags) =
self.QObject.setup() self.QObject.setup()
@ -22,6 +24,7 @@ QtObject:
self.swapEnabled = getEnv("FLAG_SWAP_ENABLED", boolToEnv(DEFAULT_FLAG_SWAP_ENABLED)) != "0" self.swapEnabled = getEnv("FLAG_SWAP_ENABLED", boolToEnv(DEFAULT_FLAG_SWAP_ENABLED)) != "0"
self.connectorEnabled = getEnv("FLAG_CONNECTOR_ENABLED", boolToEnv(DEFAULT_FLAG_CONNECTOR_ENABLED)) != "0" self.connectorEnabled = getEnv("FLAG_CONNECTOR_ENABLED", boolToEnv(DEFAULT_FLAG_CONNECTOR_ENABLED)) != "0"
self.sendViaPersonalChatEnabled = getEnv("FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED", boolToEnv(DEFAULT_FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED)) != "0" self.sendViaPersonalChatEnabled = getEnv("FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED", boolToEnv(DEFAULT_FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED)) != "0"
self.transactionDeepLinkEnabled = getEnv("FLAG_TRANSACTION_DEEP_LINK_ENABLED", boolToEnv(DEFAULT_FLAG_TRANSACTION_DEEP_LINK_ENABLED)) != "0"
proc delete*(self: FeatureFlags) = proc delete*(self: FeatureFlags) =
self.QObject.delete() self.QObject.delete()
@ -53,3 +56,9 @@ QtObject:
QtProperty[bool] sendViaPersonalChatEnabled: QtProperty[bool] sendViaPersonalChatEnabled:
read = getSendViaPersonalChatEnabled read = getSendViaPersonalChatEnabled
proc getTransactionDeepLinkEnabled*(self: FeatureFlags): bool {.slot.} =
return self.transactionDeepLinkEnabled
QtProperty[bool] transactionDeepLinkEnabled:
read = getTransactionDeepLinkEnabled

View File

@ -1657,6 +1657,8 @@ method activateStatusDeepLink*[T](self: Module[T], statusDeepLink: string) =
self.onStatusUrlRequested(StatusUrlAction.DisplayUserProfile, communityId="", channelId="", url="", self.onStatusUrlRequested(StatusUrlAction.DisplayUserProfile, communityId="", channelId="", url="",
urlData.contact.publicKey, urlData.community.shard) urlData.contact.publicKey, urlData.community.shard)
return return
if urlData.transaction.txType >= 0:
self.view.emitShowTransactionModal(urlData.transaction.txType, urlData.transaction.asset, urlData.transaction.amount, urlData.transaction.address, urlData.transaction.chainId, urlData.transaction.toAsset)
method onDeactivateChatLoader*[T](self: Module[T], sectionId: string, chatId: string) = method onDeactivateChatLoader*[T](self: Module[T], sectionId: string, chatId: string) =
if (sectionId.len > 0 and self.chatSectionModules.contains(sectionId)): if (sectionId.len > 0 and self.chatSectionModules.contains(sectionId)):

View File

@ -37,3 +37,7 @@ proc parseContactSharedUrl*(self: Controller, url: string): ContactUrlDataDto =
proc parseSharedUrl*(self: Controller, url: string): UrlDataDto = proc parseSharedUrl*(self: Controller, url: string): UrlDataDto =
return self.sharedUrlsService.parseSharedUrl(url) return self.sharedUrlsService.parseSharedUrl(url)
proc parseTransactionSharedUrl*(self: Controller, url: string): TransactionUrlDataDto =
let data = self.sharedUrlsService.parseSharedUrl(url)
return data.transaction

View File

@ -27,6 +27,9 @@ method parseContactSharedUrl*(self: AccessInterface, url: string): string {.base
method parseSharedUrl*(self: AccessInterface, url: string): UrlDataDto {.base.} = method parseSharedUrl*(self: AccessInterface, url: string): UrlDataDto {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method parseTransactionSharedUrl*(self: AccessInterface, url: string): string {.base.} =
raise newException(ValueError, "No implementation available")
# This way (using concepts) is used only for the modules managed by AppController # This way (using concepts) is used only for the modules managed by AppController
type type
DelegateInterface* = concept c DelegateInterface* = concept c

View File

@ -63,3 +63,7 @@ method parseCommunityChannelSharedUrl*(self: Module, url: string): string =
method parseContactSharedUrl*(self: Module, url: string): string = method parseContactSharedUrl*(self: Module, url: string): string =
let contactData = self.controller.parseContactSharedUrl(url) let contactData = self.controller.parseContactSharedUrl(url)
return $contactData return $contactData
method parseTransactionSharedUrl*(self: Module, url: string): string =
let transactionData = self.controller.parseTransactionSharedUrl(url)
return $transactionData

View File

@ -26,3 +26,6 @@ QtObject:
proc parseContactSharedUrl*(self: View, url: string): string {.slot.} = proc parseContactSharedUrl*(self: View, url: string): string {.slot.} =
return self.delegate.parseContactSharedUrl(url) return self.delegate.parseContactSharedUrl(url)
proc parseTransactionSharedUrl*(self: View, url: string): string {.slot.} =
return self.delegate.parseTransactionSharedUrl(url)

View File

@ -382,3 +382,8 @@ QtObject:
proc stopTokenHoldersManagement*(self: View) {.slot.} = proc stopTokenHoldersManagement*(self: View) {.slot.} =
self.delegate.stopTokenHoldersManagement() self.delegate.stopTokenHoldersManagement()
proc showTransactionModal*(self: View, txType: int, asset: string, amount: string, address: string, chainId: int, toAsset: string) {.signal.}
proc emitShowTransactionModal*(self: View, txType: int, asset: string, amount: string, address: string, chainId: int, toAsset: string) =
self.showTransactionModal(txType, asset, amount, address, chainId, toAsset)

View File

@ -8,6 +8,7 @@ import app_service/service/currency/service as currency_service
import app_service/service/currency/dto as currency_dto import app_service/service/currency/dto as currency_dto
import app_service/service/keycard/service as keycard_service import app_service/service/keycard/service as keycard_service
import app_service/service/network/network_item import app_service/service/network/network_item
import app_service/service/shared_urls/dto/url_data as shared_urls_dto
import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module
import app/modules/shared/wallet_utils import app/modules/shared/wallet_utils
@ -137,6 +138,9 @@ proc signMessage*(self: Controller, address: string, hashedPassword: string, has
proc sendRouterTransactionsWithSignatures*(self: Controller, uuid: string, signatures: TransactionsSignatures): string = proc sendRouterTransactionsWithSignatures*(self: Controller, uuid: string, signatures: TransactionsSignatures): string =
return self.transactionService.sendRouterTransactionsWithSignatures(uuid, signatures) return self.transactionService.sendRouterTransactionsWithSignatures(uuid, signatures)
proc shareTransactionURL*(self: Controller, urlData: shared_urls_dto.TransactionURLDataDto): string =
return self.transactionService.shareTransactionURL(urlData)
proc areTestNetworksEnabled*(self: Controller): bool = proc areTestNetworksEnabled*(self: Controller): bool =
return self.walletAccountService.areTestNetworksEnabled() return self.walletAccountService.areTestNetworksEnabled()

View File

@ -2,6 +2,7 @@ import Tables
import app/modules/shared_models/currency_amount import app/modules/shared_models/currency_amount
import app_service/service/transaction/dto import app_service/service/transaction/dto
import app_service/service/transaction/router_transactions_dto import app_service/service/transaction/router_transactions_dto
import app_service/service/shared_urls/dto/url_data
import app_service/service/network/network_item import app_service/service/network/network_item
import app/modules/shared_models/collectibles_model as collectibles import app/modules/shared_models/collectibles_model as collectibles
from app_service/service/keycard/service import KeycardEvent from app_service/service/keycard/service import KeycardEvent
@ -91,3 +92,6 @@ method getNetworkItem*(self: AccessInterface, chainId: int): NetworkItem {.base.
method getNetworkChainId*(self: AccessInterface, shortName: string): int {.base.} = method getNetworkChainId*(self: AccessInterface, shortName: string): int {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method shareTransactionURL*(self: AccessInterface, urlData: TransactionURLDataDto): string {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -14,6 +14,7 @@ import app_service/service/transaction/service as transaction_service
import app_service/service/keycard/service as keycard_service import app_service/service/keycard/service as keycard_service
import app_service/service/keycard/constants as keycard_constants import app_service/service/keycard/constants as keycard_constants
import app_service/service/transaction/dto import app_service/service/transaction/dto
import app_service/service/shared_urls/dto/url_data as shared_urls_dto
import app/modules/shared_models/currency_amount import app/modules/shared_models/currency_amount
import app_service/service/network/network_item as network_service_item import app_service/service/network/network_item as network_service_item
@ -410,3 +411,6 @@ method splitAndFormatAddressPrefix*(self: Module, text : string, updateInStore:
method transactionSendingComplete*(self: Module, txHash: string, status: string) = method transactionSendingComplete*(self: Module, txHash: string, status: string) =
self.clearTmpData(self.tmpKeepPinPass) self.clearTmpData(self.tmpKeepPinPass)
self.view.sendtransactionSendingCompleteSignal(txHash, status) self.view.sendtransactionSendingCompleteSignal(txHash, status)
method shareTransactionURL*(self: Module, urlData: shared_urls_dto.TransactionURLDataDto): string =
return self.controller.shareTransactionURL(urlData)

View File

@ -3,6 +3,7 @@ import NimQml, Tables, json, sequtils, strutils, stint, chronicles
import ./io_interface, ./network_route_model, ./network_route_item, ./suggested_route_item, ./transaction_routes import ./io_interface, ./network_route_model, ./network_route_item, ./suggested_route_item, ./transaction_routes
import app_service/service/network/service as network_service import app_service/service/network/service as network_service
import app_service/service/transaction/dto as transaction_dto import app_service/service/transaction/dto as transaction_dto
import app_service/service/shared_urls/dto/url_data as shared_urls_dto
import app_service/common/utils as common_utils import app_service/common/utils as common_utils
import app_service/service/eth/utils as eth_utils import app_service/service/eth/utils as eth_utils
@ -287,6 +288,16 @@ QtObject:
parseChainIds(disabledToChainIDs), parseChainIds(disabledToChainIDs),
lockedInAmountsTable) lockedInAmountsTable)
proc shareTransactionURL*(self: View, txType: int, asset: string, amount: string, address: string, chainId: int, toAsset: string): string {.slot.} =
return self.delegate.shareTransactionURL(shared_urls_dto.TransactionURLDataDto(
txType: txType,
asset: asset,
amount: amount,
address: address,
chainId: chainId,
toAsset: toAsset
))
proc transactionSendingComplete*(self: View, txHash: string, status: string) {.signal.} proc transactionSendingComplete*(self: View, txHash: string, status: string) {.signal.}
proc sendtransactionSendingCompleteSignal*(self: View, txHash: string, status: string) = proc sendtransactionSendingCompleteSignal*(self: View, txHash: string, status: string) =
self.transactionSendingComplete(txHash, status) self.transactionSendingComplete(txHash, status)

View File

@ -25,10 +25,19 @@ type ContactUrlDataDto* = object
description*: string description*: string
publicKey*: string publicKey*: string
type TransactionURLDataDto* = object
txType*: int
asset*: string
amount*: string
address*: string
chainId*: int
toAsset*: string
type UrlDataDto* = object type UrlDataDto* = object
community*: CommunityUrlDataDto community*: CommunityUrlDataDto
channel*: CommunityChannelUrlDataDto channel*: CommunityChannelUrlDataDto
contact*: ContactUrlDataDto contact*: ContactUrlDataDto
transaction*: TransactionURLDataDto
notASupportedStatusLink*: bool # If this is true, it was not a supported status link, so we should open it in a browser notASupportedStatusLink*: bool # If this is true, it was not a supported status link, so we should open it in a browser
proc getShard*(jsonObj: JsonNode): Shard = proc getShard*(jsonObj: JsonNode): Shard =
@ -69,8 +78,18 @@ proc toContactUrlDataDto*(jsonObj: JsonNode): ContactUrlDataDto =
discard jsonObj.getProp("description", result.description) discard jsonObj.getProp("description", result.description)
discard jsonObj.getProp("publicKey", result.publicKey) discard jsonObj.getProp("publicKey", result.publicKey)
proc toTransactionUrlDataDto*(jsonObj: JsonNode): TransactionURLDataDto =
result = TransactionURLDataDto()
discard jsonObj.getProp("txType", result.txType)
discard jsonObj.getProp("asset", result.asset)
discard jsonObj.getProp("amount", result.amount)
discard jsonObj.getProp("address", result.address)
discard jsonObj.getProp("chainId", result.chainId)
discard jsonObj.getProp("toAsset", result.toAsset)
proc toUrlDataDto*(jsonObj: JsonNode): UrlDataDto = proc toUrlDataDto*(jsonObj: JsonNode): UrlDataDto =
result = UrlDataDto() result = UrlDataDto()
result.transaction.txType = -1
var communityObj: JsonNode var communityObj: JsonNode
if (jsonObj.getProp("community", communityObj)): if (jsonObj.getProp("community", communityObj)):
@ -84,6 +103,10 @@ proc toUrlDataDto*(jsonObj: JsonNode): UrlDataDto =
if (jsonObj.getProp("contact", contactObj)): if (jsonObj.getProp("contact", contactObj)):
result.contact = contactObj.toContactUrlDataDto() result.contact = contactObj.toContactUrlDataDto()
var txObj: JsonNode
if (jsonObj.getProp("tx", txObj)):
result.transaction = txObj.toTransactionUrlDataDto()
proc toJsonNode*(communityUrlDataDto: CommunityUrlDataDto): JsonNode = proc toJsonNode*(communityUrlDataDto: CommunityUrlDataDto): JsonNode =
var jsonObj = newJObject() var jsonObj = newJObject()
jsonObj["displayName"] = %* communityUrlDataDto.displayName jsonObj["displayName"] = %* communityUrlDataDto.displayName
@ -113,3 +136,16 @@ proc `$`*(contactUrlDataDto: ContactUrlDataDto): string =
jsonObj["description"] = %* contactUrlDataDto.description jsonObj["description"] = %* contactUrlDataDto.description
jsonObj["publicKey"] = %* contactUrlDataDto.publicKey jsonObj["publicKey"] = %* contactUrlDataDto.publicKey
return $jsonObj return $jsonObj
proc `%`*(transactionURLData: TransactionURLDataDto): JsonNode =
return %* [{
"txType": transactionURLData.txType,
"asset": transactionURLData.asset,
"amount": transactionURLData.amount,
"address": transactionURLData.address,
"chainId": transactionURLData.chainId,
"toAsset": transactionURLData.toAsset,
}]
proc `$`*(transactionURLData: TransactionURLDataDto): string =
return $(%transactionURLData)

View File

@ -19,6 +19,7 @@ import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/network/service as network_service import app_service/service/network/service as network_service
import app_service/service/token/service as token_service import app_service/service/token/service as token_service
import app_service/service/settings/service as settings_service import app_service/service/settings/service as settings_service
import app_service/service/shared_urls/dto/url_data as shared_urls_dto
import ./dto as transaction_dto import ./dto as transaction_dto
import ./dtoV2 import ./dtoV2
import ./dto_conversion import ./dto_conversion
@ -504,3 +505,14 @@ proc sendRouterTransactionsWithSignatures*(self: Service, uuid: string, signatur
error "unexpected sending transactions response" error "unexpected sending transactions response"
return "unexpected sending transactions response" return "unexpected sending transactions response"
return "" return ""
proc shareTransactionURL*(self: Service, urlData: shared_urls_dto.TransactionURLDataDto): string =
try:
let response = transactions.shareTransactionURL(%urlData)
if response.error != nil:
error "Error sharing transaction url. Error: ", message = response.error
return ""
return response.result.getStr
except Exception as e:
error "Error sharing transaction url", message = e.msg
return ""

View File

@ -1,4 +1,5 @@
import Tables, json, stint, json_serialization, stew/shims/strformat, logging import Tables, json, stint, json_serialization, stew/shims/strformat, logging
import ../app_service/common/utils
import ./core as core import ./core as core
@ -118,3 +119,6 @@ proc sendRouterTransactionsWithSignatures*(resultOut: var JsonNode, uuid: string
except Exception as e: except Exception as e:
warn e.msg warn e.msg
return e.msg return e.msg
proc shareTransactionURL*(urlData: JsonNode): RpcResponse[JsonNode] =
return callPrivateRPC("shareTransactionURL".prefix, urlData)

View File

@ -47,6 +47,7 @@ StackLayout {
property bool communitySettingsDisabled property bool communitySettingsDisabled
property bool sendViaPersonalChatEnabled property bool sendViaPersonalChatEnabled
property bool transactionDeepLinkEnabled
property var emojiPopup property var emojiPopup
property var stickersPopup property var stickersPopup
@ -163,6 +164,7 @@ StackLayout {
root.sectionItemModel.memberRole === Constants.memberRole.tokenMaster root.sectionItemModel.memberRole === Constants.memberRole.tokenMaster
hasViewOnlyPermissions: root.permissionsStore.viewOnlyPermissionsModel.count > 0 hasViewOnlyPermissions: root.permissionsStore.viewOnlyPermissionsModel.count > 0
sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled
transactionDeepLinkEnabled: root.transactionDeepLinkEnabled
hasUnrestrictedViewOnlyPermission: { hasUnrestrictedViewOnlyPermission: {
viewOnlyUnrestrictedPermissionHelper.revision viewOnlyUnrestrictedPermissionHelper.revision

View File

@ -58,6 +58,7 @@ Item {
property bool amISectionAdmin: false property bool amISectionAdmin: false
property bool sendViaPersonalChatEnabled property bool sendViaPersonalChatEnabled
property bool transactionDeepLinkEnabled
signal openStickerPackPopup(string stickerPackId) signal openStickerPackPopup(string stickerPackId)
@ -243,6 +244,7 @@ Item {
stickersLoaded: root.stickersLoaded stickersLoaded: root.stickersLoaded
isBlocked: model.blocked isBlocked: model.blocked
sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled
transactionDeepLinkEnabled: root.transactionDeepLinkEnabled
onOpenStickerPackPopup: { onOpenStickerPackPopup: {
root.openStickerPackPopup(stickerPackId) root.openStickerPackPopup(stickerPackId)
} }

View File

@ -55,6 +55,7 @@ ColumnLayout {
} }
property bool sendViaPersonalChatEnabled property bool sendViaPersonalChatEnabled
property bool transactionDeepLinkEnabled
signal showReplyArea(messageId: string) signal showReplyArea(messageId: string)
signal forceInputFocus() signal forceInputFocus()
@ -97,6 +98,7 @@ ColumnLayout {
isChatBlocked: root.isBlocked || !root.isUserAllowedToSendMessage isChatBlocked: root.isBlocked || !root.isUserAllowedToSendMessage
channelEmoji: !chatContentModule ? "" : (chatContentModule.chatDetails.emoji || "") channelEmoji: !chatContentModule ? "" : (chatContentModule.chatDetails.emoji || "")
sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled
transactionDeepLinkEnabled: root.transactionDeepLinkEnabled
onShowReplyArea: (messageId, senderId) => { onShowReplyArea: (messageId, senderId) => {
root.showReplyArea(messageId) root.showReplyArea(messageId)
} }

View File

@ -49,6 +49,7 @@ Item {
property bool isOneToOne: false property bool isOneToOne: false
property bool sendViaPersonalChatEnabled property bool sendViaPersonalChatEnabled
property bool transactionDeepLinkEnabled
signal openStickerPackPopup(string stickerPackId) signal openStickerPackPopup(string stickerPackId)
signal showReplyArea(string messageId, string author) signal showReplyArea(string messageId, string author)
@ -284,6 +285,7 @@ Item {
isChatBlocked: root.isChatBlocked isChatBlocked: root.isChatBlocked
sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled
transactionDeepLinkEnabled: root.transactionDeepLinkEnabled
chatId: root.chatId chatId: root.chatId
messageId: model.id messageId: model.id

View File

@ -76,6 +76,7 @@ StatusSectionLayout {
property var collectiblesModel property var collectiblesModel
property bool sendViaPersonalChatEnabled property bool sendViaPersonalChatEnabled
property bool transactionDeepLinkEnabled
readonly property bool contentLocked: { readonly property bool contentLocked: {
if (!rootStore.chatCommunitySectionModule.isCommunity()) { if (!rootStore.chatCommunitySectionModule.isCommunity()) {
@ -244,6 +245,7 @@ StatusSectionLayout {
canPost: !root.rootStore.chatCommunitySectionModule.isCommunity() || root.canPost canPost: !root.rootStore.chatCommunitySectionModule.isCommunity() || root.canPost
amISectionAdmin: root.amISectionAdmin amISectionAdmin: root.amISectionAdmin
sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled
transactionDeepLinkEnabled: root.transactionDeepLinkEnabled
onOpenStickerPackPopup: { onOpenStickerPackPopup: {
Global.openPopup(statusStickerPackClickPopup, {packId: stickerPackId, store: root.stickersPopup.store} ) Global.openPopup(statusStickerPackClickPopup, {packId: stickerPackId, store: root.stickersPopup.store} )
} }

View File

@ -0,0 +1,40 @@
import QtQuick 2.15
import StatusQ.Controls 0.1
StatusButton {
id: root
text: qsTr("Share")
type: StatusBaseButton.Type.Normal
size: StatusBaseButton.Size.Tiny
horizontalPadding: 8
verticalPadding: 3
implicitHeight: 22
radius: 20
font.pixelSize: 12
Timer {
id: shareStateTimer
interval: 2000
repeat: false
}
states: State {
name: "success"
when: shareStateTimer.running
PropertyChanges {
target: shareButton
text: qsTr("Copied")
type: StatusBaseButton.Type.Success
icon.name: "tiny/checkmark"
tooltip.text: qsTr("Copied to clipboard")
}
}
onClicked: {
shareStateTimer.restart()
}
}

View File

@ -25,3 +25,4 @@ SwapProvidersTermsAndConditionsText 1.0 SwapProvidersTermsAndConditionsText.qml
TokenSelector 1.0 TokenSelector.qml TokenSelector 1.0 TokenSelector.qml
TokenSelectorButton 1.0 TokenSelectorButton.qml TokenSelectorButton 1.0 TokenSelectorButton.qml
TokenSelectorCompactButton 1.0 TokenSelectorCompactButton.qml TokenSelectorCompactButton 1.0 TokenSelectorCompactButton.qml
ShareButton 1.0 ShareButton.qml

View File

@ -1,9 +1,10 @@
import QtQml.Models 2.15
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import utils 1.0 import utils 1.0
import StatusQ 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Backpressure 0.1 import StatusQ.Core.Backpressure 0.1
@ -11,8 +12,8 @@ import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as SQUtils import StatusQ.Core.Utils 0.1 as SQUtils
import StatusQ.Popups.Dialog 0.1 import StatusQ.Popups.Dialog 0.1
import shared.popups.send.controls 1.0
import shared.controls 1.0 import shared.controls 1.0
import shared.popups.send.controls 1.0
import AppLayouts.Wallet.controls 1.0 import AppLayouts.Wallet.controls 1.0
import AppLayouts.Wallet.panels 1.0 import AppLayouts.Wallet.panels 1.0
@ -27,6 +28,8 @@ StatusDialog {
required property SwapModalAdaptor swapAdaptor required property SwapModalAdaptor swapAdaptor
required property int loginType required property int loginType
property bool transactionDeepLinkEnabled
objectName: "swapModal" objectName: "swapModal"
implicitWidth: 556 implicitWidth: 556
@ -146,6 +149,21 @@ StatusDialog {
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
text: qsTr("Swap") text: qsTr("Swap")
} }
ShareButton {
id: shareButton
visible: root.transactionDeepLinkEnabled
enabled: !!root.swapInputParamsForm.fromTokensKey || !!root.swapInputParamsForm.selectedAccountAddress || !!root.swapInputParamsForm.fromTokenAmount || !!root.swapInputParamsForm.toTokenKey
onClicked: {
const url = root.swapAdaptor.swapStore.getShareTransactionUrl(Constants.SendType.Swap,
root.swapInputParamsForm.fromTokensKey,
root.swapInputParamsForm.fromTokenAmount,
root.swapInputParamsForm.selectedAccountAddress,
root.swapInputParamsForm.selectedNetworkChainId,
root.swapInputParamsForm.toTokenKey)
ClipboardUtils.setText(url)
}
}
StatusBaseText { StatusBaseText {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
text: qsTr("On:") text: qsTr("On:")

View File

@ -52,4 +52,8 @@ QtObject {
function getWei2Eth(wei, decimals) { function getWei2Eth(wei, decimals) {
return globalUtils.wei2Eth(wei, decimals) return globalUtils.wei2Eth(wei, decimals)
} }
function getShareTransactionUrl(txType, asset, amount, address, chainId, toAsset) {
return walletSectionSendInst.shareTransactionURL(txType, asset, amount, address, chainId, toAsset)
}
} }

View File

@ -5,4 +5,5 @@ QtObject {
property bool dappsEnabled property bool dappsEnabled
property bool swapEnabled property bool swapEnabled
property bool sendViaPersonalChatEnabled property bool sendViaPersonalChatEnabled
property bool transactionDeepLinkEnabled
} }

View File

@ -45,6 +45,7 @@ import AppLayouts.Profile.stores 1.0 as ProfileStores
import AppLayouts.Wallet.popups 1.0 as WalletPopups import AppLayouts.Wallet.popups 1.0 as WalletPopups
import AppLayouts.Wallet.stores 1.0 as WalletStores import AppLayouts.Wallet.stores 1.0 as WalletStores
import AppLayouts.stores 1.0 as AppStores import AppLayouts.stores 1.0 as AppStores
import AppLayouts.Wallet.popups.swap 1.0 as WalletSwapPopups
import mainui.activitycenter.stores 1.0 import mainui.activitycenter.stores 1.0
import mainui.activitycenter.popups 1.0 import mainui.activitycenter.popups 1.0
@ -95,6 +96,7 @@ Item {
dappsEnabled: featureFlags ? featureFlags.dappsEnabled : false dappsEnabled: featureFlags ? featureFlags.dappsEnabled : false
swapEnabled: featureFlags ? featureFlags.swapEnabled : false swapEnabled: featureFlags ? featureFlags.swapEnabled : false
sendViaPersonalChatEnabled: featureFlags ? featureFlags.sendViaPersonalChatEnabled : false sendViaPersonalChatEnabled: featureFlags ? featureFlags.sendViaPersonalChatEnabled : false
transactionDeepLinkEnabled: featureFlags ? featureFlags.transactionDeepLinkEnabled : false
} }
required property bool isCentralizedMetricsEnabled required property bool isCentralizedMetricsEnabled
@ -388,11 +390,47 @@ Item {
"" ""
) )
} }
function onShowTransactionModal(txType, asset, amount, address, chainId, toAsset) {
if (txType === Constants.SendType.Swap) {
d.swapFormData.fromTokensKey = asset
d.swapFormData.toTokenKey = toAsset
d.swapFormData.fromTokenAmount = amount
d.swapFormData.selectedAccountAddress = address
d.swapFormData.selectedNetworkChainId = chainId
Global.openSwapModalRequested(d.swapFormData)
return
}
sendModal.preSelectedSendType = txType
sendModal.preDefinedAmountToSend = amount
sendModal.preSelectedHoldingID = asset
switch(txType) {
case Constants.SendType.ERC721Transfer:
sendModal.preSelectedHoldingType = Constants.TokenType.ERC721
break
case Constants.SendType.ERC1155Transfer:
sendModal.preSelectedHoldingType = Constants.TokenType.ERC1155
break
case Constants.SendType.Transfer:
case Constants.SendType.Bridge:
sendModal.preSelectedHoldingType = Constants.TokenType.ERC20
break
default:
console.error("Unsupported txType: %1 to open transaction modal").arg(txType)
return
}
sendModal.open(address)
}
} }
QtObject { QtObject {
id: d id: d
property WalletSwapPopups.SwapInputParamsForm swapFormData: WalletSwapPopups.SwapInputParamsForm {
selectedAccountAddress: WalletStores.RootStore.selectedAddress
}
property var activityCenterPopupObj: null property var activityCenterPopupObj: null
function openActivityCenterPopup() { function openActivityCenterPopup() {
@ -429,6 +467,7 @@ Item {
buyCryptoStore: appMain.buyCryptoStore buyCryptoStore: appMain.buyCryptoStore
networkConnectionStore: appMain.networkConnectionStore networkConnectionStore: appMain.networkConnectionStore
isDevBuild: !production isDevBuild: !production
transactionDeepLinkEnabled: appMain.featureFlagsStore.transactionDeepLinkEnabled
onOpenExternalLink: globalConns.onOpenLink(link) onOpenExternalLink: globalConns.onOpenLink(link)
onSaveDomainToUnfurledWhitelist: { onSaveDomainToUnfurledWhitelist: {
@ -1383,6 +1422,7 @@ Item {
emojiPopup: statusEmojiPopup.item emojiPopup: statusEmojiPopup.item
stickersPopup: statusStickersPopupLoader.item stickersPopup: statusStickersPopupLoader.item
sendViaPersonalChatEnabled: featureFlagsStore.sendViaPersonalChatEnabled && appMain.networkConnectionStore.sendBuyBridgeEnabled sendViaPersonalChatEnabled: featureFlagsStore.sendViaPersonalChatEnabled && appMain.networkConnectionStore.sendBuyBridgeEnabled
transactionDeepLinkEnabled: featureFlagsStore.transactionDeepLinkEnabled && appMain.networkConnectionStore.sendBuyBridgeEnabled
onProfileButtonClicked: { onProfileButtonClicked: {
Global.changeAppSectionBySectionType(Constants.appSection.profile); Global.changeAppSectionBySectionType(Constants.appSection.profile);
@ -1534,6 +1574,7 @@ Item {
transactionStore: appMain.transactionStore transactionStore: appMain.transactionStore
walletAssetsStore: appMain.walletAssetsStore walletAssetsStore: appMain.walletAssetsStore
currencyStore: appMain.currencyStore currencyStore: appMain.currencyStore
transactionDeepLinkEnabled: featureFlagsStore.transactionDeepLinkEnabled && appMain.networkConnectionStore.sendBuyBridgeEnabled
onProfileButtonClicked: { onProfileButtonClicked: {
Global.changeAppSectionBySectionType(Constants.appSection.profile); Global.changeAppSectionBySectionType(Constants.appSection.profile);
@ -1648,6 +1689,7 @@ Item {
collectiblesStore: appMain.walletCollectiblesStore collectiblesStore: appMain.walletCollectiblesStore
showCustomRoutingMode: !production showCustomRoutingMode: !production
transactionDeepLinkEnabled: featureFlagsStore.transactionDeepLinkEnabled
onClosed: { onClosed: {
sendModal.closed() sendModal.closed()

View File

@ -51,6 +51,7 @@ QtObject {
property NetworkConnectionStore networkConnectionStore property NetworkConnectionStore networkConnectionStore
property WalletStore.BuyCryptoStore buyCryptoStore property WalletStore.BuyCryptoStore buyCryptoStore
property bool isDevBuild property bool isDevBuild
property bool transactionDeepLinkEnabled
signal openExternalLink(string link) signal openExternalLink(string link)
signal saveDomainToUnfurledWhitelist(string domain) signal saveDomainToUnfurledWhitelist(string domain)
@ -1253,6 +1254,7 @@ QtObject {
swapFormData: swapInputParamsForm swapFormData: swapInputParamsForm
swapOutputData: SwapOutputData{} swapOutputData: SwapOutputData{}
} }
transactionDeepLinkEnabled: root.transactionDeepLinkEnabled
loginType: root.rootStore.loginType loginType: root.rootStore.loginType
onClosed: destroy() onClosed: destroy()
} }

View File

@ -64,6 +64,8 @@ StatusDialog {
property int loginType property int loginType
property bool showCustomRoutingMode property bool showCustomRoutingMode
property bool transactionDeepLinkEnabled
// In case selected address is incorrect take first account from the list // In case selected address is incorrect take first account from the list
readonly property alias selectedAccount: selectedSenderAccountEntry.item readonly property alias selectedAccount: selectedSenderAccountEntry.item
@ -469,6 +471,38 @@ StatusDialog {
amountToSend.forceActiveFocus() amountToSend.forceActiveFocus()
} }
} }
ShareButton {
id: shareButton
visible: {
if (!popup.transactionDeepLinkEnabled)
return false
switch (store.sendType) {
case Constants.SendType.Bridge:
case Constants.SendType.Transfer:
case Constants.SendType.ERC721Transfer:
case Constants.SendType.ERC1155Transfer:
return true
default:
return false
}
}
enabled: d.isSelectedHoldingValidAsset || (!d.isCollectiblesTransfer && amountToSend.ready) || recipientInputLoader.ready
onClicked: {
let asset = ""
if (!!d.selectedHolding) {
asset = d.isCollectiblesTransfer ? d.selectedHolding.symbol : d.selectedHolding.tokensKey
}
let recipient = ""
if (recipientInputLoader.ready) {
recipient = popup.store.selectedSenderAccountAddress
}
const url = popup.store.getShareTransactionUrl(store.sendType, asset, amountToSend.asNumber, recipient, 0)
ClipboardUtils.setText(url)
}
}
} }
RowLayout { RowLayout {
visible: d.isSelectedHoldingValidAsset && !d.isCollectiblesTransfer visible: d.isSelectedHoldingValidAsset && !d.isCollectiblesTransfer

View File

@ -157,6 +157,14 @@ QtObject {
} }
} }
function getShareTransactionUrl(txType, asset, amount, address, chainId) {
return walletSectionSendInst.shareTransactionURL(txType, asset, amount, address, chainId, "")
}
function getShortChainIds(chainShortNames) {
return walletSectionSendInst.getShortChainIds(chainShortNames)
}
function formatCurrencyAmountFromBigInt(balance, symbol, decimals, options = null) { function formatCurrencyAmountFromBigInt(balance, symbol, decimals, options = null) {
return currencyStore.formatCurrencyAmountFromBigInt(balance, symbol, decimals, options) return currencyStore.formatCurrencyAmountFromBigInt(balance, symbol, decimals, options)
} }

View File

@ -135,6 +135,7 @@ Loader {
property bool hasMention: false property bool hasMention: false
property bool sendViaPersonalChatEnabled property bool sendViaPersonalChatEnabled
property bool transactionDeepLinkEnabled
property bool stickersLoaded: false property bool stickersLoaded: false
property string sticker property string sticker
@ -759,7 +760,10 @@ Loader {
const linkPreviewType = root.linkPreviewModel.getLinkPreviewType(link) const linkPreviewType = root.linkPreviewModel.getLinkPreviewType(link)
if (linkPreviewType === Constants.LinkPreviewType.Standard || !Utils.isStatusDeepLink(link)) { if (linkPreviewType === Constants.LinkPreviewType.Standard
|| !Utils.isStatusDeepLink(link)
|| (!root.transactionDeepLinkEnabled && Utils.isStatusTransactionDeepLink(link)))
{
Global.openLink(link) Global.openLink(link)
return return
} }

View File

@ -293,6 +293,10 @@ QtObject {
return link.includes(Constants.deepLinkPrefix) || link.includes(Constants.externalStatusLink) return link.includes(Constants.deepLinkPrefix) || link.includes(Constants.externalStatusLink)
} }
function isStatusTransactionDeepLink(link) {
return isStatusDeepLink(link) && link.indexOf("/tx/") > -1
}
function removeGifUrls(message) { function removeGifUrls(message) {
return message.replace(/(?:https?|ftp):\/\/[\n\S]*(\.gif)+/gm, ''); return message.replace(/(?:https?|ftp):\/\/[\n\S]*(\.gif)+/gm, '');
} }

@ -1 +1 @@
Subproject commit 0141cab5ef0ec2f21ad0b04413936d08cc6e8265 Subproject commit 984560cfb0b1acd25dbc1055b5de7f6b6fceb548