fix(@desktop/keycard): migrating keypair looks somehow stucked for a while before switching to `Migrating key pair to Keycard` state

Fixes: #8177
This commit is contained in:
Sale Djenic 2022-11-14 11:24:16 +01:00 committed by saledjenic
parent db7769b072
commit 557703543c
11 changed files with 232 additions and 62 deletions

View File

@ -138,7 +138,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.nodeConfigurationService = node_configuration_service.newService(statusFoundation.fleetConfiguration, result.nodeConfigurationService = node_configuration_service.newService(statusFoundation.fleetConfiguration,
result.settingsService) result.settingsService)
result.keychainService = keychain_service.newService(statusFoundation.events) result.keychainService = keychain_service.newService(statusFoundation.events)
result.accountsService = accounts_service.newService(statusFoundation.fleetConfiguration) result.accountsService = accounts_service.newService(statusFoundation.events, statusFoundation.threadpool,
statusFoundation.fleetConfiguration)
result.networkService = network_service.newService(statusFoundation.events, result.settingsService) result.networkService = network_service.newService(statusFoundation.events, result.settingsService)
result.contactsService = contacts_service.newService( result.contactsService = contacts_service.newService(
statusFoundation.events, statusFoundation.threadpool, result.networkService, result.settingsService, statusFoundation.events, statusFoundation.threadpool, result.networkService, result.settingsService,

View File

@ -49,6 +49,8 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_NEW_KEYCARD_SET) do(e: Args): self.events.on(SIGNAL_NEW_KEYCARD_SET) do(e: Args):
let args = KeycardActivityArgs(e) let args = KeycardActivityArgs(e)
if not args.success:
return
self.delegate.onNewKeycardSet(args.keyPair) self.delegate.onNewKeycardSet(args.keyPair)
self.events.on(SIGNAL_KEYCARD_LOCKED) do(e: Args): self.events.on(SIGNAL_KEYCARD_LOCKED) do(e: Args):

View File

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

View File

@ -51,6 +51,8 @@ type
tmpUsePinFromBiometrics: bool tmpUsePinFromBiometrics: bool
tmpOfferToStoreUpdatedPinToKeychain: bool tmpOfferToStoreUpdatedPinToKeychain: bool
tmpKeycardUid: string tmpKeycardUid: string
tmpAddingMigratedKeypairSuccess: bool
tmpConvertingProfileSuccess: bool
proc newController*(delegate: io_interface.AccessInterface, proc newController*(delegate: io_interface.AccessInterface,
uniqueIdentifier: string, uniqueIdentifier: string,
@ -78,6 +80,8 @@ proc newController*(delegate: io_interface.AccessInterface,
result.tmpSeedPhraseLength = 0 result.tmpSeedPhraseLength = 0
result.tmpSelectedKeyPairIsProfile = false result.tmpSelectedKeyPairIsProfile = false
result.tmpUsePinFromBiometrics = false result.tmpUsePinFromBiometrics = false
result.tmpAddingMigratedKeypairSuccess = false
result.tmpConvertingProfileSuccess = false
proc serviceApplicable[T](service: T): bool = proc serviceApplicable[T](service: T): bool =
if not service.isNil: if not service.isNil:
@ -141,6 +145,16 @@ 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)
self.events.on(SIGNAL_NEW_KEYCARD_SET) do(e: Args):
let args = KeycardActivityArgs(e)
self.tmpAddingMigratedKeypairSuccess = args.success
self.delegate.onSecondaryActionClicked()
self.events.on(SIGNAL_CONVERTING_PROFILE_KEYPAIR) do(e: Args):
let args = ResultArgs(e)
self.tmpConvertingProfileSuccess = args.success
self.delegate.onSecondaryActionClicked()
proc getKeycardData*(self: Controller): string = proc getKeycardData*(self: Controller): string =
return self.delegate.getKeycardData() return self.delegate.getKeycardData()
@ -289,15 +303,18 @@ proc verifyPassword*(self: Controller, password: string): bool =
return return
return self.accountsService.verifyPassword(password) return self.accountsService.verifyPassword(password)
proc convertSelectedKeyPairToKeycardAccount*(self: Controller, password: string): bool = proc convertSelectedKeyPairToKeycardAccount*(self: Controller, password: string) =
if not serviceApplicable(self.accountsService): if not serviceApplicable(self.accountsService):
return return
let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true) let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true)
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW) singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW)
return self.accountsService.convertToKeycardAccount(self.tmpSelectedKeyPairDto.keyUid, self.accountsService.convertToKeycardAccount(self.tmpSelectedKeyPairDto.keyUid,
currentPassword = password, currentPassword = password,
newPassword = acc.derivedAccounts.encryption.publicKey) newPassword = acc.derivedAccounts.encryption.publicKey)
proc getConvertingProfileSuccess*(self: Controller): bool =
return self.tmpConvertingProfileSuccess
proc getLoggedInAccount*(self: Controller): AccountDto = proc getLoggedInAccount*(self: Controller): AccountDto =
if not serviceApplicable(self.accountsService): if not serviceApplicable(self.accountsService):
return return
@ -429,8 +446,8 @@ proc terminateCurrentFlow*(self: Controller, lastStepInTheCurrentFlow: bool) =
let (_, flowEvent) = self.getLastReceivedKeycardData() let (_, flowEvent) = self.getLastReceivedKeycardData()
var data = SharedKeycarModuleFlowTerminatedArgs(uniqueIdentifier: self.uniqueIdentifier, var data = SharedKeycarModuleFlowTerminatedArgs(uniqueIdentifier: self.uniqueIdentifier,
lastStepInTheCurrentFlow: lastStepInTheCurrentFlow) lastStepInTheCurrentFlow: lastStepInTheCurrentFlow)
let exportedEncryptionPubKey = flowEvent.generatedWalletAccount.publicKey
if lastStepInTheCurrentFlow: if lastStepInTheCurrentFlow:
let exportedEncryptionPubKey = flowEvent.generatedWalletAccount.publicKey
data.password = if exportedEncryptionPubKey.len > 0: exportedEncryptionPubKey else: self.getPassword() data.password = if exportedEncryptionPubKey.len > 0: exportedEncryptionPubKey else: self.getPassword()
data.pin = self.getPin() data.pin = self.getPin()
data.keyUid = flowEvent.keyUid data.keyUid = flowEvent.keyUid
@ -452,13 +469,16 @@ proc getBalanceForAddress*(self: Controller, address: string): float64 =
return return
return self.walletAccountService.fetchBalanceForAddress(address) return self.walletAccountService.fetchBalanceForAddress(address)
proc addMigratedKeyPair*(self: Controller, keyPair: KeyPairDto): bool = proc addMigratedKeyPair*(self: Controller, keyPair: KeyPairDto) =
if not serviceApplicable(self.walletAccountService): if not serviceApplicable(self.walletAccountService):
return return
if not serviceApplicable(self.accountsService): if not serviceApplicable(self.accountsService):
return return
let keystoreDir = self.accountsService.getKeyStoreDir() let keystoreDir = self.accountsService.getKeyStoreDir()
return self.walletAccountService.addMigratedKeyPair(keyPair, keystoreDir) self.walletAccountService.addMigratedKeyPairAsync(keyPair, keystoreDir)
proc getAddingMigratedKeypairSuccess*(self: Controller): bool =
return self.tmpAddingMigratedKeypairSuccess
proc getAllMigratedKeyPairs*(self: Controller): seq[KeyPairDto] = proc getAllMigratedKeyPairs*(self: Controller): seq[KeyPairDto] =
if not serviceApplicable(self.walletAccountService): if not serviceApplicable(self.walletAccountService):

View File

@ -1,33 +1,36 @@
type type
MigratingKeyPairState* = ref object of State MigratingKeyPairState* = ref object of State
migrationSuccess: bool authenticationDone: bool
authenticationOk: bool
addingMigratedKeypairDone: bool
addingMigratedKeypairOk: bool
profileConversionDone: bool
profileConversionOk: bool
proc newMigratingKeyPairState*(flowType: FlowType, backState: State): MigratingKeyPairState = proc newMigratingKeyPairState*(flowType: FlowType, backState: State): MigratingKeyPairState =
result = MigratingKeyPairState() result = MigratingKeyPairState()
result.setup(flowType, StateType.MigratingKeyPair, backState) result.setup(flowType, StateType.MigratingKeyPair, backState)
result.migrationSuccess = false result.authenticationDone = false
result.authenticationOk = false
result.addingMigratedKeypairDone = false
result.addingMigratedKeypairOk = false
result.profileConversionDone = false
result.profileConversionOk = false
proc delete*(self: MigratingKeyPairState) = proc delete*(self: MigratingKeyPairState) =
self.State.delete self.State.delete
proc doMigration(self: MigratingKeyPairState, controller: Controller) = proc doMigration(self: MigratingKeyPairState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard: let selectedKeyPairDto = controller.getSelectedKeyPairDto()
let password = controller.getPassword() controller.addMigratedKeyPair(selectedKeyPairDto)
controller.setPassword("")
if controller.getSelectedKeyPairIsProfile(): proc doConversion(self: MigratingKeyPairState, controller: Controller) =
self.migrationSuccess = controller.verifyPassword(password) let password = controller.getPassword()
if not self.migrationSuccess: controller.convertSelectedKeyPairToKeycardAccount(password)
return
let selectedKeyPairDto = controller.getSelectedKeyPairDto() proc runStoreMetadataFlow(self: MigratingKeyPairState, controller: Controller) =
self.migrationSuccess = controller.addMigratedKeyPair(selectedKeyPairDto) let selectedKeyPairDto = controller.getSelectedKeyPairDto()
if not self.migrationSuccess: controller.runStoreMetadataFlow(selectedKeyPairDto.keycardName, controller.getPin(), controller.getSelectedKeyPairWalletPaths())
return
if controller.getSelectedKeyPairIsProfile():
self.migrationSuccess = self.migrationSuccess and controller.convertSelectedKeyPairToKeycardAccount(password)
if not self.migrationSuccess:
return
controller.runStoreMetadataFlow(selectedKeyPairDto.keycardName, controller.getPin(),
controller.getSelectedKeyPairWalletPaths())
method executePrePrimaryStateCommand*(self: MigratingKeyPairState, controller: Controller) = method executePrePrimaryStateCommand*(self: MigratingKeyPairState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard: if self.flowType == FlowType.SetupNewKeycard:
@ -37,13 +40,40 @@ method executePrePrimaryStateCommand*(self: MigratingKeyPairState, controller: C
self.doMigration(controller) self.doMigration(controller)
method executePreSecondaryStateCommand*(self: MigratingKeyPairState, controller: Controller) = method executePreSecondaryStateCommand*(self: MigratingKeyPairState, controller: Controller) =
## Secondary action is called after each async action during migration process.
if self.flowType == FlowType.SetupNewKeycard: if self.flowType == FlowType.SetupNewKeycard:
self.doMigration(controller) if controller.getSelectedKeyPairIsProfile():
if not self.authenticationDone:
self.authenticationDone = true
let password = controller.getPassword()
self.authenticationOk = controller.verifyPassword(password)
if self.authenticationOk:
self.doMigration(controller)
return
if not self.addingMigratedKeypairDone:
self.addingMigratedKeypairDone = true
self.addingMigratedKeypairOk = controller.getAddingMigratedKeypairSuccess()
if self.addingMigratedKeypairOk:
self.doConversion(controller)
return
if not self.profileConversionDone:
self.profileConversionDone = true
self.profileConversionOk = controller.getConvertingProfileSuccess()
if self.profileConversionOk:
self.runStoreMetadataFlow(controller)
else:
if not self.addingMigratedKeypairDone:
self.addingMigratedKeypairDone = true
self.addingMigratedKeypairOk = controller.getAddingMigratedKeypairSuccess()
if self.addingMigratedKeypairOk:
self.runStoreMetadataFlow(controller)
method getNextSecondaryState*(self: MigratingKeyPairState, controller: Controller): State = method getNextSecondaryState*(self: MigratingKeyPairState, controller: Controller): State =
if self.flowType == FlowType.SetupNewKeycard: if self.flowType == FlowType.SetupNewKeycard:
if not self.migrationSuccess: if self.authenticationDone and not self.authenticationOk or
return createState(StateType.KeyPairMigrateFailure, self.flowType, nil) self.addingMigratedKeypairDone and not self.addingMigratedKeypairOk or
self.profileConversionDone and not self.profileConversionOk:
return createState(StateType.KeyPairMigrateFailure, self.flowType, nil)
method resolveKeycardNextState*(self: MigratingKeyPairState, keycardFlowType: string, keycardEvent: KeycardEvent, method resolveKeycardNextState*(self: MigratingKeyPairState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State = controller: Controller): State =

View File

@ -0,0 +1,21 @@
#################################################
# Async convert profile keypair
#################################################
type
ConvertToKeycardAccountTaskArg* = ref object of QObjectTaskArg
accountDataJson: JsonNode
settingsJson: JsonNode
hashedCurrentPassword: string
newPassword: string
keyStoreDir: string
const convertToKeycardAccountTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[ConvertToKeycardAccountTaskArg](argEncoded)
try:
let response = status_account.convertToKeycardAccount(arg.keyStoreDir, arg.accountDataJson, arg.settingsJson,
arg.hashedCurrentPassword, arg.newPassword)
arg.finish(response)
except Exception as e:
error "error converting profile keypair: ", message = e.msg
arg.finish("")

View File

@ -8,6 +8,9 @@ from ../keycard/service import KeycardEvent, KeyDetails
import ../../../backend/general as status_general import ../../../backend/general as status_general
import ../../../backend/core as status_core import ../../../backend/core as status_core
import ../../../app/core/eventemitter
import ../../../app/core/signals/types
import ../../../app/core/tasks/[qt, threadpool]
import ../../../app/core/fleets/fleet_configuration import ../../../app/core/fleets/fleet_configuration
import ../../common/[account_constants, network_constants, utils, string_utils] import ../../common/[account_constants, network_constants, utils, string_utils]
import ../../../constants as main_constants import ../../../constants as main_constants
@ -30,10 +33,18 @@ const KDF_ITERATIONS* {.intdefine.} = 256_000
# specific peer to set for testing messaging and mailserver functionality with squish. # specific peer to set for testing messaging and mailserver functionality with squish.
let TEST_PEER_ENR = getEnv("TEST_PEER_ENR").string let TEST_PEER_ENR = getEnv("TEST_PEER_ENR").string
const SIGNAL_CONVERTING_PROFILE_KEYPAIR* = "convertingProfileKeypair"
type ResultArgs* = ref object of Args
success*: bool
include utils include utils
include async_tasks
QtObject: QtObject:
type Service* = ref object of QObject type Service* = ref object of QObject
events: EventEmitter
threadpool: ThreadPool
fleetConfiguration: FleetConfiguration fleetConfiguration: FleetConfiguration
generatedAccounts: seq[GeneratedAccountDto] generatedAccounts: seq[GeneratedAccountDto]
accounts: seq[AccountDto] accounts: seq[AccountDto]
@ -46,10 +57,11 @@ QtObject:
proc delete*(self: Service) = proc delete*(self: Service) =
self.QObject.delete self.QObject.delete
proc newService*(fleetConfiguration: FleetConfiguration): Service = proc newService*(events: EventEmitter, threadpool: ThreadPool, fleetConfiguration: FleetConfiguration): Service =
result = Service()
new(result, delete) new(result, delete)
result.QObject.setup result.QObject.setup
result.events = events
result.threadpool = threadpool
result.fleetConfiguration = fleetConfiguration result.fleetConfiguration = fleetConfiguration
result.isFirstTimeAccountLogin = false result.isFirstTimeAccountLogin = false
result.keyStoreDir = main_constants.ROOTKEYSTOREDIR result.keyStoreDir = main_constants.ROOTKEYSTOREDIR
@ -677,36 +689,48 @@ QtObject:
error "error: ", procName="verifyAccountPassword", errName = e.name, errDesription = e.msg error "error: ", procName="verifyAccountPassword", errName = e.name, errDesription = e.msg
proc convertToKeycardAccount*(self: Service, keyUid: string, currentPassword: string, newPassword: string): bool = proc convertToKeycardAccount*(self: Service, keyUid: string, currentPassword: string, newPassword: string) =
var accountDataJson = %* {
"name": self.getLoggedInAccount().name,
"key-uid": keyUid
}
var settingsJson = %* {
"display-name": self.getLoggedInAccount().name
}
self.addKeycardDetails(settingsJson, accountDataJson)
if(accountDataJson.isNil or settingsJson.isNil):
let description = "at least one json object is not prepared well"
error "error: ", procName="convertToKeycardAccount", errDesription = description
return
let hashedCurrentPassword = hashString(currentPassword)
let arg = ConvertToKeycardAccountTaskArg(
tptr: cast[ByteAddress](convertToKeycardAccountTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onConvertToKeycardAccount",
accountDataJson: accountDataJson,
settingsJson: settingsJson,
keyStoreDir: self.keyStoreDir,
hashedCurrentPassword: hashedCurrentPassword,
newPassword: newPassword
)
self.threadpool.start(arg)
proc onConvertToKeycardAccount*(self: Service, response: string) {.slot.} =
var result = false
try: try:
var accountDataJson = %* { let rpcResponse = Json.decode(response, RpcResponse[JsonNode])
"name": self.getLoggedInAccount().name, if(rpcResponse.result.contains("error")):
"key-uid": keyUid let errMsg = rpcResponse.result["error"].getStr
}
var settingsJson = %* {
"display-name": self.getLoggedInAccount().name
}
self.addKeycardDetails(settingsJson, accountDataJson)
if(accountDataJson.isNil or settingsJson.isNil):
let description = "at least one json object is not prepared well"
error "error: ", procName="convertToKeycardAccount", errDesription = description
return
let hashedCurrentPassword = hashString(currentPassword)
let response = status_account.convertToKeycardAccount(self.keyStoreDir, accountDataJson, settingsJson,
hashedCurrentPassword, newPassword)
if(response.result.contains("error")):
let errMsg = response.result["error"].getStr
if(errMsg.len == 0): if(errMsg.len == 0):
return true result = true
else: else:
error "error: ", procName="convertToKeycardAccount", errDesription = errMsg error "error: ", procName="convertToKeycardAccount", errDesription = errMsg
return false
except Exception as e: except Exception as e:
error "error: ", procName="convertToKeycardAccount", errName = e.name, errDesription = e.msg error "error handilng migrated keypair response", errDesription=e.msg
self.events.emit(SIGNAL_CONVERTING_PROFILE_KEYPAIR, ResultArgs(success: result))
proc verifyPassword*(self: Service, password: string): bool = proc verifyPassword*(self: Service, password: string): bool =
try: try:

View File

@ -443,3 +443,26 @@ const prepareTokensTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
arg.finish(builtTokensPerAccount) arg.finish(builtTokensPerAccount)
#################################################
# Async add migrated keypair
#################################################
type
AddMigratedKeyPairTaskArg* = ref object of QObjectTaskArg
keyPair: KeyPairDto
keyStoreDir: string
const addMigratedKeyPairTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AddMigratedKeyPairTaskArg](argEncoded)
try:
let response = backend.addMigratedKeyPair(
arg.keyPair.keycardUid,
arg.keyPair.keycardName,
arg.keyPair.keyUid,
arg.keyPair.accountsAddresses,
arg.keyStoreDir
)
arg.finish(response)
except Exception as e:
error "error adding new keypair: ", message = e.msg
arg.finish("")

View File

@ -89,6 +89,7 @@ type TokensPerAccountArgs* = ref object of Args
accountsTokens*: OrderedTable[string, seq[WalletTokenDto]] # [wallet address, list of tokens] accountsTokens*: OrderedTable[string, seq[WalletTokenDto]] # [wallet address, list of tokens]
type KeycardActivityArgs* = ref object of Args type KeycardActivityArgs* = ref object of Args
success*: bool
keycardUid*: string keycardUid*: string
keycardNewUid*: string keycardNewUid*: string
keycardNewName*: string keycardNewName*: string
@ -113,6 +114,7 @@ QtObject:
walletAccounts: OrderedTable[string, WalletAccountDto] walletAccounts: OrderedTable[string, WalletAccountDto]
timerStartTimeInSeconds: int64 timerStartTimeInSeconds: int64
priceCache: TimedCache priceCache: TimedCache
processedKeyPair: KeyPairDto
# Forward declaration # Forward declaration
proc buildAllTokens(self: Service, calledFromTimerOrInit = false) proc buildAllTokens(self: Service, calledFromTimerOrInit = false)
@ -547,6 +549,28 @@ QtObject:
error "error: ", procName=procName, errDesription = errMsg error "error: ", procName=procName, errDesription = errMsg
return false return false
proc addMigratedKeyPairAsync*(self: Service, keyPair: KeyPairDto, keyStoreDir: string) =
let arg = AddMigratedKeyPairTaskArg(
tptr: cast[ByteAddress](addMigratedKeyPairTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onMigratedKeyPairAdded",
keyPair: keyPair,
keyStoreDir: keyStoreDir
)
self.processedKeyPair = keyPair
self.threadpool.start(arg)
proc onMigratedKeyPairAdded*(self: Service, response: string) {.slot.} =
var result = false
try:
let rpcResponse = Json.decode(response, RpcResponse[JsonNode])
result = self.responseHasNoErrors("addMigratedKeyPair", rpcResponse)
except Exception as e:
error "error handilng migrated keypair response", errDesription=e.msg
let data = KeycardActivityArgs(success: result, keyPair: self.processedKeyPair)
self.processedKeyPair = KeyPairDto()
self.events.emit(SIGNAL_NEW_KEYCARD_SET, data)
proc addMigratedKeyPair*(self: Service, keyPair: KeyPairDto, keyStoreDir: string): bool = proc addMigratedKeyPair*(self: Service, keyPair: KeyPairDto, keyStoreDir: string): bool =
try: try:
let response = backend.addMigratedKeyPair( let response = backend.addMigratedKeyPair(
@ -558,7 +582,7 @@ QtObject:
) )
result = self.responseHasNoErrors("addMigratedKeyPair", response) result = self.responseHasNoErrors("addMigratedKeyPair", response)
if result: if result:
self.events.emit(SIGNAL_NEW_KEYCARD_SET, KeycardActivityArgs(keyPair: keyPair)) self.events.emit(SIGNAL_NEW_KEYCARD_SET, KeycardActivityArgs(success: true, keyPair: 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

View File

@ -26,6 +26,7 @@ Item {
id: d id: d
readonly property bool hideKeyPair: root.sharedKeycardModule.keycardData & Constants.predefinedKeycardData.hideKeyPair readonly property bool hideKeyPair: root.sharedKeycardModule.keycardData & Constants.predefinedKeycardData.hideKeyPair
readonly property bool continuousProcessingAnimation: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.migratingKeyPair
} }
Timer { Timer {
@ -484,13 +485,25 @@ Item {
} }
PropertyChanges { PropertyChanges {
target: image target: image
pattern: Constants.keycardAnimations.warning.pattern pattern: d.continuousProcessingAnimation?
Constants.keycardAnimations.processing.pattern :
Constants.keycardAnimations.warning.pattern
source: "" source: ""
startImgIndexForTheFirstLoop: Constants.keycardAnimations.warning.startImgIndexForTheFirstLoop startImgIndexForTheFirstLoop: d.continuousProcessingAnimation?
startImgIndexForOtherLoops: Constants.keycardAnimations.warning.startImgIndexForOtherLoops Constants.keycardAnimations.processing.startImgIndexForTheFirstLoop :
endImgIndex: Constants.keycardAnimations.warning.endImgIndex Constants.keycardAnimations.warning.startImgIndexForTheFirstLoop
duration: Constants.keycardAnimations.warning.duration startImgIndexForOtherLoops: d.continuousProcessingAnimation?
loops: Constants.keycardAnimations.warning.loops Constants.keycardAnimations.processing.startImgIndexForOtherLoops :
Constants.keycardAnimations.warning.startImgIndexForOtherLoops
endImgIndex: d.continuousProcessingAnimation?
Constants.keycardAnimations.processing.endImgIndex :
Constants.keycardAnimations.warning.endImgIndex
duration: d.continuousProcessingAnimation?
Constants.keycardAnimations.processing.duration :
Constants.keycardAnimations.warning.duration
loops: d.continuousProcessingAnimation?
Constants.keycardAnimations.processing.loops :
Constants.keycardAnimations.warning.loops
} }
PropertyChanges { PropertyChanges {
target: message target: message

View File

@ -196,6 +196,15 @@ QtObject {
readonly property int loops: 1 readonly property int loops: 1
} }
readonly property QtObject processing: QtObject {
readonly property string pattern: "keycard/warning/img-%1"
readonly property int startImgIndexForTheFirstLoop: 0
readonly property int startImgIndexForOtherLoops: 18
readonly property int endImgIndex: 47
readonly property int duration: 1500
readonly property int loops: -1
}
readonly property QtObject strongError: QtObject { readonly property QtObject strongError: QtObject {
readonly property string pattern: "keycard/strong_error/img-%1" readonly property string pattern: "keycard/strong_error/img-%1"
readonly property int startImgIndexForTheFirstLoop: 0 readonly property int startImgIndexForTheFirstLoop: 0