From 4854d9d100ba71353af29baf140e95e9137e2248 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Wed, 20 Dec 2023 11:46:33 +0100 Subject: [PATCH] feat(@desktop/wallet): mark address as shown when it is about to be fully displayed Fixes: #12989 --- src/app/global/app_signals.nim | 8 +++++++ src/app/global/utils.nim | 3 +++ src/app/modules/main/communities/module.nim | 7 ++++++ src/app/modules/main/io_interface.nim | 6 +++++ src/app/modules/main/module.nim | 16 +++++++++++++ .../profile_section/profile/controller.nim | 4 +++- .../main/profile_section/profile/module.nim | 11 +++++++-- src/app/modules/main/view.nim | 24 +++++++++++++------ .../service/transaction/service.nim | 15 ++++++++---- .../wallet_account/service_account.nim | 8 +++++++ src/backend/accounts.nim | 4 ++++ .../Profile/popups/WalletAddressMenu.qml | 12 ++++++++++ .../Profile/stores/ProfileSectionStore.qml | 4 ++++ .../AppLayouts/Profile/views/WalletView.qml | 3 +++ .../AppLayouts/Wallet/popups/ReceiveModal.qml | 4 ++++ .../Wallet/popups/TransactionAddressMenu.qml | 12 ++++++++++ ui/app/AppLayouts/Wallet/stores/RootStore.qml | 5 ++++ .../AppLayouts/Wallet/views/RightTabView.qml | 10 +++++++- ui/imports/shared/views/HistoryView.qml | 4 ++++ 19 files changed, 145 insertions(+), 15 deletions(-) diff --git a/src/app/global/app_signals.nim b/src/app/global/app_signals.nim index b8d1708132..465a68ea5d 100644 --- a/src/app/global/app_signals.nim +++ b/src/app/global/app_signals.nim @@ -42,4 +42,12 @@ type const SIGNAL_STATUS_URL_ACTIVATED* = "statusUrlActivated" const FAKE_LOADING_SCREEN_FINISHED* = "fakeLoadingScreenFinished" + +type + WalletAddressesArgs* = ref object of Args + addresses*: seq[string] + +const MARK_WALLET_ADDRESSES_AS_SHOWN* = "markWalletAddressesAsShown" + + const WALLET_CONNECT_CHECK_PAIRINGS* = "walletConnectCheckPairings" \ No newline at end of file diff --git a/src/app/global/utils.nim b/src/app/global/utils.nim index b89616108f..cb169d96e0 100644 --- a/src/app/global/utils.nim +++ b/src/app/global/utils.nim @@ -88,7 +88,10 @@ QtObject: except: return false + proc checkIfAddressWasCopied*(self: Utils, value: string) {.signal.} + proc copyToClipboard*(self: Utils, content: string) {.slot.} = + self.checkIfAddressWasCopied(content) setClipBoardText(content) proc getFromClipboard*(self: Utils): string {.slot.} = diff --git a/src/app/modules/main/communities/module.nim b/src/app/modules/main/communities/module.nim index 62dcd8a613..62ad5bea5b 100644 --- a/src/app/modules/main/communities/module.nim +++ b/src/app/modules/main/communities/module.nim @@ -15,6 +15,7 @@ import ./models/discord_import_tasks_model import app/modules/shared_models/[member_item, section_model, section_item, token_permissions_model, token_permission_item, token_list_item, token_list_model, token_criteria_item, token_criteria_model, token_permission_chat_list_model, keypair_model] import app/global/global_singleton +import app/global/app_signals import app/core/eventemitter import app_service/common/types import app_service/common/utils as common_utils @@ -83,6 +84,7 @@ type view: View viewVariant: QVariant moduleLoaded: bool + events: EventEmitter curatedCommunitiesLoaded: bool communityTokensModule: community_tokens_module.AccessInterface checkingPermissionToJoinInProgress: bool @@ -125,6 +127,7 @@ proc newModule*( ) result.communityTokensModule = community_tokens_module.newCommunityTokensModule(result, events, communityTokensService, transactionService, networksService, communityService) result.moduleLoaded = false + result.events = events result.curatedCommunitiesLoaded = false result.checkingPermissionToJoinInProgress = false result.checkingAllChannelPermissionsInProgress = false @@ -796,12 +799,16 @@ method joinCommunityOrEditSharedAddresses*(self: Module) = addressesToShare, airdropAddress, signatures) + # The user reveals address after sending join coummunity request, before that he sees only the name of the wallet account, not the address. + self.events.emit(MARK_WALLET_ADDRESSES_AS_SHOWN, WalletAddressesArgs(addresses: addressesToShare)) return if self.joiningCommunityDetails.action == Action.EditSharedAddresses: self.controller.asyncEditSharedAddresses(self.joiningCommunityDetails.communityId, addressesToShare, airdropAddress, signatures) + # The user reveals address after sending edit coummunity request, before that he sees only the name of the wallet account, not the address. + self.events.emit(MARK_WALLET_ADDRESSES_AS_SHOWN, WalletAddressesArgs(addresses: addressesToShare)) return self.communityAccessFailed(self.joiningCommunityDetails.communityId, "unexpected action") diff --git a/src/app/modules/main/io_interface.nim b/src/app/modules/main/io_interface.nim index 0bb4e2f211..31bda73db9 100644 --- a/src/app/modules/main/io_interface.nim +++ b/src/app/modules/main/io_interface.nim @@ -399,6 +399,12 @@ method onCommunityTokensDetailsLoaded*(self: AccessInterface, communityId: strin communityTokens: seq[CommunityTokenDto], communityTokenJsonItems: JsonNode) {.base.} = raise newException(ValueError, "No implementation available") +method addressWasShown*(self: AccessInterface, address: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method checkIfAddressWasCopied*(self: AccessInterface, value: string) {.base.} = + raise newException(ValueError, "No implementation available") + # This way (using concepts) is used only for the modules managed by AppController type DelegateInterface* = concept c diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index b6417edd09..80c167db39 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -494,6 +494,11 @@ proc connectForNotificationsOnly[T](self: Module[T]) = let args = TransactionSentArgs(e) self.view.showToastTransactionSent(args.chainId, args.txHash, args.uuid, args.error) + self.events.on(MARK_WALLET_ADDRESSES_AS_SHOWN) do(e:Args): + let args = WalletAddressesArgs(e) + for address in args.addresses: + self.addressWasShown(address) + method load*[T]( self: Module[T], events: EventEmitter, @@ -1588,4 +1593,15 @@ method removeMockedKeycardAction*[T](self: Module[T]) = method fakeLoadingScreenFinished*[T](self: Module[T]) = self.events.emit(FAKE_LOADING_SCREEN_FINISHED, Args()) +method addressWasShown*[T](self: Module[T], address: string) = + if address.len == 0: + return + self.walletAccountService.addressWasShown(address) + +method checkIfAddressWasCopied*[T](self: Module[T], value: string) = + let walletAcc = self.walletAccountService.getAccountByAddress(value) + if walletAcc.isNil: + return + self.addressWasShown(value) + {.pop.} diff --git a/src/app/modules/main/profile_section/profile/controller.nim b/src/app/modules/main/profile_section/profile/controller.nim index a3522e039e..0af595ba7e 100644 --- a/src/app/modules/main/profile_section/profile/controller.nim +++ b/src/app/modules/main/profile_section/profile/controller.nim @@ -1,5 +1,6 @@ import io_interface +import app/global/app_signals import app/core/eventemitter import app_service/service/profile/service as profile_service import app_service/service/settings/service as settings_service @@ -92,8 +93,9 @@ proc getBio*(self: Controller): string = proc setBio*(self: Controller, bio: string): bool = self.settingsService.saveBio(bio) -proc storeProfileShowcasePreferences*(self: Controller, preferences: ProfileShowcasePreferencesDto) = +proc storeProfileShowcasePreferences*(self: Controller, preferences: ProfileShowcasePreferencesDto, revealedAddresses: seq[string]) = self.profileService.setProfileShowcasePreferences(preferences) + self.events.emit(MARK_WALLET_ADDRESSES_AS_SHOWN, WalletAddressesArgs(addresses: revealedAddresses)) proc requestProfileShowcasePreferences*(self: Controller) = self.profileService.requestProfileShowcasePreferences() diff --git a/src/app/modules/main/profile_section/profile/module.nim b/src/app/modules/main/profile_section/profile/module.nim index 73369ee7db..21ec264391 100644 --- a/src/app/modules/main/profile_section/profile/module.nim +++ b/src/app/modules/main/profile_section/profile/module.nim @@ -113,12 +113,19 @@ method storeProfileShowcasePreferences(self: Module, error "Attempt to save preferences with wrong public key" return + var revealedAddresses: seq[string] + for acc in accounts: + if acc.showcaseVisibility != ProfileShowcaseVisibility.ToNoOne: + revealedAddresses.add(acc.address) + self.controller.storeProfileShowcasePreferences(ProfileShowcasePreferencesDto( communities: communities.map(item => item.toShowcasePreferenceItem()), accounts: accounts.map(item => item.toShowcasePreferenceItem()), collectibles: collectibles.map(item => item.toShowcasePreferenceItem()), - assets: assets.map(item => item.toShowcasePreferenceItem()) - )) + assets: assets.map(item => item.toShowcasePreferenceItem()), + ), + revealedAddresses + ) method requestProfileShowcasePreferences(self: Module) = let myPublicKey = singletonInstance.userProfile.getPubKey() diff --git a/src/app/modules/main/view.nim b/src/app/modules/main/view.nim index 31b7082fae..b0effabde6 100644 --- a/src/app/modules/main/view.nim +++ b/src/app/modules/main/view.nim @@ -1,12 +1,13 @@ import NimQml, strutils -import ../shared_models/section_model -import ../shared_models/section_item -import ../shared_models/section_details +import app/global/global_singleton +import app/modules/shared_models/section_model +import app/modules/shared_models/section_item +import app/modules/shared_models/section_details import io_interface import chat_search_model import ephemeral_notification_model -from ../../../app_service/common/conversion import intToEnum -from ../../../app_service/common/types import StatusType +from app_service/common/conversion import intToEnum +from app_service/common/types import StatusType QtObject: type @@ -51,8 +52,9 @@ QtObject: result.chatSearchModelVariant = newQVariant(result.chatSearchModel) result.ephemeralNotificationModel = ephemeralNotification_model.newModel() result.ephemeralNotificationModelVariant = newQVariant(result.ephemeralNotificationModel) - signalConnect(result.model, "notificationsCountChanged()", result, - "onNotificationsCountChanged()", 2) + + signalConnect(result.model, "notificationsCountChanged()", result, "onNotificationsCountChanged()", 2) + signalConnect(singletonInstance.utils, "checkIfAddressWasCopied(QString)", result, "onCheckIfAddressWasCopied(QString)", 2) proc load*(self: View) = # In some point, here, we will setup some exposed main module related things. @@ -326,3 +328,11 @@ QtObject: proc fakeLoadingScreenFinished*(self: View) {.slot.} = self.delegate.fakeLoadingScreenFinished() + + ## Address was shown is added here because it will be used from many different parts of the app + ## and "mainModule" is accessible from everywhere + proc addressWasShown*(self: View, address: string) {.slot.} = + self.delegate.addressWasShown(address) + + proc onCheckIfAddressWasCopied*(self: View, value: string) {.slot.} = + self.delegate.checkIfAddressWasCopied(value) \ No newline at end of file diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim index ad17765b86..0c62804fa4 100644 --- a/src/app_service/service/transaction/service.nim +++ b/src/app_service/service/transaction/service.nim @@ -10,10 +10,12 @@ import ../../common/conversion as common_conversion import ../../common/utils as common_utils import ../../common/types as common_types -import ../../../app/core/[main] -import ../../../app/core/signals/types -import ../../../app/core/tasks/[qt, threadpool] -import ../../../app/global/global_singleton +import app/core/[main] +import app/core/signals/types +import app/core/tasks/[qt, threadpool] +import app/global/global_singleton +import app/global/app_signals + import ../wallet_account/service as wallet_account_service import ../network/service as network_service import ../token/service as token_service @@ -287,6 +289,11 @@ QtObject: proc sendTransactionSentSignal(self: Service, fromAddr: string, toAddr: string, uuid: string, routes: seq[TransactionPathDto], response: RpcResponse[JsonNode], err: string = "", tokenName = "", isOwnerToken=false) = + # While preparing the tx in the Send modal user cannot see the address, it's revealed once the tx is sent + # (there are few places where we display the toast from and link to the etherscan where the address can be seen) + # that's why we need to mark the addresses as shown here (safer). + self.events.emit(MARK_WALLET_ADDRESSES_AS_SHOWN, WalletAddressesArgs(addresses: @[fromAddr, toAddr])) + if err.len > 0: self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(uuid: uuid, error: err)) elif response.result{"hashes"} != nil: diff --git a/src/app_service/service/wallet_account/service_account.nim b/src/app_service/service/wallet_account/service_account.nim index 8326475ac2..f04d7f48d2 100644 --- a/src/app_service/service/wallet_account/service_account.nim +++ b/src/app_service/service/wallet_account/service_account.nim @@ -782,3 +782,11 @@ proc importPartiallyOperableAccounts(self: Service, keyUid: string, password: st if keyUid != singletonInstance.userProfile.getKeyUid(): return self.makePartiallyOperableAccoutsFullyOperable(password, not singletonInstance.userProfile.getIsKeycardUser()) + +proc addressWasShown*(self: Service, address: string) = + try: + let response = status_go_accounts.addressWasShown(address) + if not response.error.isNil: + raise newException(CatchableError, response.error.message) + except Exception as e: + error "error: ", procName="addressWasShown", errName=e.name, errDesription=e.msg \ No newline at end of file diff --git a/src/backend/accounts.nim b/src/backend/accounts.nim index 7573e9223e..479e23ac51 100644 --- a/src/backend/accounts.nim +++ b/src/backend/accounts.nim @@ -483,3 +483,7 @@ proc getProfileShowcasePreferences*(): RpcResponse[JsonNode] {.raises: [Exceptio proc setProfileShowcasePreferences*(preferences: JsonNode): RpcResponse[JsonNode] {.raises: [Exception].} = result = callPrivateRPC("setProfileShowcasePreferences".prefix, preferences) + +proc addressWasShown*(address: string): RpcResponse[JsonNode] {.raises: [Exception].} = + let payload = %* [address] + return core.callPrivateRPC("accounts_addressWasShown", payload) \ No newline at end of file diff --git a/ui/app/AppLayouts/Profile/popups/WalletAddressMenu.qml b/ui/app/AppLayouts/Profile/popups/WalletAddressMenu.qml index e0f6b63006..8faadce1db 100644 --- a/ui/app/AppLayouts/Profile/popups/WalletAddressMenu.qml +++ b/ui/app/AppLayouts/Profile/popups/WalletAddressMenu.qml @@ -6,6 +6,18 @@ import utils 1.0 import AppLayouts.Wallet.popups 1.0 +//////////////////////////////////////////////////////////////////////////////// +// NOTE: +// +// The address should be marked as shown (calling `mainModule.addressWasShown(address)`) if the user interacts with any +// of the current actions in the menu. +// +// That call is not added now, just because the only place where this menu is used is in the account view and the address +// will be already marked as shown when the user opens the account view. +// +// This note here is just to remember that if this menu is used in other places, the address should be marked as shown. +//////////////////////////////////////////////////////////////////////////////// + StatusMenu { id: root diff --git a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml index 298570a2b8..2b129cc6b2 100644 --- a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml +++ b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml @@ -197,4 +197,8 @@ QtObject { return "" } + + function addressWasShown(address) { + return root.mainModuleInst.addressWasShown(address) + } } diff --git a/ui/app/AppLayouts/Profile/views/WalletView.qml b/ui/app/AppLayouts/Profile/views/WalletView.qml index 0c7e4a37e0..13fd6b7a07 100644 --- a/ui/app/AppLayouts/Profile/views/WalletView.qml +++ b/ui/app/AppLayouts/Profile/views/WalletView.qml @@ -151,6 +151,9 @@ SettingsContentBase { } onGoToAccountView: { + if (!!account && !!account.address) { + root.rootStore.addressWasShown(account.address) + } root.walletStore.selectedAccount = account accountView.keyPair = keypair stackContainer.currentIndex = accountViewIndex diff --git a/ui/app/AppLayouts/Wallet/popups/ReceiveModal.qml b/ui/app/AppLayouts/Wallet/popups/ReceiveModal.qml index a970ff1666..de290beb94 100644 --- a/ui/app/AppLayouts/Wallet/popups/ReceiveModal.qml +++ b/ui/app/AppLayouts/Wallet/popups/ReceiveModal.qml @@ -41,6 +41,10 @@ StatusModal { } } + onOpened: { + RootStore.addressWasShown(root.address) + } + headerSettings.title: qsTr("Receive") contentHeight: layout.implicitHeight width: 556 diff --git a/ui/app/AppLayouts/Wallet/popups/TransactionAddressMenu.qml b/ui/app/AppLayouts/Wallet/popups/TransactionAddressMenu.qml index 48addfa723..23d762c444 100644 --- a/ui/app/AppLayouts/Wallet/popups/TransactionAddressMenu.qml +++ b/ui/app/AppLayouts/Wallet/popups/TransactionAddressMenu.qml @@ -15,6 +15,18 @@ import shared.stores 1.0 import "../stores" as WalletStores +//////////////////////////////////////////////////////////////////////////////// +// NOTE: +// +// The address should be marked as shown (calling `mainModule.addressWasShown(address)`) if the user interacts with +// actions in the menu that reveals the address. +// +// That call is not added now, just because the only place where this menu is used is in the transaction details view +// and the address will be already marked as shown when the user opens the transaction details view. +// +// This note here is just to remember that if this menu is used in other places, the address should be marked as shown. +//////////////////////////////////////////////////////////////////////////////// + StatusMenu { id: root diff --git a/ui/app/AppLayouts/Wallet/stores/RootStore.qml b/ui/app/AppLayouts/Wallet/stores/RootStore.qml index 0c165970a8..e75e300894 100644 --- a/ui/app/AppLayouts/Wallet/stores/RootStore.qml +++ b/ui/app/AppLayouts/Wallet/stores/RootStore.qml @@ -39,6 +39,7 @@ QtObject { property bool hideSignPhraseModal: accountSensitiveSettings.hideSignPhraseModal // "walletSection" is a context property slow to lookup, so we cache it here + property var mainModuleInst: mainModule property var walletSectionInst: walletSection property var totalCurrencyBalance: walletSectionInst.totalCurrencyBalance property var activityController: walletSectionInst.activityController @@ -401,4 +402,8 @@ QtObject { } property CurrenciesStore currencyStore: CurrenciesStore {} + + function addressWasShown(address) { + return root.mainModuleInst.addressWasShown(address) + } } diff --git a/ui/app/AppLayouts/Wallet/views/RightTabView.qml b/ui/app/AppLayouts/Wallet/views/RightTabView.qml index c6906b4eb2..2059c12480 100644 --- a/ui/app/AppLayouts/Wallet/views/RightTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/RightTabView.qml @@ -229,8 +229,16 @@ Item { TransactionDetailView { id: transactionDetailView onVisibleChanged: { - if (!visible) + if (visible) { + if (!!transaction) { + RootStore.addressWasShown(transaction.sender) + if (transaction.sender !== transaction.recipient) { + RootStore.addressWasShown(transaction.recipient) + } + } + } else { transaction = null + } } showAllAccounts: root.showAllAccounts sendModal: root.sendModal diff --git a/ui/imports/shared/views/HistoryView.qml b/ui/imports/shared/views/HistoryView.qml index e81587c2ec..9574597cc4 100644 --- a/ui/imports/shared/views/HistoryView.qml +++ b/ui/imports/shared/views/HistoryView.qml @@ -367,6 +367,10 @@ ColumnLayout { onTriggered: { if (!delegateMenu.transactionDelegate) return + WalletStores.RootStore.addressWasShown(delegateMenu.transaction.sender) + if (delegateMenu.transaction.sender !== delegateMenu.transaction.recipient) { + WalletStores.RootStore.addressWasShown(delegateMenu.transaction.recipient) + } RootStore.copyToClipboard(delegateMenu.transactionDelegate.getDetailsString()) } }