chore(keycard): sync keycard with the current app state updated

This commit resolves a crash happened due to connection to
`SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT` when keycard sync
flow was run in the background.

Also updated the keycard synchronization process with the current state of
the application and is the first step of many which leads towards completion
of entire syncing feature.
This commit is contained in:
Sale Djenic 2023-02-21 09:25:02 +01:00 committed by saledjenic
parent cf5271cfea
commit c83794470b
21 changed files with 285 additions and 152 deletions

View File

@ -1,4 +1,4 @@
import NimQml, sequtils, sugar, chronicles, os, uuids import NimQml, sequtils, chronicles, os, uuids
import ../../app_service/service/general/service as general_service import ../../app_service/service/general/service as general_service
import ../../app_service/service/keychain/service as keychain_service import ../../app_service/service/keychain/service as keychain_service
@ -32,12 +32,11 @@ import ../../app_service/service/mailservers/service as mailservers_service
import ../../app_service/service/gif/service as gif_service import ../../app_service/service/gif/service as gif_service
import ../../app_service/service/ens/service as ens_service import ../../app_service/service/ens/service as ens_service
import ../../app_service/service/community_tokens/service as tokens_service import ../../app_service/service/community_tokens/service as tokens_service
import ../../app_service/common/account_constants
import ../modules/shared_modules/keycard_popup/module as keycard_shared_module
import ../modules/startup/module as startup_module import ../modules/startup/module as startup_module
import ../modules/main/module as main_module import ../modules/main/module as main_module
import ../core/notifications/notifications_manager import ../core/notifications/notifications_manager
import ../../constants as main_constants import ../../constants as main_constants
import ../global/global_singleton import ../global/global_singleton
@ -107,6 +106,7 @@ type
proc load(self: AppController) proc load(self: AppController)
proc buildAndRegisterLocalAccountSensitiveSettings(self: AppController) proc buildAndRegisterLocalAccountSensitiveSettings(self: AppController)
proc buildAndRegisterUserProfile(self: AppController) proc buildAndRegisterUserProfile(self: AppController)
proc tryKeycardSyncWithTheAppState(self: AppController)
# Startup Module Delegate Interface # Startup Module Delegate Interface
proc startupDidLoad*(self: AppController) proc startupDidLoad*(self: AppController)
@ -373,6 +373,7 @@ proc startupDidLoad*(self: AppController) =
self.startupModule.startUpUIRaised() self.startupModule.startUpUIRaised()
proc mainDidLoad*(self: AppController) = proc mainDidLoad*(self: AppController) =
self.tryKeycardSyncWithTheAppState()
self.startupModule.moveToAppState() self.startupModule.moveToAppState()
self.checkForStoringPasswordToKeychain() self.checkForStoringPasswordToKeychain()
@ -492,6 +493,7 @@ proc buildAndRegisterUserProfile(self: AppController) =
singletonInstance.engine.setRootContextProperty("userProfile", self.userProfileVariant) singletonInstance.engine.setRootContextProperty("userProfile", self.userProfileVariant)
proc tryKeycardSyncWithTheAppState(self: AppController) =
############################################################################## store def kc sync with app kc uid ############################################################################## store def kc sync with app kc uid
## Onboarding flows sync keycard state after login keypair | (inc. kp store) | update ## Onboarding flows sync keycard state after login keypair | (inc. kp store) | update
## `Im new to Status` -> `Generate new keys` -> na | na | na ## `Im new to Status` -> `Generate new keys` -> na | na | na
@ -513,56 +515,19 @@ proc buildAndRegisterUserProfile(self: AppController) =
## `Login` -> if card was unlocked via seed phrase -> no | no | yes ## `Login` -> if card was unlocked via seed phrase -> no | no | yes
## `Login` -> `Create replacement Keycard with seed phrase` -> no | yes | no (we don't know which kc is replaced if user has more kc for the same kp) ## `Login` -> `Create replacement Keycard with seed phrase` -> no | yes | no (we don't know which kc is replaced if user has more kc for the same kp)
############################################################################## ##############################################################################
if singletonInstance.userProfile.getIsKeycardUser(): if singletonInstance.userProfile.getIsKeycardUser() or
if self.storeDefaultKeyPair: self.syncKeycardBasedOnAppWalletState:
let allAccounts = self.walletAccountService.fetchAccounts() let data = SharedKeycarModuleArgs(
let defaultWalletAccounts = allAccounts.filter(a => pin: self.startupModule.getPin(),
a.walletType == WalletTypeDefaultStatusAccount and keyUid: singletonInstance.userProfile.getKeyUid()
a.path == account_constants.PATH_DEFAULT_WALLET and
not a.isChat and
a.isWallet
) )
if defaultWalletAccounts.len == 0: self.statusFoundation.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC, data)
error "default wallet account was not generated"
return
let defaultWalletAddress = defaultWalletAccounts[0].address
let keyPair = KeyPairDto(keycardUid: self.keycardService.getLastReceivedKeycardData().flowEvent.instanceUID,
keycardName: displayName,
keycardLocked: false,
accountsAddresses: @[defaultWalletAddress],
keyUid: loggedInAccount.keyUid)
discard self.walletAccountService.addMigratedKeyPair(keyPair)
if self.syncKeycardBasedOnAppWalletState:
let allAccounts = self.walletAccountService.fetchAccounts()
let accountsForLoggedInUser = allAccounts.filter(a => a.keyUid == loggedInAccount.keyUid)
var keyPair = KeyPairDto(keycardUid: "",
keycardName: displayName,
keycardLocked: false,
accountsAddresses: @[],
keyUid: loggedInAccount.keyUid)
var activeValidPathsToStoreToAKeycard: seq[string]
for acc in accountsForLoggedInUser:
activeValidPathsToStoreToAKeycard.add(acc.path)
keyPair.accountsAddresses.add(acc.address)
self.keycardService.startStoreMetadataFlow(displayName, self.startupModule.getPin(), activeValidPathsToStoreToAKeycard)
## sleep for 3 seconds, since that is more than enough to store metadata to a keycard, if the reader is still plugged in
## and the card is still inserted, otherwise we just skip that.
## At the moment we're not able to sync later keycard without metadata, cause such card doesn't return instance uid for
## loaded seed phrase, that's in the notes I am taking for discussion with keycard team. If they are able to provide
## instance uid for GetMetadata flow we will be able to use SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC signal for syncing
## otherwise we need to handle that way separatelly in `handleKeycardSyncing` of shared module
sleep(3000)
self.keycardService.cancelCurrentFlow()
let (_, kcEvent) = self.keycardService.getLastReceivedKeycardData()
if kcEvent.instanceUID.len > 0:
keyPair.keycardUid = kcEvent.instanceUID
discard self.walletAccountService.addMigratedKeyPair(keyPair)
if self.changedKeycardUids.len > 0: if self.changedKeycardUids.len > 0:
let oldUid = self.changedKeycardUids[0].oldKcUid let oldUid = self.changedKeycardUids[0].oldKcUid
let newUid = self.changedKeycardUids[^1].newKcUid let newUid = self.changedKeycardUids[^1].newKcUid
discard self.walletAccountService.updateKeycardUid(oldUid, newUid) discard self.walletAccountService.updateKeycardUid(oldUid, newUid)
discard self.walletAccountService.setKeycardUnlocked(loggedInAccount.keyUid, newUid) discard self.walletAccountService.setKeycardUnlocked(singletonInstance.userProfile.getKeyUid(), newUid)
proc storeDefaultKeyPairForNewKeycardUser*(self: AppController) = proc storeDefaultKeyPairForNewKeycardUser*(self: AppController) =
self.storeDefaultKeyPair = true self.storeDefaultKeyPair = true

View File

@ -11,6 +11,7 @@ import ../../../../app_service/service/contacts/dto/[contacts, status_update]
import ../../../../app_service/service/devices/dto/[device] import ../../../../app_service/service/devices/dto/[device]
import ../../../../app_service/service/settings/dto/[settings] import ../../../../app_service/service/settings/dto/[settings]
import ../../../../app_service/service/saved_address/[dto] import ../../../../app_service/service/saved_address/[dto]
import ../../../../app_service/service/wallet_account/[key_pair_dto]
type MessageSignal* = ref object of Signal type MessageSignal* = ref object of Signal
bookmarks*: seq[BookmarkDto] bookmarks*: seq[BookmarkDto]
@ -32,6 +33,8 @@ type MessageSignal* = ref object of Signal
clearedHistories*: seq[ClearedHistoryDto] clearedHistories*: seq[ClearedHistoryDto]
verificationRequests*: seq[VerificationRequest] verificationRequests*: seq[VerificationRequest]
savedAddresses*: seq[SavedAddressDto] savedAddresses*: seq[SavedAddressDto]
keycards*: seq[KeyPairDto]
keycardActions*: seq[KeycardActionDto]
type MessageDeliveredSignal* = ref object of Signal type MessageDeliveredSignal* = ref object of Signal
chatId*: string chatId*: string
@ -130,5 +133,13 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
for jsonSavedAddress in event["event"]["savedAddresses"]: for jsonSavedAddress in event["event"]["savedAddresses"]:
signal.savedAddresses.add(jsonSavedAddress.toSavedAddressDto()) signal.savedAddresses.add(jsonSavedAddress.toSavedAddressDto())
if event["event"]{"keycards"} != nil:
for jsonKc in event["event"]["keycards"]:
signal.keycards.add(jsonKc.toKeyPairDto())
if event["event"]{"keycardActions"} != nil:
for jsonKc in event["event"]["keycardActions"]:
signal.keycardActions.add(jsonKc.toKeycardActionDto())
result = signal result = signal

View File

@ -53,6 +53,12 @@ proc init*(self: Controller) =
return return
self.delegate.onNewKeycardSet(args.keyPair) self.delegate.onNewKeycardSet(args.keyPair)
self.events.on(SIGNAL_KEYCARDS_SYNCHRONIZED) do(e: Args):
let args = KeycardActivityArgs(e)
if not args.success:
return
self.delegate.onKeycardsSynchronized()
self.events.on(SIGNAL_KEYCARD_LOCKED) do(e: Args): self.events.on(SIGNAL_KEYCARD_LOCKED) do(e: Args):
let args = KeycardActivityArgs(e) let args = KeycardActivityArgs(e)
self.delegate.onKeycardLocked(args.keyPair.keyUid, args.keyPair.keycardUid) self.delegate.onKeycardLocked(args.keyPair.keyUid, args.keyPair.keycardUid)

View File

@ -66,6 +66,9 @@ method runCreateNewPairingCodePopup*(self: AccessInterface, keyUid: string) {.ba
method onLoggedInUserImageChanged*(self: AccessInterface) {.base.} = method onLoggedInUserImageChanged*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method onKeycardsSynchronized*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onNewKeycardSet*(self: AccessInterface, keyPair: KeyPairDto) {.base.} = method onNewKeycardSet*(self: AccessInterface, keyPair: KeyPairDto) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")

View File

@ -276,6 +276,9 @@ method onLoggedInUserImageChanged*(self: Module) =
return return
self.view.keycardDetailsModel().setImage(singletonInstance.userProfile.getPubKey(), singletonInstance.userProfile.getIcon()) self.view.keycardDetailsModel().setImage(singletonInstance.userProfile.getPubKey(), singletonInstance.userProfile.getIcon())
method onKeycardsSynchronized*(self: Module) =
self.buildKeycardList()
method onNewKeycardSet*(self: Module, keyPair: KeyPairDto) = method onNewKeycardSet*(self: Module, keyPair: KeyPairDto) =
let walletAccounts = self.controller.getWalletAccounts() let walletAccounts = self.controller.getWalletAccounts()
var mainViewItem = self.view.keycardModel().getItemForKeyUid(keyPair.keyUid) var mainViewItem = self.view.keycardModel().getItemForKeyUid(keyPair.keyUid)

View File

@ -150,6 +150,12 @@ method load*(self: Module) =
return return
self.refreshWalletAccounts() self.refreshWalletAccounts()
self.events.on(SIGNAL_KEYCARDS_SYNCHRONIZED) do(e: Args):
let args = KeycardActivityArgs(e)
if not args.success:
return
self.refreshWalletAccounts()
self.controller.init() self.controller.init()
self.view.load() self.view.load()

View File

@ -165,7 +165,7 @@ proc disconnectAll*(self: Controller) =
proc delete*(self: Controller) = proc delete*(self: Controller) =
self.disconnectAll() self.disconnectAll()
proc init*(self: Controller) = proc init*(self: Controller, fullConnect = true) =
self.connectKeycardReponseSignal() self.connectKeycardReponseSignal()
var handlerId = self.events.onWithUUID(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args): var handlerId = self.events.onWithUUID(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
@ -176,22 +176,23 @@ proc init*(self: Controller) =
self.delegate.onUserAuthenticated(args.password, args.pin) self.delegate.onUserAuthenticated(args.password, args.pin)
self.connectionIds.add(handlerId) self.connectionIds.add(handlerId)
handlerId = self.events.onWithUUID(SIGNAL_NEW_KEYCARD_SET) do(e: Args): if fullConnect:
let args = KeycardActivityArgs(e) handlerId = self.events.onWithUUID(SIGNAL_NEW_KEYCARD_SET) do(e: Args):
self.tmpAddingMigratedKeypairSuccess = args.success let args = KeycardActivityArgs(e)
self.delegate.onSecondaryActionClicked() self.tmpAddingMigratedKeypairSuccess = args.success
self.connectionIds.add(handlerId) self.delegate.onSecondaryActionClicked()
self.connectionIds.add(handlerId)
handlerId = self.events.onWithUUID(SIGNAL_CONVERTING_PROFILE_KEYPAIR) do(e: Args):
let args = ResultArgs(e) handlerId = self.events.onWithUUID(SIGNAL_CONVERTING_PROFILE_KEYPAIR) do(e: Args):
self.tmpConvertingProfileSuccess = args.success let args = ResultArgs(e)
self.delegate.onSecondaryActionClicked() self.tmpConvertingProfileSuccess = args.success
self.connectionIds.add(handlerId) self.delegate.onSecondaryActionClicked()
self.connectionIds.add(handlerId)
handlerId = self.events.onWithUUID(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args): handlerId = self.events.onWithUUID(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args):
let arg = TokensPerAccountArgs(e) let arg = TokensPerAccountArgs(e)
self.delegate.onTokensRebuilt(arg.accountsTokens) self.delegate.onTokensRebuilt(arg.accountsTokens)
self.connectionIds.add(handlerId) self.connectionIds.add(handlerId)
proc switchToWalletSection*(self: Controller) = proc switchToWalletSection*(self: Controller) =
let data = ActiveSectionChatArgs(sectionId: conf.WALLET_SECTION_ID) let data = ActiveSectionChatArgs(sectionId: conf.WALLET_SECTION_ID)
@ -661,11 +662,11 @@ proc setCurrentKeycardStateToUnlocked*(self: Controller, keyUid: string, keycard
if not self.walletAccountService.setKeycardUnlocked(keyUid, keycardUid): if not self.walletAccountService.setKeycardUnlocked(keyUid, keycardUid):
info "updating keycard unlocked state failed", keyUid=keyUid, keycardUid=keycardUid info "updating keycard unlocked state failed", keyUid=keyUid, keycardUid=keycardUid
proc updateKeycardName*(self: Controller, keyUid: string, keycardUid: string, keycardName: string): bool = proc updateKeycardName*(self: Controller, keycardUid: string, keycardName: string): bool =
if not serviceApplicable(self.walletAccountService): if not serviceApplicable(self.walletAccountService):
return false return false
if not self.walletAccountService.updateKeycardName(keyUid, keycardUid, keycardName): if not self.walletAccountService.updateKeycardName(keycardUid, keycardName):
info "updating keycard name failed", keyUid=keyUid, keycardUid=keycardUid, keycardName=keycardName info "updating keycard name failed", keycardUid=keycardUid, keycardName=keycardName
return false return false
return true return true

View File

@ -15,7 +15,7 @@ method executePrePrimaryStateCommand*(self: RenamingKeycardState, controller: Co
let md = controller.getMetadataFromKeycard() let md = controller.getMetadataFromKeycard()
let paths = md.walletAccounts.map(a => a.path) let paths = md.walletAccounts.map(a => a.path)
let name = controller.getKeyPairForProcessing().getName() let name = controller.getKeyPairForProcessing().getName()
self.success = controller.updateKeycardName(controller.getKeyPairForProcessing().getKeyUid(), controller.getKeycardUid(), name) self.success = controller.updateKeycardName(controller.getKeycardUid(), name)
if self.success: if self.success:
controller.runStoreMetadataFlow(name, controller.getPin(), paths) controller.runStoreMetadataFlow(name, controller.getPin(), paths)
else: else:

View File

@ -1,5 +1,3 @@
import os
type type
WrongSeedPhraseState* = ref object of State WrongSeedPhraseState* = ref object of State
verifiedSeedPhrase: bool verifiedSeedPhrase: bool

View File

@ -1,7 +1,7 @@
import NimQml, Tables, strformat, strutils import NimQml, Tables, strformat, strutils
import key_pair_account_item import key_pair_account_item
import ../../../../../app_service/common/account_constants import ../../../../../app_service/common/utils
export key_pair_account_item export key_pair_account_item
@ -82,10 +82,8 @@ QtObject:
proc containsPathOutOfTheDefaultStatusDerivationTree*(self: KeyPairAccountModel): bool = proc containsPathOutOfTheDefaultStatusDerivationTree*(self: KeyPairAccountModel): bool =
for it in self.items: for it in self.items:
if not it.getPath().startsWith(account_constants.PATH_WALLET_ROOT&"/") or if utils.isPathOutOfTheDefaultStatusDerivationTree(it.getPath()):
it.getPath().count("'") != 3 or return true
it.getPath().count("/") != 5:
return true
return false return false
proc getItemAtIndex*(self: KeyPairAccountModel, index: int): KeyPairAccountItem = proc getItemAtIndex*(self: KeyPairAccountModel, index: int): KeyPairAccountItem =

View File

@ -1,4 +1,4 @@
import NimQml, tables, random, strutils, marshal, sequtils, sugar, chronicles import NimQml, tables, random, strutils, sequtils, sugar, chronicles
import io_interface import io_interface
import view, controller import view, controller
@ -66,10 +66,10 @@ method delete*[T](self: Module[T]) =
self.viewVariant.delete self.viewVariant.delete
self.controller.delete self.controller.delete
proc init[T](self: Module[T]) = proc init[T](self: Module[T], fullConnect = true) =
if not self.initialized: if not self.initialized:
self.initialized = true self.initialized = true
self.controller.init() self.controller.init(fullConnect)
method getModuleAsVariant*[T](self: Module[T]): QVariant = method getModuleAsVariant*[T](self: Module[T]): QVariant =
return self.viewVariant return self.viewVariant
@ -241,13 +241,20 @@ proc handleKeycardSyncing[T](self: Module[T]) =
accountsAddresses: @[], accountsAddresses: @[],
keyUid: flowEvent.keyUid) keyUid: flowEvent.keyUid)
let alreadySetKeycards = self.controller.getAllKnownKeycards().filter(kp => kp.keycardUid == flowEvent.instanceUID) let alreadySetKeycards = self.controller.getAllKnownKeycards().filter(kp => kp.keycardUid == flowEvent.instanceUID)
if alreadySetKeycards.len == 1: if alreadySetKeycards.len <= 1:
var accountsToRemove = alreadySetKeycards[0].accountsAddresses var accountsToRemove: seq[string]
if alreadySetKeycards.len == 1:
accountsToRemove = alreadySetKeycards[0].accountsAddresses
let appAccounts = self.controller.getWalletAccounts() let appAccounts = self.controller.getWalletAccounts()
var activeValidPathsToStoreToAKeycard: seq[string] var activeValidPathsToStoreToAKeycard: seq[string]
var containsPathOutOfTheDefaultStatusDerivationTree = false
for appAcc in appAccounts: for appAcc in appAccounts:
if appAcc.keyUid != flowEvent.keyUid: if appAcc.keyUid != flowEvent.keyUid:
continue continue
# do not sync if any wallet's account has path out of the default Status derivation tree
if utils.isPathOutOfTheDefaultStatusDerivationTree(appAcc.path):
containsPathOutOfTheDefaultStatusDerivationTree = true
break
activeValidPathsToStoreToAKeycard.add(appAcc.path) activeValidPathsToStoreToAKeycard.add(appAcc.path)
var index = -1 var index = -1
var found = false var found = false
@ -264,24 +271,25 @@ proc handleKeycardSyncing[T](self: Module[T]) =
# we store to db only accounts we haven't stored before, accounts which are already on a keycard (in metadata) # we store to db only accounts we haven't stored before, accounts which are already on a keycard (in metadata)
# we assume they are already in the db # we assume they are already in the db
kpDto.accountsAddresses.add(appAcc.address) kpDto.accountsAddresses.add(appAcc.address)
if accountsToRemove.len > 0: if not containsPathOutOfTheDefaultStatusDerivationTree:
self.controller.removeMigratedAccountsForKeycard(kpDto.keyUid, kpDto.keycardUid, accountsToRemove) if accountsToRemove.len > 0:
if kpDto.accountsAddresses.len > 0: self.controller.removeMigratedAccountsForKeycard(kpDto.keyUid, kpDto.keycardUid, accountsToRemove)
self.controller.addMigratedKeyPair(kpDto) if kpDto.accountsAddresses.len > 0:
# if all accounts are removed from the app, there is no point in storing empty accounts list to a keycard, cause in that case self.controller.addMigratedKeyPair(kpDto)
# keypair which is on that keycard won't be known to the app, that means keypair was removed from the app # if all accounts are removed from the app, there is no point in storing empty accounts list to a keycard, cause in that case
if activeValidPathsToStoreToAKeycard.len > 0: # keypair which is on that keycard won't be known to the app, that means keypair was removed from the app
## we need to store paths to a keycard if the num of paths in the app and on a keycard is diffrent if activeValidPathsToStoreToAKeycard.len > 0:
## or if the paths are different ## we need to store paths to a keycard if the num of paths in the app and on a keycard is diffrent
var storeToKeycard = activeValidPathsToStoreToAKeycard.len != flowEvent.cardMetadata.walletAccounts.len ## or if the paths are different
if not storeToKeycard: var storeToKeycard = activeValidPathsToStoreToAKeycard.len != flowEvent.cardMetadata.walletAccounts.len
for wa in flowEvent.cardMetadata.walletAccounts: if not storeToKeycard:
if not utils.arrayContains(activeValidPathsToStoreToAKeycard, wa.path): for wa in flowEvent.cardMetadata.walletAccounts:
storeToKeycard = true if not utils.arrayContains(activeValidPathsToStoreToAKeycard, wa.path):
break storeToKeycard = true
if storeToKeycard: break
self.controller.runStoreMetadataFlow(flowEvent.cardMetadata.name, self.controller.getPin(), activeValidPathsToStoreToAKeycard) if storeToKeycard:
return self.controller.runStoreMetadataFlow(flowEvent.cardMetadata.name, self.controller.getPin(), activeValidPathsToStoreToAKeycard)
return
elif alreadySetKeycards.len > 1: elif alreadySetKeycards.len > 1:
error "it's impossible to have more then one keycard with the same uid", keycarUid=flowEvent.instanceUID error "it's impossible to have more then one keycard with the same uid", keycarUid=flowEvent.instanceUID
self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true) self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
@ -295,7 +303,7 @@ method syncKeycardBasedOnAppState*[T](self: Module[T], keyUid: string, pin: stri
if keyUid.len == 0: if keyUid.len == 0:
debug "cannot sync with the empty keyUid" debug "cannot sync with the empty keyUid"
return return
self.init() self.init(fullConnect = false)
self.controller.setKeyUidWhichIsBeingSyncing(keyUid) self.controller.setKeyUidWhichIsBeingSyncing(keyUid)
self.controller.setPin(pin) self.controller.setPin(pin)
self.controller.setKeycardSyncingInProgress(true) self.controller.setKeycardSyncingInProgress(true)

View File

@ -342,7 +342,7 @@ proc delayStartingApp[T](self: Module[T]) =
## - FlowType.FirstRunOldUserImportSeedPhrase ## - FlowType.FirstRunOldUserImportSeedPhrase
## - FlowType.FirstRunOldUserKeycardImport ## - FlowType.FirstRunOldUserKeycardImport
## we want to delay app start just to be sure that messages from waku will be received ## we want to delay app start just to be sure that messages from waku will be received
self.controller.connectToTimeoutEventAndStratTimer(timeoutInMilliseconds = 10000) # delay for 30 seconds self.controller.connectToTimeoutEventAndStratTimer(timeoutInMilliseconds = 30000) # delay for 30 seconds
method startAppAfterDelay*[T](self: Module[T]) = method startAppAfterDelay*[T](self: Module[T]) =
if not self.view.fetchingDataModel().allMessagesLoaded(): if not self.view.fetchingDataModel().allMessagesLoaded():

View File

@ -1,6 +1,6 @@
import json, random, times, strutils, sugar, os, re, chronicles import json, random, times, strutils, sugar, os, re, chronicles
import nimcrypto import nimcrypto
import signing_phrases import signing_phrases, account_constants
import ../../constants as main_constants import ../../constants as main_constants
@ -74,3 +74,10 @@ proc validateLink*(link: string): bool =
link, re"[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)", 0): link, re"[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)", 0):
error "Invalid social link", errDescription = link error "Invalid social link", errDescription = link
result = false result = false
proc isPathOutOfTheDefaultStatusDerivationTree*(path: string): bool =
if not path.startsWith(account_constants.PATH_WALLET_ROOT&"/") or
path.count("'") != 3 or
path.count("/") != 5:
return true
return false

View File

@ -140,14 +140,14 @@ type
const addMigratedKeyPairTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = const addMigratedKeyPairTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AddMigratedKeyPairTaskArg](argEncoded) let arg = decode[AddMigratedKeyPairTaskArg](argEncoded)
try: try:
let response = backend.addMigratedKeyPair( let response = backend.addMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(
arg.keyPair.keycardUid, arg.keyPair.keycardUid,
arg.keyPair.keycardName, arg.keyPair.keycardName,
arg.keyPair.keyUid, arg.keyPair.keyUid,
arg.keyPair.accountsAddresses, arg.keyPair.accountsAddresses,
arg.password arg.password
) )
let success = responseHasNoErrors("addMigratedKeyPair", response) let success = responseHasNoErrors("addMigratedKeyPairOrAddAccountsIfKeyPairIsAdded", response)
let responseJson = %*{ let responseJson = %*{
"success": success, "success": success,
"keyPair": arg.keyPair.toJsonNode() "keyPair": arg.keyPair.toJsonNode()

View File

@ -2,12 +2,23 @@ import json
include ../../common/json_utils include ../../common/json_utils
const KeycardUid = "keycard-uid" const ParamKeycardUid = "keycard-uid"
const KeycardName = "keycard-name" const ParamKeycardName = "keycard-name"
const KeycardLocked = "keycard-locked" const ParamKeycardLocked = "keycard-locked"
const KeyUid = "key-uid" const ParamKeyUid = "key-uid"
const AccountAddresses = "accounts-addresses" const ParamAccountAddresses = "accounts-addresses"
const ParamAction = "action"
const ParamOldKeycardUid = "old-keycard-uid"
const ParamKeycard = "keycard"
const KeycardActionKeycardAdded* = "KEYCARD_ADDED"
const KeycardActionAccountsAdded* = "ACCOUNTS_ADDED"
const KeycardActionKeycardDeleted* = "KEYCARD_DELETED"
const KeycardActionAccountsRemoved* = "ACCOUNTS_REMOVED"
const KeycardActionLocked* = "LOCKED"
const KeycardActionUnlocked* = "UNLOCKED"
const KeycardActionUidUpdated* = "UID_UPDATED"
const KeycardActionNameChanged* = "NAME_CHANGED"
type KeyPairDto* = object type KeyPairDto* = object
keycardUid*: string keycardUid*: string
@ -16,23 +27,37 @@ type KeyPairDto* = object
accountsAddresses*: seq[string] accountsAddresses*: seq[string]
keyUid*: string keyUid*: string
type KeycardActionDto* = object
action*: string
oldKeycardUid*: string
keycard*: KeyPairDto
proc toKeyPairDto*(jsonObj: JsonNode): KeyPairDto = proc toKeyPairDto*(jsonObj: JsonNode): KeyPairDto =
result = KeyPairDto() result = KeyPairDto()
discard jsonObj.getProp(KeycardUid, result.keycardUid) discard jsonObj.getProp(ParamKeycardUid, result.keycardUid)
discard jsonObj.getProp(KeycardName, result.keycardName) discard jsonObj.getProp(ParamKeycardName, result.keycardName)
discard jsonObj.getProp(KeycardLocked, result.keycardLocked) discard jsonObj.getProp(ParamKeycardLocked, result.keycardLocked)
discard jsonObj.getProp(KeyUid, result.keyUid) discard jsonObj.getProp(ParamKeyUid, result.keyUid)
var jArr: JsonNode var jArr: JsonNode
if(jsonObj.getProp(AccountAddresses, jArr) and jArr.kind == JArray): if(jsonObj.getProp(ParamAccountAddresses, jArr) and jArr.kind == JArray):
for addrObj in jArr: for addrObj in jArr:
result.accountsAddresses.add(addrObj.getStr) result.accountsAddresses.add(addrObj.getStr)
proc toKeycardActionDto*(jsonObj: JsonNode): KeycardActionDto =
result = KeycardActionDto()
discard jsonObj.getProp(ParamAction, result.action)
discard jsonObj.getProp(ParamOldKeycardUid, result.oldKeycardUid)
var keycardObj: JsonNode
if(jsonObj.getProp("keycard", keycardObj)):
result.keycard = toKeyPairDto(keycardObj)
proc toJsonNode*(self: KeyPairDto): JsonNode = proc toJsonNode*(self: KeyPairDto): JsonNode =
result = %* { result = %* {
KeycardUid: self.keycardUid, ParamKeycardUid: self.keycardUid,
KeycardName: self.keycardName, ParamKeycardName: self.keycardName,
KeycardLocked: self.keycardLocked, ParamKeycardLocked: self.keycardLocked,
KeyUid: self.keyUid, ParamKeyUid: self.keyUid,
AccountAddresses: self.accountsAddresses ParamAccountAddresses: self.accountsAddresses
} }

View File

@ -34,7 +34,9 @@ const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_READY* = "walletAccount/derivedAddre
const SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT* = "walletAccount/tokensRebuilt" const SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT* = "walletAccount/tokensRebuilt"
const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_DETAILS_FETCHED* = "walletAccount/derivedAddressDetailsFetched" const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_DETAILS_FETCHED* = "walletAccount/derivedAddressDetailsFetched"
const SIGNAL_KEYCARDS_SYNCHRONIZED* = "keycardsSynchronized"
const SIGNAL_NEW_KEYCARD_SET* = "newKeycardSet" const SIGNAL_NEW_KEYCARD_SET* = "newKeycardSet"
const SIGNAL_KEYCARD_DELETED* = "keycardDeleted"
const SIGNAL_KEYCARD_ACCOUNTS_REMOVED* = "keycardAccountsRemoved" const SIGNAL_KEYCARD_ACCOUNTS_REMOVED* = "keycardAccountsRemoved"
const SIGNAL_KEYCARD_LOCKED* = "keycardLocked" const SIGNAL_KEYCARD_LOCKED* = "keycardLocked"
const SIGNAL_KEYCARD_UNLOCKED* = "keycardUnlocked" const SIGNAL_KEYCARD_UNLOCKED* = "keycardUnlocked"
@ -124,6 +126,8 @@ QtObject:
proc checkRecentHistory*(self: Service) proc checkRecentHistory*(self: Service)
proc checkConnected(self: Service) proc checkConnected(self: Service)
proc startWallet(self: Service) proc startWallet(self: Service)
proc handleKeycardActions(self: Service, keycardActions: seq[KeycardActionDto])
proc handleKeycardsState(self: Service, keycardsState: seq[KeyPairDto])
proc delete*(self: Service) = proc delete*(self: Service) =
self.closingApp = true self.closingApp = true
@ -227,6 +231,9 @@ QtObject:
if settingsField.name == KEY_CURRENCY: if settingsField.name == KEY_CURRENCY:
self.events.emit(SIGNAL_WALLET_ACCOUNT_CURRENCY_UPDATED, CurrencyUpdated()) self.events.emit(SIGNAL_WALLET_ACCOUNT_CURRENCY_UPDATED, CurrencyUpdated())
self.handleKeycardsState(receivedData.keycards)
self.handleKeycardActions(receivedData.keycardActions)
self.events.on(SignalType.Wallet.event) do(e:Args): self.events.on(SignalType.Wallet.event) do(e:Args):
var data = WalletSignal(e) var data = WalletSignal(e)
case data.eventType: case data.eventType:
@ -614,24 +621,33 @@ QtObject:
) )
self.threadpool.start(arg) self.threadpool.start(arg)
proc emitAddKeycardAddAccountsChange(self: Service, success: bool, keyPair: KeyPairDto) =
let data = KeycardActivityArgs(
success: success,
keyPair: keyPair
)
self.events.emit(SIGNAL_NEW_KEYCARD_SET, data)
proc onMigratedKeyPairAdded*(self: Service, response: string) {.slot.} = proc onMigratedKeyPairAdded*(self: Service, response: string) {.slot.} =
var data = KeycardActivityArgs()
data.success = false
try: try:
let responseObj = response.parseJson let responseObj = response.parseJson
discard responseObj.getProp("success", data.success) var keyPair: KeyPairDto
var kpJson: JsonNode var success = false
if responseObj.getProp("keyPair", kpJson): discard responseObj.getProp("success", success)
data.keyPair = kpJson.toKeyPairDto() if success:
var kpJson: JsonNode
if responseObj.getProp("keyPair", kpJson):
keyPair = kpJson.toKeyPairDto()
self.emitAddKeycardAddAccountsChange(success, keyPair)
except Exception as e: except Exception as e:
error "error handilng migrated keypair response", errDesription=e.msg error "error handilng migrated keypair response", errDesription=e.msg
self.events.emit(SIGNAL_NEW_KEYCARD_SET, data) self.emitAddKeycardAddAccountsChange(success = false, KeyPairDto())
proc addMigratedKeyPair*(self: Service, keyPair: KeyPairDto, password = ""): bool = proc addMigratedKeyPair*(self: Service, keyPair: KeyPairDto, password = ""): bool =
# Providing a password corresponding local keystore file will be removed as well, though # Providing a password corresponding local keystore file will be removed as well, though
# in some contexts we just need to add keypair to the db, so password is not needed. # in some contexts we just need to add keypair to the db, so password is not needed.
try: try:
let response = backend.addMigratedKeyPair( let response = backend.addMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(
keyPair.keycardUid, keyPair.keycardUid,
keyPair.keycardName, keyPair.keycardName,
keyPair.keyUid, keyPair.keyUid,
@ -640,7 +656,7 @@ QtObject:
) )
result = responseHasNoErrors("addMigratedKeyPair", response) result = responseHasNoErrors("addMigratedKeyPair", response)
if result: if result:
self.events.emit(SIGNAL_NEW_KEYCARD_SET, KeycardActivityArgs(success: true, keyPair: keyPair)) self.emitAddKeycardAddAccountsChange(success = true, keyPair)
except Exception as e: except Exception as e:
error "error: ", procName="addMigratedKeyPair", errName = e.name, errDesription = e.msg error "error: ", procName="addMigratedKeyPair", errName = e.name, errDesription = e.msg
@ -653,18 +669,28 @@ QtObject:
) )
self.threadpool.start(arg) self.threadpool.start(arg)
proc emitKeycardRemovedAccountsChange(self: Service, success: bool, keyUid: string, keycardUid: string,
removedAccounts: seq[string]) =
let data = KeycardActivityArgs(
success: success,
keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid, accountsAddresses: removedAccounts)
)
self.events.emit(SIGNAL_KEYCARD_ACCOUNTS_REMOVED, data)
proc onMigratedAccountsForKeycardRemoved*(self: Service, response: string) {.slot.} = proc onMigratedAccountsForKeycardRemoved*(self: Service, response: string) {.slot.} =
var data = KeycardActivityArgs()
data.success = false
try: try:
let responseObj = response.parseJson let responseObj = response.parseJson
discard responseObj.getProp("success", data.success) var keyPair: KeyPairDto
var kpJson: JsonNode var success = false
if responseObj.getProp("keyPair", kpJson): discard responseObj.getProp("success", success)
data.keyPair = kpJson.toKeyPairDto() if success:
var kpJson: JsonNode
if responseObj.getProp("keyPair", kpJson):
keyPair = kpJson.toKeyPairDto()
self.emitKeycardRemovedAccountsChange(success, keyPair.keyUid, keyPair.keycardUid, keyPair.accountsAddresses)
except Exception as e: except Exception as e:
error "error handilng migrated keypair response", errDesription=e.msg error "error handilng migrated keypair response", errDesription=e.msg
self.events.emit(SIGNAL_KEYCARD_ACCOUNTS_REMOVED, data) self.emitKeycardRemovedAccountsChange(success = false, keyUid = "", keycardUid = "", removedAccounts = @[])
proc getAllKnownKeycards*(self: Service): seq[KeyPairDto] = proc getAllKnownKeycards*(self: Service): seq[KeyPairDto] =
try: try:
@ -674,6 +700,16 @@ QtObject:
except Exception as e: except Exception as e:
error "error: ", procName="getAllKnownKeycards", errName = e.name, errDesription = e.msg error "error: ", procName="getAllKnownKeycards", errName = e.name, errDesription = e.msg
proc getKeycardWithKeycardUid*(self: Service, keycardUid: string): KeyPairDto =
let allKnownKeycards = self.getAllKnownKeycards()
let keycardsWithKeycardUid = allKnownKeycards.filter(kp => kp.keycardUid == keycardUid)
if keycardsWithKeycardUid.len == 0:
return
if keycardsWithKeycardUid.len > 1:
error "there are more than one keycard with the same uid", keycardUid=keycardUid
return
return keycardsWithKeycardUid[0]
proc getAllMigratedKeyPairs*(self: Service): seq[KeyPairDto] = proc getAllMigratedKeyPairs*(self: Service): seq[KeyPairDto] =
try: try:
let response = backend.getAllMigratedKeyPairs() let response = backend.getAllMigratedKeyPairs()
@ -690,50 +726,68 @@ QtObject:
except Exception as e: except Exception as e:
error "error: ", procName="getMigratedKeyPairByKeyUid", errName = e.name, errDesription = e.msg error "error: ", procName="getMigratedKeyPairByKeyUid", errName = e.name, errDesription = e.msg
proc updateKeycardName*(self: Service, keyUid: string, keycardUid: string, name: string): bool = proc emitKeycardNameChange(self: Service, keycardUid: string, name: string) =
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keycardUid: keycardUid, keycardName: name))
self.events.emit(SIGNAL_KEYCARD_NAME_CHANGED, data)
proc updateKeycardName*(self: Service, keycardUid: string, name: string): bool =
try: try:
let response = backend.setKeycardName(keycardUid, name) let response = backend.setKeycardName(keycardUid, name)
result = responseHasNoErrors("updateKeycardName", response) result = responseHasNoErrors("updateKeycardName", response)
if result: if result:
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid, keycardName: name)) self.emitKeycardNameChange(keycardUid, name)
self.events.emit(SIGNAL_KEYCARD_NAME_CHANGED, data)
except Exception as e: except Exception as e:
error "error: ", procName="updateKeycardName", errName = e.name, errDesription = e.msg error "error: ", procName="updateKeycardName", errName = e.name, errDesription = e.msg
proc emitKeycardLockedChange(self: Service, keyUid: string, keycardUid: string) =
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_LOCKED, data)
proc setKeycardLocked*(self: Service, keyUid: string, keycardUid: string): bool = proc setKeycardLocked*(self: Service, keyUid: string, keycardUid: string): bool =
try: try:
let response = backend.keycardLocked(keycardUid) let response = backend.keycardLocked(keycardUid)
result = responseHasNoErrors("setKeycardLocked", response) result = responseHasNoErrors("setKeycardLocked", response)
if result: if result:
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid)) self.emitKeycardLockedChange(keyUid, keycardUid)
self.events.emit(SIGNAL_KEYCARD_LOCKED, data)
except Exception as e: except Exception as e:
error "error: ", procName="setKeycardLocked", errName = e.name, errDesription = e.msg error "error: ", procName="setKeycardLocked", errName = e.name, errDesription = e.msg
proc emitKeycardUnlockedChange(self: Service, keyUid: string, keycardUid: string) =
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_UNLOCKED, data)
proc setKeycardUnlocked*(self: Service, keyUid: string, keycardUid: string): bool = proc setKeycardUnlocked*(self: Service, keyUid: string, keycardUid: string): bool =
try: try:
let response = backend.keycardUnlocked(keycardUid) let response = backend.keycardUnlocked(keycardUid)
result = responseHasNoErrors("setKeycardUnlocked", response) result = responseHasNoErrors("setKeycardUnlocked", response)
if result: if result:
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid)) self.emitKeycardUnlockedChange(keyUid, keycardUid)
self.events.emit(SIGNAL_KEYCARD_UNLOCKED, data)
except Exception as e: except Exception as e:
error "error: ", procName="setKeycardUnlocked", errName = e.name, errDesription = e.msg error "error: ", procName="setKeycardUnlocked", errName = e.name, errDesription = e.msg
proc emitUpdateKeycardUidChange(self: Service, oldKeycardUid: string, newKeycardUid: string) =
let data = KeycardActivityArgs(success: true, oldKeycardUid: oldKeycardUid, keyPair: KeyPairDto(keycardUid: newKeycardUid))
self.events.emit(SIGNAL_KEYCARD_UID_UPDATED, data)
proc updateKeycardUid*(self: Service, oldKeycardUid: string, newKeycardUid: string): bool = proc updateKeycardUid*(self: Service, oldKeycardUid: string, newKeycardUid: string): bool =
try: try:
let response = backend.updateKeycardUID(oldKeycardUid, newKeycardUid) let response = backend.updateKeycardUID(oldKeycardUid, newKeycardUid)
result = responseHasNoErrors("updateKeycardUid", response) result = responseHasNoErrors("updateKeycardUid", response)
if result: if result:
let data = KeycardActivityArgs(success: true, oldKeycardUid: oldKeycardUid, keyPair: KeyPairDto(keycardUid: newKeycardUid)) self.emitUpdateKeycardUidChange(oldKeycardUid, newKeycardUid)
self.events.emit(SIGNAL_KEYCARD_UID_UPDATED, data)
except Exception as e: except Exception as e:
error "error: ", procName="updateKeycardUid", errName = e.name, errDesription = e.msg error "error: ", procName="updateKeycardUid", errName = e.name, errDesription = e.msg
proc emitDeleteKeycardChange(self: Service, keycardUid: string) =
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_DELETED, data)
proc deleteKeycard*(self: Service, keycardUid: string): bool = proc deleteKeycard*(self: Service, keycardUid: string): bool =
try: try:
let response = backend.deleteKeycard(keycardUid) let response = backend.deleteKeycard(keycardUid)
return responseHasNoErrors("deleteKeycard", response) result = responseHasNoErrors("deleteKeycard", response)
if result:
self.emitDeleteKeycardChange(keycardUid)
except Exception as e: except Exception as e:
error "error: ", procName="deleteKeycard", errName = e.name, errDesription = e.msg error "error: ", procName="deleteKeycard", errName = e.name, errDesription = e.msg
return false return false
@ -743,4 +797,35 @@ QtObject:
result = self.addOrReplaceWalletAccount(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid, result = self.addOrReplaceWalletAccount(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid,
accountType, color, emoji) accountType, color, emoji)
if result.len == 0: if result.len == 0:
self.addNewAccountToLocalStore() self.addNewAccountToLocalStore()
proc handleKeycardActions(self: Service, keycardActions: seq[KeycardActionDto]) =
if keycardActions.len == 0:
return
for kcAction in keycardActions:
if kcAction.action == KeycardActionKeycardAdded or
kcAction.action == KeycardActionAccountsAdded:
self.emitAddKeycardAddAccountsChange(success = true, kcAction.keycard)
elif kcAction.action == KeycardActionKeycardDeleted:
self.emitDeleteKeycardChange(kcAction.keycard.keycardUid)
elif kcAction.action == KeycardActionAccountsRemoved:
let keycard = self.getKeycardWithKeycardUid(kcAction.keycard.keycardUid)
self.emitKeycardRemovedAccountsChange(success = true, keycard.keyUid, kcAction.keycard.keycardUid, kcAction.keycard.accountsAddresses)
elif kcAction.action == KeycardActionLocked:
let keycard = self.getKeycardWithKeycardUid(kcAction.keycard.keycardUid)
self.emitKeycardLockedChange(keycard.keyUid, kcAction.keycard.keycardUid)
elif kcAction.action == KeycardActionUnlocked:
let keycard = self.getKeycardWithKeycardUid(kcAction.keycard.keycardUid)
self.emitKeycardUnlockedChange(keycard.keyUid, kcAction.keycard.keycardUid)
elif kcAction.action == KeycardActionUidUpdated:
self.emitUpdateKeycardUidChange(kcAction.oldKeycardUid, kcAction.keycard.keycardUid)
elif kcAction.action == KeycardActionNameChanged:
self.emitKeycardNameChange(kcAction.keycard.keycardUid, kcAction.keycard.keycardName)
else:
error "unsupported action received", action=kcAction.action
proc handleKeycardsState(self: Service, keycardsState: seq[KeyPairDto]) =
if keycardsState.len == 0:
return
let data = KeycardActivityArgs(success: true)
self.events.emit(SIGNAL_KEYCARDS_SYNCHRONIZED, data)

View File

@ -239,12 +239,12 @@ rpc(fetchMarketValues, "wallet"):
rpc(fetchTokenDetails, "wallet"): rpc(fetchTokenDetails, "wallet"):
symbols: seq[string] symbols: seq[string]
rpc(addMigratedKeyPair, "accounts"): rpc(addMigratedKeyPairOrAddAccountsIfKeyPairIsAdded, "accounts"):
keycardUid: string keycardUid: string
keyPairName: string keyPairName: string
keyUid: string keyUid: string
accountAddresses: seq[string] accountAddresses: seq[string]
keyStoreDir: string password: string
rpc(removeMigratedAccountsForKeycard, "accounts"): rpc(removeMigratedAccountsForKeycard, "accounts"):
keycardUid: string keycardUid: string

View File

@ -84,6 +84,10 @@ SettingsContentBase {
Layout.preferredWidth: root.contentWidth Layout.preferredWidth: root.contentWidth
keycardStore: root.keycardStore keycardStore: root.keycardStore
keyUid: d.observedKeyUid keyUid: d.observedKeyUid
onChangeSectionTitle: {
root.sectionTitle = title
}
} }
Connections { Connections {

View File

@ -18,11 +18,20 @@ ColumnLayout {
property KeycardStore keycardStore property KeycardStore keycardStore
property string keyUid: "" property string keyUid: ""
signal changeSectionTitle(string title)
spacing: Constants.settingsSection.itemSpacing spacing: Constants.settingsSection.itemSpacing
QtObject { QtObject {
id: d id: d
property bool collapsed: true property bool collapsed: true
function checkAndCheckTitleIfNeeded(newKeycardName) {
// We change title if there is only a single keycard for a keypair in keycard details view
if (root.keycardStore.keycardModule.keycardDetailsModel.count === 1) {
root.changeSectionTitle(newKeycardName)
}
}
} }
StatusListView { StatusListView {
@ -42,6 +51,10 @@ ColumnLayout {
keyPairIcon: model.keycard.icon keyPairIcon: model.keycard.icon
keyPairImage: model.keycard.image keyPairImage: model.keycard.image
keyPairAccounts: model.keycard.accounts keyPairAccounts: model.keycard.accounts
onKeycardNameChanged: {
d.checkAndCheckTitleIfNeeded(keycardName)
}
} }
} }

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit d0cc036d486092262382d45967db11f19d66641e Subproject commit 2d16e7b8910f40070086f3966ce8f9eb55ee8223

@ -1 +1 @@
Subproject commit b50cfe22ac3802d508e34b0561095c7100f6efa8 Subproject commit 5bfafd14e6361c551cfb55b527448de8e4646e83