feat(@desktop/wallet): mark address as shown when it is about to be fully displayed

Fixes: #12989
This commit is contained in:
Sale Djenic 2023-12-20 11:46:33 +01:00 committed by saledjenic
parent 4c3c763c3a
commit 4854d9d100
19 changed files with 145 additions and 15 deletions

View File

@ -42,4 +42,12 @@ type
const SIGNAL_STATUS_URL_ACTIVATED* = "statusUrlActivated" const SIGNAL_STATUS_URL_ACTIVATED* = "statusUrlActivated"
const FAKE_LOADING_SCREEN_FINISHED* = "fakeLoadingScreenFinished" 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" const WALLET_CONNECT_CHECK_PAIRINGS* = "walletConnectCheckPairings"

View File

@ -88,7 +88,10 @@ QtObject:
except: except:
return false return false
proc checkIfAddressWasCopied*(self: Utils, value: string) {.signal.}
proc copyToClipboard*(self: Utils, content: string) {.slot.} = proc copyToClipboard*(self: Utils, content: string) {.slot.} =
self.checkIfAddressWasCopied(content)
setClipBoardText(content) setClipBoardText(content)
proc getFromClipboard*(self: Utils): string {.slot.} = proc getFromClipboard*(self: Utils): string {.slot.} =

View File

@ -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, 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] 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/global_singleton
import app/global/app_signals
import app/core/eventemitter import app/core/eventemitter
import app_service/common/types import app_service/common/types
import app_service/common/utils as common_utils import app_service/common/utils as common_utils
@ -83,6 +84,7 @@ type
view: View view: View
viewVariant: QVariant viewVariant: QVariant
moduleLoaded: bool moduleLoaded: bool
events: EventEmitter
curatedCommunitiesLoaded: bool curatedCommunitiesLoaded: bool
communityTokensModule: community_tokens_module.AccessInterface communityTokensModule: community_tokens_module.AccessInterface
checkingPermissionToJoinInProgress: bool checkingPermissionToJoinInProgress: bool
@ -125,6 +127,7 @@ proc newModule*(
) )
result.communityTokensModule = community_tokens_module.newCommunityTokensModule(result, events, communityTokensService, transactionService, networksService, communityService) result.communityTokensModule = community_tokens_module.newCommunityTokensModule(result, events, communityTokensService, transactionService, networksService, communityService)
result.moduleLoaded = false result.moduleLoaded = false
result.events = events
result.curatedCommunitiesLoaded = false result.curatedCommunitiesLoaded = false
result.checkingPermissionToJoinInProgress = false result.checkingPermissionToJoinInProgress = false
result.checkingAllChannelPermissionsInProgress = false result.checkingAllChannelPermissionsInProgress = false
@ -796,12 +799,16 @@ method joinCommunityOrEditSharedAddresses*(self: Module) =
addressesToShare, addressesToShare,
airdropAddress, airdropAddress,
signatures) 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 return
if self.joiningCommunityDetails.action == Action.EditSharedAddresses: if self.joiningCommunityDetails.action == Action.EditSharedAddresses:
self.controller.asyncEditSharedAddresses(self.joiningCommunityDetails.communityId, self.controller.asyncEditSharedAddresses(self.joiningCommunityDetails.communityId,
addressesToShare, addressesToShare,
airdropAddress, airdropAddress,
signatures) 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 return
self.communityAccessFailed(self.joiningCommunityDetails.communityId, "unexpected action") self.communityAccessFailed(self.joiningCommunityDetails.communityId, "unexpected action")

View File

@ -399,6 +399,12 @@ method onCommunityTokensDetailsLoaded*(self: AccessInterface, communityId: strin
communityTokens: seq[CommunityTokenDto], communityTokenJsonItems: JsonNode) {.base.} = communityTokens: seq[CommunityTokenDto], communityTokenJsonItems: JsonNode) {.base.} =
raise newException(ValueError, "No implementation available") 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 # This way (using concepts) is used only for the modules managed by AppController
type type
DelegateInterface* = concept c DelegateInterface* = concept c

View File

@ -494,6 +494,11 @@ proc connectForNotificationsOnly[T](self: Module[T]) =
let args = TransactionSentArgs(e) let args = TransactionSentArgs(e)
self.view.showToastTransactionSent(args.chainId, args.txHash, args.uuid, args.error) 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]( method load*[T](
self: Module[T], self: Module[T],
events: EventEmitter, events: EventEmitter,
@ -1588,4 +1593,15 @@ method removeMockedKeycardAction*[T](self: Module[T]) =
method fakeLoadingScreenFinished*[T](self: Module[T]) = method fakeLoadingScreenFinished*[T](self: Module[T]) =
self.events.emit(FAKE_LOADING_SCREEN_FINISHED, Args()) 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.} {.pop.}

View File

@ -1,5 +1,6 @@
import io_interface import io_interface
import app/global/app_signals
import app/core/eventemitter import app/core/eventemitter
import app_service/service/profile/service as profile_service import app_service/service/profile/service as profile_service
import app_service/service/settings/service as settings_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 = proc setBio*(self: Controller, bio: string): bool =
self.settingsService.saveBio(bio) self.settingsService.saveBio(bio)
proc storeProfileShowcasePreferences*(self: Controller, preferences: ProfileShowcasePreferencesDto) = proc storeProfileShowcasePreferences*(self: Controller, preferences: ProfileShowcasePreferencesDto, revealedAddresses: seq[string]) =
self.profileService.setProfileShowcasePreferences(preferences) self.profileService.setProfileShowcasePreferences(preferences)
self.events.emit(MARK_WALLET_ADDRESSES_AS_SHOWN, WalletAddressesArgs(addresses: revealedAddresses))
proc requestProfileShowcasePreferences*(self: Controller) = proc requestProfileShowcasePreferences*(self: Controller) =
self.profileService.requestProfileShowcasePreferences() self.profileService.requestProfileShowcasePreferences()

View File

@ -113,12 +113,19 @@ method storeProfileShowcasePreferences(self: Module,
error "Attempt to save preferences with wrong public key" error "Attempt to save preferences with wrong public key"
return return
var revealedAddresses: seq[string]
for acc in accounts:
if acc.showcaseVisibility != ProfileShowcaseVisibility.ToNoOne:
revealedAddresses.add(acc.address)
self.controller.storeProfileShowcasePreferences(ProfileShowcasePreferencesDto( self.controller.storeProfileShowcasePreferences(ProfileShowcasePreferencesDto(
communities: communities.map(item => item.toShowcasePreferenceItem()), communities: communities.map(item => item.toShowcasePreferenceItem()),
accounts: accounts.map(item => item.toShowcasePreferenceItem()), accounts: accounts.map(item => item.toShowcasePreferenceItem()),
collectibles: collectibles.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) = method requestProfileShowcasePreferences(self: Module) =
let myPublicKey = singletonInstance.userProfile.getPubKey() let myPublicKey = singletonInstance.userProfile.getPubKey()

View File

@ -1,12 +1,13 @@
import NimQml, strutils import NimQml, strutils
import ../shared_models/section_model import app/global/global_singleton
import ../shared_models/section_item import app/modules/shared_models/section_model
import ../shared_models/section_details import app/modules/shared_models/section_item
import app/modules/shared_models/section_details
import io_interface import io_interface
import chat_search_model import chat_search_model
import ephemeral_notification_model import ephemeral_notification_model
from ../../../app_service/common/conversion import intToEnum from app_service/common/conversion import intToEnum
from ../../../app_service/common/types import StatusType from app_service/common/types import StatusType
QtObject: QtObject:
type type
@ -51,8 +52,9 @@ QtObject:
result.chatSearchModelVariant = newQVariant(result.chatSearchModel) result.chatSearchModelVariant = newQVariant(result.chatSearchModel)
result.ephemeralNotificationModel = ephemeralNotification_model.newModel() result.ephemeralNotificationModel = ephemeralNotification_model.newModel()
result.ephemeralNotificationModelVariant = newQVariant(result.ephemeralNotificationModel) 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) = proc load*(self: View) =
# In some point, here, we will setup some exposed main module related things. # In some point, here, we will setup some exposed main module related things.
@ -326,3 +328,11 @@ QtObject:
proc fakeLoadingScreenFinished*(self: View) {.slot.} = proc fakeLoadingScreenFinished*(self: View) {.slot.} =
self.delegate.fakeLoadingScreenFinished() 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)

View File

@ -10,10 +10,12 @@ import ../../common/conversion as common_conversion
import ../../common/utils as common_utils import ../../common/utils as common_utils
import ../../common/types as common_types import ../../common/types as common_types
import ../../../app/core/[main] import app/core/[main]
import ../../../app/core/signals/types import app/core/signals/types
import ../../../app/core/tasks/[qt, threadpool] import app/core/tasks/[qt, threadpool]
import ../../../app/global/global_singleton import app/global/global_singleton
import app/global/app_signals
import ../wallet_account/service as wallet_account_service import ../wallet_account/service as wallet_account_service
import ../network/service as network_service import ../network/service as network_service
import ../token/service as token_service import ../token/service as token_service
@ -287,6 +289,11 @@ QtObject:
proc sendTransactionSentSignal(self: Service, fromAddr: string, toAddr: string, uuid: string, proc sendTransactionSentSignal(self: Service, fromAddr: string, toAddr: string, uuid: string,
routes: seq[TransactionPathDto], response: RpcResponse[JsonNode], err: string = "", tokenName = "", isOwnerToken=false) = 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: if err.len > 0:
self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(uuid: uuid, error: err)) self.events.emit(SIGNAL_TRANSACTION_SENT, TransactionSentArgs(uuid: uuid, error: err))
elif response.result{"hashes"} != nil: elif response.result{"hashes"} != nil:

View File

@ -782,3 +782,11 @@ proc importPartiallyOperableAccounts(self: Service, keyUid: string, password: st
if keyUid != singletonInstance.userProfile.getKeyUid(): if keyUid != singletonInstance.userProfile.getKeyUid():
return return
self.makePartiallyOperableAccoutsFullyOperable(password, not singletonInstance.userProfile.getIsKeycardUser()) 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

View File

@ -483,3 +483,7 @@ proc getProfileShowcasePreferences*(): RpcResponse[JsonNode] {.raises: [Exceptio
proc setProfileShowcasePreferences*(preferences: JsonNode): RpcResponse[JsonNode] {.raises: [Exception].} = proc setProfileShowcasePreferences*(preferences: JsonNode): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("setProfileShowcasePreferences".prefix, preferences) result = callPrivateRPC("setProfileShowcasePreferences".prefix, preferences)
proc addressWasShown*(address: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [address]
return core.callPrivateRPC("accounts_addressWasShown", payload)

View File

@ -6,6 +6,18 @@ import utils 1.0
import AppLayouts.Wallet.popups 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 { StatusMenu {
id: root id: root

View File

@ -197,4 +197,8 @@ QtObject {
return "" return ""
} }
function addressWasShown(address) {
return root.mainModuleInst.addressWasShown(address)
}
} }

View File

@ -151,6 +151,9 @@ SettingsContentBase {
} }
onGoToAccountView: { onGoToAccountView: {
if (!!account && !!account.address) {
root.rootStore.addressWasShown(account.address)
}
root.walletStore.selectedAccount = account root.walletStore.selectedAccount = account
accountView.keyPair = keypair accountView.keyPair = keypair
stackContainer.currentIndex = accountViewIndex stackContainer.currentIndex = accountViewIndex

View File

@ -41,6 +41,10 @@ StatusModal {
} }
} }
onOpened: {
RootStore.addressWasShown(root.address)
}
headerSettings.title: qsTr("Receive") headerSettings.title: qsTr("Receive")
contentHeight: layout.implicitHeight contentHeight: layout.implicitHeight
width: 556 width: 556

View File

@ -15,6 +15,18 @@ import shared.stores 1.0
import "../stores" as WalletStores 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 { StatusMenu {
id: root id: root

View File

@ -39,6 +39,7 @@ QtObject {
property bool hideSignPhraseModal: accountSensitiveSettings.hideSignPhraseModal property bool hideSignPhraseModal: accountSensitiveSettings.hideSignPhraseModal
// "walletSection" is a context property slow to lookup, so we cache it here // "walletSection" is a context property slow to lookup, so we cache it here
property var mainModuleInst: mainModule
property var walletSectionInst: walletSection property var walletSectionInst: walletSection
property var totalCurrencyBalance: walletSectionInst.totalCurrencyBalance property var totalCurrencyBalance: walletSectionInst.totalCurrencyBalance
property var activityController: walletSectionInst.activityController property var activityController: walletSectionInst.activityController
@ -401,4 +402,8 @@ QtObject {
} }
property CurrenciesStore currencyStore: CurrenciesStore {} property CurrenciesStore currencyStore: CurrenciesStore {}
function addressWasShown(address) {
return root.mainModuleInst.addressWasShown(address)
}
} }

View File

@ -229,9 +229,17 @@ Item {
TransactionDetailView { TransactionDetailView {
id: transactionDetailView id: transactionDetailView
onVisibleChanged: { onVisibleChanged: {
if (!visible) if (visible) {
if (!!transaction) {
RootStore.addressWasShown(transaction.sender)
if (transaction.sender !== transaction.recipient) {
RootStore.addressWasShown(transaction.recipient)
}
}
} else {
transaction = null transaction = null
} }
}
showAllAccounts: root.showAllAccounts showAllAccounts: root.showAllAccounts
sendModal: root.sendModal sendModal: root.sendModal
contactsStore: root.contactsStore contactsStore: root.contactsStore

View File

@ -367,6 +367,10 @@ ColumnLayout {
onTriggered: { onTriggered: {
if (!delegateMenu.transactionDelegate) if (!delegateMenu.transactionDelegate)
return 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()) RootStore.copyToClipboard(delegateMenu.transactionDelegate.getDetailsString())
} }
} }