From 9ca7167f713694d41f1aba0ef3fb539de7f7a56c Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Wed, 20 Nov 2024 08:34:05 +0100 Subject: [PATCH] feat(wallet): in app tx related notifications improvements Closes #16338 --- src/app/modules/main/module.nim | 206 +++++++--- src/app/modules/main/view.nim | 27 +- .../main/wallet_section/send/controller.nim | 14 +- src/app_service/common/wallet_constants.nim | 2 + src/app_service/service/ens/service.nim | 47 ++- src/app_service/service/stickers/service.nim | 24 +- .../transaction/router_transactions_dto.nim | 43 +- .../service/transaction/service.nim | 102 ++--- ui/app/AppLayouts/Profile/views/EnsView.qml | 44 --- ui/app/mainui/AppMain.qml | 372 +++++++++++++----- ui/imports/utils/Constants.qml | 7 + 11 files changed, 563 insertions(+), 325 deletions(-) diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 0438986242..be02092658 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -2,14 +2,14 @@ import NimQml, tables, json, sequtils, stew/shims/strformat, marshal, times, chr import io_interface, view, controller, chat_search_item, chat_search_model import ephemeral_notification_item, ephemeral_notification_model -import ../shared_models/[user_item, member_item, member_model, section_item, section_model, section_details] -import ../shared_models/[color_hash_item, color_hash_model] -import ../shared_modules/keycard_popup/module as keycard_shared_module -import ../../global/app_sections_config as conf -import ../../global/app_signals -import ../../global/global_singleton -import ../../global/utils as utils -import ../../../constants +import app/modules/shared_models/[user_item, member_item, member_model, section_item, section_model, section_details] +import app/modules/shared_models/[color_hash_item, color_hash_model] +import app/modules/shared_modules/keycard_popup/module as keycard_shared_module +import app/global/app_sections_config as conf +import app/global/app_signals +import app/global/global_singleton +import app/global/utils as utils +import constants import chat_section/model as chat_model import chat_section/item as chat_item @@ -27,51 +27,51 @@ import communities/tokens/models/token_model import network_connection/module as network_connection_module import shared_urls/module as shared_urls_module -import ../../../app_service/service/contacts/dto/contacts -import ../../../app_service/service/community_tokens/community_collectible_owner +import app_service/service/contacts/dto/contacts +import app_service/service/community_tokens/community_collectible_owner -import ../../../app_service/service/keychain/service as keychain_service -import ../../../app_service/service/chat/service as chat_service -import ../../../app_service/service/community/service as community_service -import ../../../app_service/service/message/service as message_service -import ../../../app_service/service/token/service as token_service -import ../../../app_service/service/collectible/service as collectible_service -import ../../../app_service/service/currency/service as currency_service -import ../../../app_service/service/ramp/service as ramp_service -import ../../../app_service/service/transaction/service as transaction_service -import ../../../app_service/service/wallet_account/service as wallet_account_service -import ../../../app_service/service/provider/service as provider_service -import ../../../app_service/service/profile/service as profile_service -import ../../../app_service/service/accounts/service as accounts_service -import ../../../app_service/service/accounts/utils as accounts_utils -import ../../../app_service/service/settings/service as settings_service -import ../../../app_service/service/contacts/service as contacts_service -import ../../../app_service/service/about/service as about_service -import ../../../app_service/service/language/service as language_service -import ../../../app_service/service/privacy/service as privacy_service -import ../../../app_service/service/stickers/service as stickers_service -import ../../../app_service/service/activity_center/service as activity_center_service -import ../../../app_service/service/saved_address/service as saved_address_service -import ../../../app_service/service/node/service as node_service -import ../../../app_service/service/node_configuration/service as node_configuration_service -import ../../../app_service/service/devices/service as devices_service -import ../../../app_service/service/mailservers/service as mailservers_service -import ../../../app_service/service/gif/service as gif_service -import ../../../app_service/service/ens/service as ens_service -import ../../../app_service/service/community_tokens/service as community_tokens_service -import ../../../app_service/service/network/service as network_service -import ../../../app_service/service/general/service as general_service -import ../../../app_service/service/keycard/service as keycard_service -import ../../../app_service/service/shared_urls/service as urls_service -import ../../../app_service/service/network_connection/service as network_connection_service -import ../../../app_service/service/visual_identity/service as procs_from_visual_identity_service -import ../../../app_service/common/types -import ../../../app_service/common/utils as common_utils +import app_service/service/keychain/service as keychain_service +import app_service/service/chat/service as chat_service +import app_service/service/community/service as community_service +import app_service/service/message/service as message_service +import app_service/service/token/service as token_service +import app_service/service/collectible/service as collectible_service +import app_service/service/currency/service as currency_service +import app_service/service/ramp/service as ramp_service +import app_service/service/transaction/service as transaction_service +import app_service/service/wallet_account/service as wallet_account_service +import app_service/service/provider/service as provider_service +import app_service/service/profile/service as profile_service +import app_service/service/accounts/service as accounts_service +import app_service/service/accounts/utils as accounts_utils +import app_service/service/settings/service as settings_service +import app_service/service/contacts/service as contacts_service +import app_service/service/about/service as about_service +import app_service/service/language/service as language_service +import app_service/service/privacy/service as privacy_service +import app_service/service/stickers/service as stickers_service +import app_service/service/activity_center/service as activity_center_service +import app_service/service/saved_address/service as saved_address_service +import app_service/service/node/service as node_service +import app_service/service/node_configuration/service as node_configuration_service +import app_service/service/devices/service as devices_service +import app_service/service/mailservers/service as mailservers_service +import app_service/service/gif/service as gif_service +import app_service/service/ens/service as ens_service +import app_service/service/community_tokens/service as community_tokens_service +import app_service/service/network/service as network_service +import app_service/service/general/service as general_service +import app_service/service/keycard/service as keycard_service +import app_service/service/shared_urls/service as urls_service +import app_service/service/network_connection/service as network_connection_service +import app_service/service/visual_identity/service as procs_from_visual_identity_service +import app_service/common/types +import app_service/common/utils as common_utils import app_service/service/network/network_item -import ../../core/notifications/details -import ../../core/eventemitter -import ../../core/custom_urls/urls_manager +import app/core/notifications/details +import app/core/eventemitter +import app/core/custom_urls/urls_manager export io_interface @@ -101,6 +101,7 @@ type walletAccountService: wallet_account_service.Service keychainService: keychain_service.Service networkConnectionService: network_connection_service.Service + stickersService: stickers_service.Service walletSectionModule: wallet_section_module.AccessInterface profileSectionModule: profile_section_module.AccessInterface stickersModule: stickers_module.AccessInterface @@ -207,6 +208,7 @@ proc newModule*[T]( result.accountsService = accountsService result.walletAccountService = walletAccountService result.keychainService = keychainService + result.stickersService = stickersService # Submodules result.chatSectionModules = initOrderedTable[string, chat_section_module.AccessInterface]() @@ -334,7 +336,7 @@ proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityD # If there are tokens already in the model, we should keep the existing community tokens, until # getCommunityTokensDetailsAsync will trigger onCommunityTokensDetailsLoaded if not existingCommunity.isEmpty() and not existingCommunity.communityTokens.isNil: - communityTokensItems = existingCommunity.communityTokens.items + communityTokensItems = existingCommunity.communityTokens.items let (unviewedCount, notificationsCount) = self.controller.sectionUnreadMessagesAndMentionsCount( communityDetails.id, @@ -436,6 +438,86 @@ proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityD activeMembersCount = int(communityDetails.activeMembersCount), ) +proc sendNotification[T](self: Module[T], status: string, sendDetails: SendDetailsDto, sentTransaction: RouterSentTransaction) = + var + addressFrom = sendDetails.fromAddress + addressTo = sendDetails.toAddress + txTo = "" # txFrom is always the same as addressFrom, but txTo is different from addressTo when the app performs any sending flow via certain contract + fromChain = sendDetails.fromChain + toChain = sendDetails.toChain + fromAmount = sendDetails.fromAmount.toString(10) + toAmount = sendDetails.toAmount.toString(10) + fromAsset = sendDetails.fromToken + toAsset = sendDetails.toToken + error = "" + + if not sendDetails.errorResponse.isNil: + error = sendDetails.errorResponse.details + + if sentTransaction.hash.len > 0: + txTo = sentTransaction.toAddress + if sentTransaction.fromChain > 0: + fromChain = sentTransaction.fromChain + if sentTransaction.toChain > 0: + toChain = sentTransaction.toChain + let + txAmountIn = sentTransaction.amountIn.toString(10) + txAmountOut = sentTransaction.amountOut.toString(10) + if txAmountIn != "0": + fromAmount = txAmountIn + if txAmountOut != "0": + toAmount = txAmountOut + if sentTransaction.fromToken.len > 0: + fromAsset = sentTransaction.fromToken + if sentTransaction.toToken.len > 0: + toAsset = sentTransaction.toToken + + + var accFromName = "" + var accDto = self.walletAccountService.getAccountByAddress(addressFrom) + if not accDto.isNil: + accFromName = accDto.name + + var accToName = "" + accDto = self.walletAccountService.getAccountByAddress(addressTo) + if not accDto.isNil: + accToName = accDto.name + + var txToName = "" + let txType = SendType(sendDetails.sendType) + if txType == SendType.Bridge: + txToName = "Hop" # no translations, currently we hardcode providers here, when we add more, this info should come from the status-go side. + elif txType == SendType.Swap: + txToName = "ParaSwap" # no translations, currently we hardcode providers here, when we add more, this info should come from the status-go side. + else: + accDto = self.walletAccountService.getAccountByAddress(txTo) + if not accDto.isNil: + txToName = accDto.name + + self.view.showTransactionToast( + sendDetails.uuid, + sendDetails.sendType, + fromChain, + toChain, + addressFrom, + accFromName, + addressTo, + accToName, + txTo, + txToName, + sentTransaction.hash, + sentTransaction.approvalTx, + fromAmount, + toAmount, + fromAsset, + toAsset, + sendDetails.username, + sendDetails.publicKey, + sendDetails.packId, + status, + error, + ) + proc connectForNotificationsOnly[T](self: Module[T]) = self.events.on(SIGNAL_WALLET_ACCOUNT_SAVED) do(e:Args): let args = AccountArgs(e) @@ -464,22 +546,26 @@ proc connectForNotificationsOnly[T](self: Module[T]) = kpName = args.keypairs[0].name self.view.showToastKeypairsImported(kpName, args.keypairs.len, args.error) + self.events.on(SIGNAL_SENDING_TRANSACTIONS_STARTED) do(e:Args): + let args = TransactionArgs(e) + # we allow this notification only if an error occurs, otherwise skip it and display notification after the tx gets submitted to the network + if args.sendDetails.errorResponse.isNil: + return + self.sendNotification(args.status, args.sendDetails, args.sentTransaction) + self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args): - let args = TransactionSentArgs(e) - self.view.showToastTransactionSent(args.chainId, args.txHash, args.uuid, args.error, - ord(args.txType), args.fromAddress, args.toAddress, args.fromTokenKey, args.fromAmount, - args.toTokenKey, args.toAmount) + let args = TransactionArgs(e) + self.sendNotification(args.status, args.sendDetails, args.sentTransaction) + + self.events.on(SIGNAL_TRANSACTION_STATUS_CHANGED) do(e:Args): + let args = TransactionArgs(e) + self.sendNotification(args.status, args.sendDetails, args.sentTransaction) self.events.on(MARK_WALLET_ADDRESSES_AS_SHOWN) do(e:Args): let args = WalletAddressesArgs(e) for address in args.addresses: self.addressWasShown(address) - self.events.on(SIGNAL_TRANSACTION_SENDING_COMPLETE) do(e:Args): - let args = TransactionMinedArgs(e) - self.view.showToastTransactionSendingComplete(args.chainId, args.transactionHash, args.data, args.success, - ord(args.txType), args.fromAddress, args.toAddress, args.fromTokenKey, args.fromAmount, args.toTokenKey, args.toAmount) - self.events.on(SIGNAL_PAIRING_FALLBACK_COMPLETED) do(e:Args): self.view.showToastPairingFallbackCompleted() diff --git a/src/app/modules/main/view.nim b/src/app/modules/main/view.nim index fc1b9d19b8..f90bf1d103 100644 --- a/src/app/modules/main/view.nim +++ b/src/app/modules/main/view.nim @@ -339,12 +339,31 @@ QtObject: proc showNetworkEndpointUpdated*(self: View, name: string, isTest: bool, revertedToDefault: bool) {.signal.} proc showToastKeypairRemoved*(self: View, keypairName: string) {.signal.} proc showToastKeypairsImported*(self: View, keypairName: string, keypairsCount: int, error: string) {.signal.} - proc showToastTransactionSent*(self: View, chainId: int, txHash: string, uuid: string, error: string, - txType: int, fromAddr: string, toAddr: string, fromTokenKey: string, fromAmount: string, toTokenKey: string, toAmount: string) {.signal.} - proc showToastTransactionSendingComplete*(self: View, chainId: int, txHash: string, data: string, success: bool, - txType: int, fromAddr: string, toAddr: string, fromTokenKey: string, fromAmount: string, toTokenKey: string, toAmount: string) {.signal.} proc showToastPairingFallbackCompleted*(self: View) {.signal.} + proc showTransactionToast*(self: View, + uuid: string, + txType: int, + fromChainId: int, + toChainId: int, + fromAddr: string, + fromName: string, + toAddr: string, + toName: string, + txToAddr: string, + txToName: string, + txHash: string, + approvalTx: bool, + fromAmount: string, + toAmount: string, + fromAsset: string, + toAsset: string, + username: string, + publicKey: string, + packId: string, + status: string, + error: string) {.signal.} + ## Used in test env only, for testing keycard flows proc registerMockedKeycard*(self: View, cardIndex: int, readerState: int, keycardState: int, mockedKeycard: string, mockedKeycardHelper: string) {.slot.} = diff --git a/src/app/modules/main/wallet_section/send/controller.nim b/src/app/modules/main/wallet_section/send/controller.nim index a39345e093..9a08b1b1d6 100644 --- a/src/app/modules/main/wallet_section/send/controller.nim +++ b/src/app/modules/main/wallet_section/send/controller.nim @@ -54,8 +54,14 @@ proc delete*(self: Controller) = proc init*(self: Controller) = self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args): - let args = TransactionSentArgs(e) - self.delegate.transactionWasSent(args.uuid, args.chainId, args.approvalTx, args.txHash, args.error) + let args = TransactionArgs(e) + self.delegate.transactionWasSent( + args.sendDetails.uuid, + args.sendDetails.fromChain, + args.sentTransaction.approvalTx, + args.sentTransaction.hash, + if not args.sendDetails.errorResponse.isNil: args.sendDetails.errorResponse.details else: "" + ) self.events.on(SIGNAL_OWNER_TOKEN_SENT) do(e:Args): let args = OwnerTokenSentArgs(e) @@ -76,8 +82,8 @@ proc init*(self: Controller) = self.delegate.prepareSignaturesForTransactions(data.data) self.events.on(SIGNAL_TRANSACTION_STATUS_CHANGED) do(e:Args): - let args = TransactionStatusArgs(e) - self.delegate.transactionSendingComplete(args.data.hash, args.data.status) + let args = TransactionArgs(e) + self.delegate.transactionSendingComplete(args.sentTransaction.hash, args.status) proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] = return self.walletAccountService.getWalletAccounts() diff --git a/src/app_service/common/wallet_constants.nim b/src/app_service/common/wallet_constants.nim index 1b9d0c3304..9f8f8c84bb 100644 --- a/src/app_service/common/wallet_constants.nim +++ b/src/app_service/common/wallet_constants.nim @@ -1,4 +1,6 @@ const + ETH_SYMBOL* = "ETH" + ETH_TRANSACTION_TYPE* = "eth" ERC20_TRANSACTION_TYPE* = "erc20" ERC721_TRANSACTION_TYPE* = "erc721" diff --git a/src/app_service/service/ens/service.nim b/src/app_service/service/ens/service.nim index 3b24c24860..826d804c49 100644 --- a/src/app_service/service/ens/service.nim +++ b/src/app_service/service/ens/service.nim @@ -146,48 +146,55 @@ QtObject: proc doConnect(self: Service) = self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args): - let args = TransactionSentArgs(e) - if args.txType != SendType.ENSRegister and args.txType != SendType.ENSSetPubKey and args.txType != SendType.ENSRelease: + let args = TransactionArgs(e) + let txType = SendType(args.sendDetails.sendType) + if txType != SendType.ENSRegister and txType != SendType.ENSSetPubKey and txType != SendType.ENSRelease: return - var err = args.error + var err = if not args.sendDetails.errorResponse.isNil: args.sendDetails.errorResponse.details else: "" var dto = EnsUsernameDto( - chainId: args.chainId, - username: args.username, - txHash: args.txHash, - txStatus: TxStatusPending + chainId: args.sendDetails.fromChain, + username: args.sendDetails.username, + txHash: args.sentTransaction.hash, + txStatus: args.status ) - if args.txType == SendType.ENSRegister: + if txType == SendType.ENSRegister: dto.txType = RegisterENS if err.len == 0: - let ensUsernameFinal = self.formatUsername(args.username, true) - if not self.add(args.chainId, ensUsernameFinal): + let ensUsernameFinal = self.formatUsername(args.sendDetails.username, true) + if not self.add(args.sendDetails.fromChain, ensUsernameFinal): err = "failed to add ens username" error "error", err - elif args.txType == SendType.ENSSetPubKey: + elif txType == SendType.ENSSetPubKey: dto.txType = SetPubKey if err.len == 0: - let usernameWithDomain = args.username.addDomain() - if not self.add(args.chainId, usernameWithDomain): + let usernameWithDomain = args.sendDetails.username.addDomain() + if not self.add(args.sendDetails.fromChain, usernameWithDomain): err = "failed to set ens username" error "error", err - elif args.txType == SendType.ENSRelease: + elif txType == SendType.ENSRelease: dto.txType = ReleaseENS if err.len == 0: - let ensUsernameFinal = self.formatUsername(args.username, true) - if not self.remove(args.chainId, ensUsernameFinal): + let ensUsernameFinal = self.formatUsername(args.sendDetails.username, true) + if not self.remove(args.sendDetails.fromChain, ensUsernameFinal): err = "failed to remove ens username" error "error", err - self.pendingEnsUsernames[makeKey(dto.username, args.chainId)] = dto + self.pendingEnsUsernames[makeKey(dto.username, args.sendDetails.fromChain)] = dto - let data = EnsTxResultArgs(transactionType: $dto.txType, chainId: args.chainId, ensUsername: args.username, txHash: args.txHash, error: err) + let data = EnsTxResultArgs( + transactionType: $dto.txType, + chainId: args.sendDetails.fromChain, + ensUsername: args.sendDetails.username, + txHash: args.sentTransaction.hash, + error: err + ) self.events.emit(SIGNAL_ENS_TRANSACTION_SENT, data) self.events.on(SIGNAL_TRANSACTION_STATUS_CHANGED) do(e:Args): - let args = TransactionStatusArgs(e) - self.updateEnsUsernames(args.data.chainId, args.data.hash, args.data.status) + let args = TransactionArgs(e) + self.updateEnsUsernames(args.sentTransaction.fromChain, args.sentTransaction.hash, args.status) proc init*(self: Service) = self.doConnect() diff --git a/src/app_service/service/stickers/service.nim b/src/app_service/service/stickers/service.nim index 472cb871f7..f3e6799d5a 100644 --- a/src/app_service/service/stickers/service.nim +++ b/src/app_service/service/stickers/service.nim @@ -153,17 +153,23 @@ QtObject: proc init*(self: Service) = self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args): - let args = TransactionSentArgs(e) - if args.txType != SendType.StickersBuy: + let args = TransactionArgs(e) + let txType = SendType(args.sendDetails.sendType) + if txType != SendType.StickersBuy: return - self.marketStickerPacks[$args.packId].status = StickerPackStatus.Pending - self.marketStickerPacks[$args.packId].txHash = args.txHash - let data = StickerBuyResultArgs(chainId: args.chainId, packId: args.packId, txHash: args.txHash, error: args.error) + self.marketStickerPacks[$args.sendDetails.packId].status = StickerPackStatus.Pending + self.marketStickerPacks[$args.sendDetails.packId].txHash = args.sentTransaction.hash + let data = StickerBuyResultArgs( + chainId: args.sendDetails.fromChain, + packId: args.sendDetails.packId, + txHash: args.sentTransaction.hash, + error: if not args.sendDetails.errorResponse.isNil: args.sendDetails.errorResponse.details else: "" + ) self.events.emit(SIGNAL_STICKER_TRANSACTION_SENT, data) self.events.on(SIGNAL_TRANSACTION_STATUS_CHANGED) do(e:Args): - let args = TransactionStatusArgs(e) - self.updateStickersPack(args.data.hash, args.data.status) + let args = TransactionArgs(e) + self.updateStickersPack(args.sentTransaction.hash, args.status) proc setMarketStickerPacks*(self: Service, strickersJSON: string) {.slot.} = let stickersResult = Json.decode(strickersJSON, tuple[packs: seq[StickerPackDto], error: string]) @@ -222,6 +228,10 @@ QtObject: return packId return "0" + proc getStickersByPackId*(self: Service, packId: string): StickerPackDto = + if self.marketStickerPacks.hasKey(packId): + return self.marketStickerPacks[packId] + proc getRecentStickers*(self: Service): seq[StickerDto] = try: let recentResponse = status_stickers.recent() diff --git a/src/app_service/service/transaction/router_transactions_dto.nim b/src/app_service/service/transaction/router_transactions_dto.nim index 75cdf45ce0..b324545619 100644 --- a/src/app_service/service/transaction/router_transactions_dto.nim +++ b/src/app_service/service/transaction/router_transactions_dto.nim @@ -3,16 +3,11 @@ import json, stint include ../../common/json_utils const + TxStatusSending* = "Sending" TxStatusPending* = "Pending" TxStatusSuccess* = "Success" TxStatusFailed* = "Failed" -type - TransactionStatusChange* = ref object - status*: string - hash*: string - chainId*: int - type ErrorResponse* = ref object details*: string @@ -21,6 +16,8 @@ type SendDetailsDto* = ref object uuid*: string sendType*: int + fromChain*: int + toChain*: int fromAddress*: string toAddress*: string fromToken*: string @@ -54,7 +51,9 @@ type toChain*: int fromToken*: string toToken*: string - amount*: UInt256 # amount of the transaction + amount*: UInt256 # amount sent + amountIn*: UInt256 # amount that is "data" of tx (important for erc20 tokens) + amountOut*: UInt256 # amount that will be received hash*: string approvalTx*: bool @@ -63,11 +62,13 @@ type sendDetails*: SendDetailsDto sentTransactions*: seq[RouterSentTransaction] -proc toTransactionStatusChange*(jsonObj: JsonNode): TransactionStatusChange = - result = TransactionStatusChange() - discard jsonObj.getProp("status", result.status) - discard jsonObj.getProp("hash", result.hash) - discard jsonObj.getProp("chainId", result.chainId) +type + TransactionStatusChange* = ref object + status*: string + hash*: string + chainId*: int + sendDetails*: SendDetailsDto + sentTransactions*: seq[RouterSentTransaction] proc toErrorResponse*(jsonObj: JsonNode): ErrorResponse = result = ErrorResponse() @@ -80,6 +81,8 @@ proc toSendDetailsDto*(jsonObj: JsonNode): SendDetailsDto = result = SendDetailsDto() discard jsonObj.getProp("uuid", result.uuid) discard jsonObj.getProp("sendType", result.sendType) + discard jsonObj.getProp("fromChain", result.fromChain) + discard jsonObj.getProp("toChain", result.toChain) discard jsonObj.getProp("fromAddress", result.fromAddress) discard jsonObj.getProp("toAddress", result.toAddress) discard jsonObj.getProp("fromToken", result.fromToken) @@ -130,10 +133,26 @@ proc toRouterSentTransaction*(jsonObj: JsonNode): RouterSentTransaction = var tmpObj: JsonNode if jsonObj.getProp("amount", tmpObj): result.amount = stint.fromHex(UInt256, tmpObj.getStr) + if jsonObj.getProp("amountIn", tmpObj): + result.amountIn = stint.fromHex(UInt256, tmpObj.getStr) + if jsonObj.getProp("amountOut", tmpObj): + result.amountOut = stint.fromHex(UInt256, tmpObj.getStr) proc toRouterSentTransactionsDto*(jsonObj: JsonNode): RouterSentTransactionsDto = result = RouterSentTransactionsDto() var tmpObj: JsonNode + if jsonObj.getProp("sendDetails", tmpObj) and tmpObj.kind == JObject: + result.sendDetails = toSendDetailsDto(tmpObj) + if jsonObj.getProp("sentTransactions", tmpObj) and tmpObj.kind == JArray: + for tx in tmpObj: + result.sentTransactions.add(toRouterSentTransaction(tx)) + +proc toTransactionStatusChange*(jsonObj: JsonNode): TransactionStatusChange = + result = TransactionStatusChange() + discard jsonObj.getProp("status", result.status) + discard jsonObj.getProp("hash", result.hash) + discard jsonObj.getProp("chainId", result.chainId) + var tmpObj: JsonNode if jsonObj.getProp("sendDetails", tmpObj) and tmpObj.kind == JObject: result.sendDetails = toSendDetailsDto(tmpObj) if jsonObj.getProp("sentTransactions", tmpObj) and tmpObj.kind == JArray: diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim index b15b403e98..a05ed2a2f2 100644 --- a/src/app_service/service/transaction/service.nim +++ b/src/app_service/service/transaction/service.nim @@ -44,7 +44,6 @@ const SIGNAL_HISTORY_NON_ARCHIVAL_NODE* = "historyNonArchivalNode" const SIGNAL_HISTORY_ERROR* = "historyError" const SIGNAL_TRANSACTION_DECODED* = "transactionDecoded" const SIGNAL_OWNER_TOKEN_SENT* = "ownerTokenSent" -const SIGNAL_TRANSACTION_SENDING_COMPLETE* = "transactionSendingComplete" const SIGNAL_TRANSACTION_STATUS_CHANGED* = "transactionStatusChanged" const InternalErrorCode = -1 @@ -104,24 +103,6 @@ proc `$`*(self: TransactionMinedArgs): string = except ValueError: raiseAssert "static fmt" -type - TransactionSentArgs* = ref object of Args - chainId*: int - txHash*: string - uuid*: string - error*: string - txType*: SendType - fromAddress*: string - toAddress*: string - fromTokenKey*: string - fromAmount*: string - toTokenKey*: string - toAmount*: string - approvalTx*: bool - username*: string - publicKey*: string - packId*: string - type OwnerTokenSentArgs* = ref object of Args chainId*: int @@ -147,12 +128,10 @@ type data*: RouterTransactionsForSigningDto type - RouterTransactionsSendingStartedArgs* = ref object of Args - data*: SendDetailsDto - -type - TransactionStatusArgs* = ref object of Args - data*: TransactionStatusChange + TransactionArgs* = ref object of Args + status*: string + sendDetails*: SendDetailsDto + sentTransaction*: RouterSentTransaction QtObject: type Service* = ref object of QObject @@ -201,9 +180,10 @@ QtObject: self.events.on(SignalType.WalletRouterSendingTransactionsStarted.event) do(e:Args): var data = WalletSignal(e) - self.events.emit(SIGNAL_SENDING_TRANSACTIONS_STARTED, RouterTransactionsSendingStartedArgs(data: data.routerTransactionsSendingDetails)) - # TODO: the line below is to aling with the old implementation, remove it - self.sendTransactionsSignal(data.routerTransactionsSendingDetails) + self.events.emit(SIGNAL_SENDING_TRANSACTIONS_STARTED, TransactionArgs( + status: TxStatusSending, + sendDetails: data.routerTransactionsSendingDetails + )) self.events.on(SignalType.WalletRouterSignTransactions.event) do(e:Args): var data = WalletSignal(e) @@ -219,18 +199,26 @@ QtObject: self.events.on(SignalType.WalletTransactionStatusChanged.event) do(e:Args): var data = WalletSignal(e) - self.events.emit(SIGNAL_TRANSACTION_STATUS_CHANGED, TransactionStatusArgs(data: data.transactionStatusChange)) + if data.transactionStatusChange.isNil: + return + for tx in data.transactionStatusChange.sentTransactions: + self.events.emit(SIGNAL_TRANSACTION_STATUS_CHANGED, TransactionArgs( + status: data.transactionStatusChange.status, + sendDetails: data.transactionStatusChange.sendDetails, + sentTransaction: tx + )) - self.events.on(PendingTransactionTypeDto.WalletTransfer.event) do(e: Args): - try: - var receivedData = TransactionMinedArgs(e) - let tokenMetadata = receivedData.data.parseJson().toTokenTransferMetadata() - if tokenMetadata.isOwnerToken: - let status = if receivedData.success: ContractTransactionStatus.Completed else: ContractTransactionStatus.Failed - self.events.emit(SIGNAL_OWNER_TOKEN_SENT, OwnerTokenSentArgs(chainId: receivedData.chainId, txHash: receivedData.transactionHash, tokenName: tokenMetadata.tokenName, status: status)) - self.events.emit(SIGNAL_TRANSACTION_SENDING_COMPLETE, receivedData) - except Exception as e: - debug "Not the owner token transfer", msg=e.msg + # TODO: handle this for community related tx (minting, airdropping...) + # self.events.on(PendingTransactionTypeDto.WalletTransfer.event) do(e: Args): + # try: + # var receivedData = TransactionMinedArgs(e) + # let tokenMetadata = receivedData.data.parseJson().toTokenTransferMetadata() + # if tokenMetadata.isOwnerToken: + # let status = if receivedData.success: ContractTransactionStatus.Completed else: ContractTransactionStatus.Failed + # self.events.emit(SIGNAL_OWNER_TOKEN_SENT, OwnerTokenSentArgs(chainId: receivedData.chainId, txHash: receivedData.transactionHash, tokenName: tokenMetadata.tokenName, status: status)) + # self.events.emit(SIGNAL_TRANSACTION_SENDING_COMPLETE, receivedData) + # except Exception as e: + # debug "Not the owner token transfer", msg=e.msg proc getPendingTransactions*(self: Service): seq[TransactionDto] = try: @@ -330,21 +318,8 @@ QtObject: # that's why we need to mark the addresses as shown here (safer). self.events.emit(MARK_WALLET_ADDRESSES_AS_SHOWN, WalletAddressesArgs(addresses: @[sendDetails.fromAddress, sendDetails.toAddress])) - if not sendDetails.errorResponse.isNil and sendDetails.errorResponse.details.len > 0: - self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs( - uuid: sendDetails.uuid, - error: sendDetails.errorResponse.details, - txType: SendType(sendDetails.sendType), - fromAddress: sendDetails.fromAddress, - toAddress: sendDetails.toAddress, - fromTokenKey: sendDetails.fromToken, - fromAmount: sendDetails.fromAmount.toString(10), - toTokenKey: sendDetails.toToken, - toAmount: sendDetails.toAmount.toString(10), - username: sendDetails.username, - publicKey: sendDetails.publicKey, - packId: sendDetails.packId - )) + if sentTransactions.len == 0: + self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionArgs(sendDetails: sendDetails)) return for tx in sentTransactions: @@ -357,21 +332,10 @@ QtObject: status: ContractTransactionStatus.InProgress )) else: - self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs( - chainId: tx.fromChain, - txHash: tx.hash, - uuid: sendDetails.uuid, - txType: SendType(sendDetails.sendType), - fromAddress: tx.fromAddress, - toAddress: tx.toAddress, - fromTokenKey: tx.fromToken, - fromAmount: tx.amount.toString(10), - toTokenKey: tx.toToken, - toAmount: tx.amount.toString(10), - approvalTx: tx.approvalTx, - username: sendDetails.username, - publicKey: sendDetails.publicKey, - packId: sendDetails.packId + self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionArgs( + status: TxStatusSending, # here should be TxStatusPending state, but that's not what Figma wants + sendDetails: sendDetails, + sentTransaction: tx )) proc suggestedFees*(self: Service, chainId: int): SuggestedFeesDto = diff --git a/ui/app/AppLayouts/Profile/views/EnsView.qml b/ui/app/AppLayouts/Profile/views/EnsView.qml index 285716f3d7..3f54438e2b 100644 --- a/ui/app/AppLayouts/Profile/views/EnsView.qml +++ b/ui/app/AppLayouts/Profile/views/EnsView.qml @@ -435,49 +435,5 @@ Item { } } - Connections { - target: ensView.ensUsernamesStore.ensUsernamesModule - function onTransactionCompleted(success: bool, txHash: string, username: string, trxType: string) { - let title = "" - switch(trxType){ - case d.registerENS: - title = !success ? - qsTr("ENS Registration failed") - : - qsTr("ENS Registration completed"); - break; - case d.setPubKey: - title = !success ? - qsTr("Updating ENS pubkey failed") - : - qsTr("Updating ENS pubkey completed"); - break; - case d.releaseENS: - title = !success ? - qsTr("Releasing ENS name failed") - : - qsTr("ENS name released"); - break - default: - console.error("unknown transaction type: ", trxType); - return - } - - let icon = "block-icon"; - let ephType = Constants.ephemeralNotificationType.normal; - if (success) { - icon = "check-circle"; - ephType = Constants.ephemeralNotificationType.success; - } - - let url = `${ensView.ensUsernamesStore.getEtherscanTxLink()}/${txHash}`; - Global.displayToastMessage(qsTr("Transaction pending..."), - qsTr("View on etherscan"), - icon, - false, - ephType, - url) - } - } } diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index e373822fd3..9e53a01d67 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -237,121 +237,276 @@ Item { ) } - function onShowToastTransactionSent(chainId: int, txHash: string, uuid: string, error: string, txType: int, - fromAddr: string, toAddr: string, fromTokenKey: string, fromAmount: string, - toTokenKey: string, toAmount: string) { - switch(txType) { - case Constants.SendType.Approve: { - const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromTokenKey) - const fromAccountName = SQUtils.ModelUtils.getByKey(appMain.transactionStore.accounts, "address", fromAddr, "name") - const networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName") - if(!!fromToken && !!fromAccountName && !!networkName) { - const approvalAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals) - let toastTitle = qsTr("Setting spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) - let toastSubtitle = qsTr("View on %1").arg(networkName) - let urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash) - let toastType = Constants.ephemeralNotificationType.normal - let icon = "" - if(error) { - toastTitle = qsTr("Failed to set spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) - toastSubtitle = "" - urlLink = "" - toastType = Constants.ephemeralNotificationType.danger - icon = "warning" - } - Global.displayToastMessage(toastTitle, toastSubtitle, icon, !error, toastType, urlLink) - } - break - } - case Constants.SendType.Swap: { - const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromTokenKey) - const toToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", toTokenKey) - const fromAccountName = SQUtils.ModelUtils.getByKey(appMain.transactionStore.accounts, "address", fromAddr, "name") - const networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName") - if(!!fromToken && !!toToken && !!fromAccountName && !!networkName) { - const fromSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals) - const toSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(toAmount, toToken.symbol, toToken.decimals) - let toastTitle = qsTr("Swapping %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) - let toastSubtitle = qsTr("View on %1").arg(networkName) - let urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash) - let toastType = Constants.ephemeralNotificationType.normal - let icon = "" - if(error) { - toastTitle = qsTr("Failed to swap %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) - toastSubtitle = "" - urlLink = "" - toastType = Constants.ephemeralNotificationType.danger - icon = "warning" - } - Global.displayToastMessage(toastTitle, toastSubtitle, icon, !error, toastType, urlLink) - } - break - } - default: { - if (!error) { - let networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName") - if(!!networkName) { - Global.displayToastMessage(qsTr("Transaction pending..."), - qsTr("View on %1").arg(networkName), - "", - true, - Constants.ephemeralNotificationType.normal, - "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash)) - } - } - break - } - } - } + function onShowTransactionToast(uuid: string, + txType: int, + fromChainId: int, + toChainId: int, + fromAddr: string, + fromName: string, + toAddr: string, + toName: string, + txToAddr: string, + txToName: string, + txHash: string, + approvalTx: bool, + fromAmount: string, + toAmount: string, + fromAsset: string, + toAsset: string, + username: string, + publicKey: string, + packId: string, + status: string, + error: string) { - function onShowToastTransactionSendingComplete(chainId: int, txHash: string, data: string, success: bool, - txType: int, fromAddr: string, toAddr: string, fromTokenKey: string, - fromAmount: string, toTokenKey: string, toAmount: string) { - switch(txType) { - case Constants.SendType.Approve: { - const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromTokenKey) - const fromAccountName = SQUtils.ModelUtils.getByKey(appMain.transactionStore.accounts, "address", fromAddr, "name") - const networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName") - if(!!fromToken && !!fromAccountName && !!networkName) { - const approvalAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals) - let toastTitle = qsTr("Spending cap set: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) - const toastSubtitle = qsTr("View on %1").arg(networkName) - const urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash) - let toastType = Constants.ephemeralNotificationType.success - let icon = "checkmark-circle" - if(!success) { - toastTitle = qsTr("Failed to set spending cap: %1 in %2 for %3 on %4").arg(approvalAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) - toastType = Constants.ephemeralNotificationType.danger - icon = "warning" + let toastTitle = "" + let toastSubtitle = "" + let toastIcon = "" + let toastLoading = false + let toastType = Constants.ephemeralNotificationType.normal + let toastLink = "" + + const sender = !!fromName? fromName : SQUtils.Utils.elideText(fromAddr, 6, 4) + let senderChainName = qsTr("unknown") + let sentAmount = "" + + const recipient = !!toName? toName : SQUtils.Utils.elideText(toAddr, 6, 4) + const txRecipient = !!txToName? txToName : SQUtils.Utils.elideText(txToAddr, 6, 4) + let recipientChainName = qsTr("unknown") + let receivedAmount = "" + + let assetName = qsTr("unknown") + let ensName = d.ensName(username) + let stickersPackName = qsTr("unknown") + + if (!!txHash) { + toastLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(fromChainId)).arg(txHash) + } + + const fromChainName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", fromChainId, "chainName") + if (!!fromChainName) { + senderChainName = fromChainName + toastSubtitle = qsTr("View on %1").arg(senderChainName) + } + const toChainName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", toChainId, "chainName") + if (!!toChainName) { + recipientChainName = toChainName + } + + const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromAsset) + if (!!fromToken) { + sentAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals) + } + + const toToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", toAsset) + if (!!toToken) { + receivedAmount = currencyStore.formatCurrencyAmountFromBigInt(toAmount, toToken.symbol, toToken.decimals) + } + + if (txType === Constants.SendType.ERC721Transfer || txType === Constants.SendType.ERC1155Transfer) { + const key = "%1+%2+%3".arg(fromChainId).arg(txToAddr).arg(fromAsset) + const entry = SQUtils.ModelUtils.getByKey(appMain.walletCollectiblesStore.allCollectiblesModel, "symbol", key) + if (!!entry) { + assetName = entry.name + } + } + + if (txType === Constants.SendType.StickersBuy) { + const idx = appMain.rootChatStore.stickersModuleInst.stickerPacks.findIndexById(packId, false) + if(idx >= 0) { + const entry = SQUtils.ModelUtils.get(appMain.rootChatStore.stickersModuleInst.stickerPacks, idx) + if (!!entry) { + stickersPackName = entry.name } - Global.displayToastMessage(toastTitle, toastSubtitle, icon, false, toastType, urlLink) + } + } + + switch(status) { + case Constants.txStatus.sending: { + toastTitle = qsTr("Sending %1 from %2 to %3") + toastLoading = true + + switch(txType) { + case Constants.SendType.Transfer: { + toastTitle = toastTitle.arg(sentAmount).arg(sender).arg(recipient) + break + } + case Constants.SendType.ENSRegister: { + toastTitle = qsTr("Registering %1 ENS name using %2").arg(ensName).arg(sender) + break + } + case Constants.SendType.ENSRelease: { + toastTitle = qsTr("Releasing %1 ENS username using %2").arg(ensName).arg(sender) + break + } + case Constants.SendType.ENSSetPubKey: { + toastTitle = qsTr("Setting public key %1 using %2").arg(ensName).arg(sender) + break + } + case Constants.SendType.StickersBuy: { + toastTitle = qsTr("Purchasing %1 sticker pack using %2").arg(stickersPackName).arg(sender) + break + } + case Constants.SendType.Bridge: { + toastTitle = qsTr("Bridging %1 from %2 to %3 in %4").arg(sentAmount).arg(senderChainName).arg(recipientChainName).arg(sender) + if (approvalTx) { + toastTitle = qsTr("Setting spending cap: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient) + } + break + } + case Constants.SendType.ERC721Transfer: { + toastTitle = toastTitle.arg(assetName).arg(sender).arg(recipient) + break + } + case Constants.SendType.ERC1155Transfer: { + toastTitle = qsTr("Sending %1 %2 from %3 to %4").arg(fromAmount).arg(assetName).arg(sender).arg(recipient) + break + } + case Constants.SendType.Swap: { + toastTitle = qsTr("Swapping %1 to %2 in %3").arg(sentAmount).arg(receivedAmount).arg(sender) + if (approvalTx) { + toastTitle = qsTr("Setting spending cap: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient) + } + break + } + case Constants.SendType.Approve: { + console.warn("tx type approve not yet identified as a stand alone path") + break + } + default: + console.warn("status: sending - tx type not supproted") + return } break } - case Constants.SendType.Swap: { - const fromToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", fromTokenKey) - const toToken = SQUtils.ModelUtils.getByKey(appMain.tokensStore.plainTokensBySymbolModel, "key", toTokenKey) - const fromAccountName = SQUtils.ModelUtils.getByKey(appMain.transactionStore.accounts, "address", fromAddr, "name") - const networkName = SQUtils.ModelUtils.getByKey(WalletStores.RootStore.filteredFlatModel, "chainId", chainId, "chainName") - if(!!fromToken && !!toToken && !!fromAccountName && !!networkName) { - const fromSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(fromAmount, fromToken.symbol, fromToken.decimals) - const toSwapAmount = currencyStore.formatCurrencyAmountFromBigInt(toAmount, toToken.symbol, toToken.decimals) - let toastTitle = qsTr("Swapped %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) - const toastSubtitle = qsTr("View on %1").arg(networkName) - const urlLink = "%1/%2".arg(appMain.rootStore.getEtherscanTxLink(chainId)).arg(txHash) - let toastType = Constants.ephemeralNotificationType.success - let icon = "checkmark-circle" - if(!success) { - toastTitle = qsTr("Failed to swap %1 to %2 in %3 using %4 on %5").arg(fromSwapAmount).arg(toSwapAmount).arg(fromAccountName).arg(Constants.swap.paraswapHostname).arg(networkName) - toastType = Constants.ephemeralNotificationType.danger - icon = "warning" + case Constants.txStatus.pending: { + // So far we don't display notification when it's accepted by the network and its status is pending + // discussed in wallet group chat, we considered that pending status will be displayed almost at the + // same time as sending and decided to skip it. + return + } + case Constants.txStatus.success: { + toastTitle = qsTr("Sent %1 from %2 to %3") + toastIcon = "checkmark-circle" + toastType = Constants.ephemeralNotificationType.success + + switch(txType) { + case Constants.SendType.Transfer: { + toastTitle = toastTitle.arg(sentAmount).arg(sender).arg(recipient) + break + } + case Constants.SendType.ENSRegister: { + toastTitle = qsTr("Registered %1 ENS name using %2").arg(ensName).arg(sender) + break + } + case Constants.SendType.ENSRelease: { + toastTitle = qsTr("Released %1 ENS username using %2").arg(ensName).arg(sender) + break + } + case Constants.SendType.ENSSetPubKey: { + toastTitle = qsTr("Set public key %1 using %2").arg(ensName).arg(sender) + break + } + case Constants.SendType.StickersBuy: { + toastTitle = qsTr("Purchased %1 sticker pack using %2").arg(stickersPackName).arg(sender) + break + } + case Constants.SendType.Bridge: { + toastTitle = qsTr("Bridged %1 from %2 to %3 in %4").arg(sentAmount).arg(senderChainName).arg(recipientChainName).arg(sender) + if (approvalTx) { + toastTitle = qsTr("Spending spending cap: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient) } - Global.displayToastMessage(toastTitle, toastSubtitle, icon, false, toastType, urlLink) + break + } + case Constants.SendType.ERC721Transfer: { + toastTitle = toastTitle.arg(assetName).arg(sender).arg(recipient) + break + } + case Constants.SendType.ERC1155Transfer: { + toastTitle = qsTr("Sent %1 %2 from %3 to %4").arg(fromAmount).arg(assetName).arg(sender).arg(recipient) + break + } + case Constants.SendType.Swap: { + toastTitle = qsTr("Swapped %1 to %2 in %3").arg(sentAmount).arg(receivedAmount).arg(sender) + if (approvalTx) { + toastTitle = qsTr("Spending cap set: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient) + } + break + } + case Constants.SendType.Approve: { + console.warn("tx type approve not yet identified as a stand alone path") + break + } + default: + console.warn("status: success - tx type not supproted") + return } break } - default: break + case Constants.txStatus.failed: { + toastTitle = "Send failed: %1 from %2 to %3" + toastIcon = "warning" + toastType = Constants.ephemeralNotificationType.danger + + switch(txType) { + case Constants.SendType.Transfer: { + toastTitle = toastTitle.arg(sentAmount).arg(sender).arg(recipient) + break + } + case Constants.SendType.ENSRegister: { + toastTitle = qsTr("ENS username registeration failed: %1 using %2").arg(ensName).arg(sender) + break + } + case Constants.SendType.ENSRelease: { + toastTitle = qsTr("ENS username release failed: %1 using %2").arg(ensName).arg(sender) + break + } + case Constants.SendType.ENSSetPubKey: { + toastTitle = qsTr("Set public key failed: %1 using %2").arg(ensName).arg(sender) + break + } + case Constants.SendType.StickersBuy: { + toastTitle = qsTr("Sticker pack purchase failed: %1 using %2").arg(stickersPackName).arg(sender) + break + } + case Constants.SendType.Bridge: { + toastTitle = qsTr("Bridge failed: %1 from %2 to %3 in %4").arg(sentAmount).arg(senderChainName).arg(recipientChainName).arg(sender) + if (approvalTx) { + toastTitle = qsTr("Spending spending failed: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient) + } + break + } + case Constants.SendType.ERC721Transfer: { + toastTitle = toastTitle.arg(assetName).arg(sender).arg(recipient) + break + } + case Constants.SendType.ERC1155Transfer: { + toastTitle = qsTr("Send failed: %1 %2 from %3 to %4").arg(fromAmount).arg(assetName).arg(sender).arg(recipient) + break + } + case Constants.SendType.Swap: { + toastTitle = qsTr("Swapp failed: %1 to %2 in %3").arg(sentAmount).arg(receivedAmount).arg(sender) + if (approvalTx) { + toastTitle = qsTr("Spending cap failed: %1 in %2 for %3").arg(sentAmount).arg(sender).arg(txRecipient) + } + break + } + case Constants.SendType.Approve: { + console.warn("tx type approve not yet identified as a stand alone path") + break + } + default: + console.warn("status: failed - tx type not supproted") + return + } + break } + default: + console.warn("not supported status") + return + } + + Global.displayToastMessage(toastTitle, toastSubtitle, toastIcon, toastLoading, toastType, toastLink) } function onCommunityMemberStatusEphemeralNotification(communityName: string, memberName: string, state: CommunityMembershipRequestState) { @@ -408,6 +563,13 @@ Item { activityCenterPopupObj.open() } } + + function ensName(username) { + if (!username.endsWith(".stateofus.eth") && !username.endsWith(".eth")) { + return "%1.%2".arg(username).arg("stateofus.eth") + } + return username + } } Settings { diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index 9f04a462b2..2a74bb4469 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -1104,6 +1104,13 @@ QtObject { High } + readonly property QtObject txStatus: QtObject { + readonly property string sending: "Sending" + readonly property string pending: "Pending" + readonly property string success: "Success" + readonly property string failed: "Failed" + } + enum LoginType { Password, Biometrics,