mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-19 18:18:38 +00:00
feat(@desktop/keycard): adding wallet account using Authenticate
flow
Fixes: #7509
This commit is contained in:
parent
4c3aca273a
commit
861c585d2b
@ -240,8 +240,11 @@ proc init*(self: Controller) =
|
|||||||
self.authenticateUserFlowRequestedBy.len == 0:
|
self.authenticateUserFlowRequestedBy.len == 0:
|
||||||
return
|
return
|
||||||
self.delegate.onSharedKeycarModuleFlowTerminated(args.lastStepInTheCurrentFlow)
|
self.delegate.onSharedKeycarModuleFlowTerminated(args.lastStepInTheCurrentFlow)
|
||||||
|
var password = args.password
|
||||||
|
if password.len == 0 and args.keyUid.len > 0:
|
||||||
|
password = args.keyUid
|
||||||
let data = SharedKeycarModuleArgs(uniqueIdentifier: self.authenticateUserFlowRequestedBy,
|
let data = SharedKeycarModuleArgs(uniqueIdentifier: self.authenticateUserFlowRequestedBy,
|
||||||
data: args.data,
|
password: password,
|
||||||
keyUid: args.keyUid,
|
keyUid: args.keyUid,
|
||||||
txR: args.txR,
|
txR: args.txR,
|
||||||
txS: args.txS,
|
txS: args.txS,
|
||||||
|
@ -2,20 +2,29 @@ import io_interface
|
|||||||
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
|
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
|
||||||
import ../../../../../app_service/service/accounts/service as accounts_service
|
import ../../../../../app_service/service/accounts/service as accounts_service
|
||||||
|
|
||||||
|
import ../../../../global/global_singleton
|
||||||
|
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
|
||||||
|
|
||||||
|
import ../../../../core/eventemitter
|
||||||
|
|
||||||
|
const UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER* = "WalletSection-AccountsModule"
|
||||||
|
|
||||||
type
|
type
|
||||||
Controller* = ref object of RootObj
|
Controller* = ref object of RootObj
|
||||||
delegate: io_interface.AccessInterface
|
delegate: io_interface.AccessInterface
|
||||||
|
events: EventEmitter
|
||||||
walletAccountService: wallet_account_service.Service
|
walletAccountService: wallet_account_service.Service
|
||||||
accountsService: accounts_service.Service
|
accountsService: accounts_service.Service
|
||||||
|
|
||||||
proc newController*(
|
proc newController*(
|
||||||
delegate: io_interface.AccessInterface,
|
delegate: io_interface.AccessInterface,
|
||||||
walletAccountService: wallet_account_service.Service
|
events: EventEmitter,
|
||||||
walletAccountService: wallet_account_service.Service,
|
walletAccountService: wallet_account_service.Service,
|
||||||
accountsService: accounts_service.Service
|
accountsService: accounts_service.Service
|
||||||
): Controller =
|
): Controller =
|
||||||
result = Controller()
|
result = Controller()
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
|
result.events = events
|
||||||
result.walletAccountService = walletAccountService
|
result.walletAccountService = walletAccountService
|
||||||
result.accountsService = accountsService
|
result.accountsService = accountsService
|
||||||
|
|
||||||
@ -23,7 +32,11 @@ proc delete*(self: Controller) =
|
|||||||
discard
|
discard
|
||||||
|
|
||||||
proc init*(self: Controller) =
|
proc init*(self: Controller) =
|
||||||
discard
|
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
|
||||||
|
let args = SharedKeycarModuleArgs(e)
|
||||||
|
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER:
|
||||||
|
return
|
||||||
|
self.delegate.onUserAuthenticated(args.password)
|
||||||
|
|
||||||
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
|
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
|
||||||
return self.walletAccountService.getWalletAccounts()
|
return self.walletAccountService.getWalletAccounts()
|
||||||
@ -56,3 +69,18 @@ proc validSeedPhrase*(self: Controller, seedPhrase: string): bool =
|
|||||||
let err = self.accountsService.validateMnemonic(seedPhrase)
|
let err = self.accountsService.validateMnemonic(seedPhrase)
|
||||||
return err.len == 0
|
return err.len == 0
|
||||||
|
|
||||||
|
proc loggedInUserUsesBiometricLogin*(self: Controller): bool =
|
||||||
|
if(not defined(macosx)):
|
||||||
|
return false
|
||||||
|
let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
|
||||||
|
if (value != LS_VALUE_STORE):
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc getLoggedInAccount*(self: Controller): AccountDto =
|
||||||
|
return self.accountsService.getLoggedInAccount()
|
||||||
|
|
||||||
|
proc authenticateUser*(self: Controller, keyUid = "") =
|
||||||
|
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER,
|
||||||
|
keyUid: keyUid)
|
||||||
|
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
|
||||||
|
@ -48,3 +48,15 @@ method viewDidLoad*(self: AccessInterface) {.base.} =
|
|||||||
|
|
||||||
method validSeedPhrase*(self: AccessInterface, value: string): bool {.base.} =
|
method validSeedPhrase*(self: AccessInterface, value: string): bool {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method authenticateUser*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method loggedInUserUsesBiometricLogin*(self: AccessInterface): bool {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method isProfileKeyPairMigrated*(self: AccessInterface): bool {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
@ -172,3 +172,22 @@ method getDerivedAddressForPrivateKey*(self: Module, privateKey: string) =
|
|||||||
|
|
||||||
method validSeedPhrase*(self: Module, value: string): bool =
|
method validSeedPhrase*(self: Module, value: string): bool =
|
||||||
return self.controller.validSeedPhrase(value)
|
return self.controller.validSeedPhrase(value)
|
||||||
|
|
||||||
|
method loggedInUserUsesBiometricLogin*(self: Module): bool =
|
||||||
|
return self.controller.loggedInUserUsesBiometricLogin()
|
||||||
|
|
||||||
|
method isProfileKeyPairMigrated*(self: Module): bool =
|
||||||
|
return self.controller.getLoggedInAccount().keycardPairing.len > 0
|
||||||
|
|
||||||
|
method authenticateUser*(self: Module) =
|
||||||
|
if self.isProfileKeyPairMigrated():
|
||||||
|
let keyUid = singletonInstance.userProfile.getKeyUid()
|
||||||
|
self.controller.authenticateUser(keyUid)
|
||||||
|
else:
|
||||||
|
self.controller.authenticateUser()
|
||||||
|
|
||||||
|
method onUserAuthenticated*(self: Module, password: string) =
|
||||||
|
if password.len > 0:
|
||||||
|
self.view.userAuthenticaionSuccess(password)
|
||||||
|
else:
|
||||||
|
self.view.userAuthentiactionFail()
|
||||||
|
@ -277,3 +277,15 @@ QtObject:
|
|||||||
|
|
||||||
proc validSeedPhrase*(self: View, value: string): bool {.slot.} =
|
proc validSeedPhrase*(self: View, value: string): bool {.slot.} =
|
||||||
return self.delegate.validSeedPhrase(value)
|
return self.delegate.validSeedPhrase(value)
|
||||||
|
|
||||||
|
proc userAuthenticaionSuccess*(self: View, password: string) {.signal.}
|
||||||
|
proc userAuthentiactionFail*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc authenticateUser*(self: View) {.slot.} =
|
||||||
|
self.delegate.authenticateUser()
|
||||||
|
|
||||||
|
proc loggedInUserUsesBiometricLogin*(self: View): bool {.slot.} =
|
||||||
|
return self.delegate.loggedInUserUsesBiometricLogin()
|
||||||
|
|
||||||
|
proc isProfileKeyPairMigrated*(self: View): bool {.slot.} =
|
||||||
|
return self.delegate.isProfileKeyPairMigrated()
|
@ -44,6 +44,7 @@ type
|
|||||||
tmpKeyUidWhichIsBeingAuthenticating: string
|
tmpKeyUidWhichIsBeingAuthenticating: string
|
||||||
tmpKeyUidWhichIsBeingUnlocking: string
|
tmpKeyUidWhichIsBeingUnlocking: string
|
||||||
tmpUsePinFromBiometrics: bool
|
tmpUsePinFromBiometrics: bool
|
||||||
|
tmpOfferToStoreUpdatedPinToKeychain: bool
|
||||||
tmpKeycardUid: string
|
tmpKeycardUid: string
|
||||||
|
|
||||||
proc newController*(delegate: io_interface.AccessInterface,
|
proc newController*(delegate: io_interface.AccessInterface,
|
||||||
@ -125,7 +126,7 @@ proc init*(self: Controller) =
|
|||||||
if args.uniqueIdentifier != self.uniqueIdentifier:
|
if args.uniqueIdentifier != self.uniqueIdentifier:
|
||||||
return
|
return
|
||||||
self.connectKeycardReponseSignal()
|
self.connectKeycardReponseSignal()
|
||||||
self.delegate.onUserAuthenticated(args.data)
|
self.delegate.onUserAuthenticated(args.password)
|
||||||
self.connectionIds.add(handlerId)
|
self.connectionIds.add(handlerId)
|
||||||
|
|
||||||
proc getKeycardData*(self: Controller): string =
|
proc getKeycardData*(self: Controller): string =
|
||||||
@ -170,6 +171,12 @@ proc setPinMatch*(self: Controller, value: bool) =
|
|||||||
proc getPinMatch*(self: Controller): bool =
|
proc getPinMatch*(self: Controller): bool =
|
||||||
return self.tmpPinMatch
|
return self.tmpPinMatch
|
||||||
|
|
||||||
|
proc setOfferToStoreUpdatedPinToKeychain*(self: Controller, value: bool) =
|
||||||
|
self.tmpOfferToStoreUpdatedPinToKeychain = value
|
||||||
|
|
||||||
|
proc offerToStoreUpdatedPinToKeychain*(self: Controller): bool =
|
||||||
|
return self.tmpOfferToStoreUpdatedPinToKeychain
|
||||||
|
|
||||||
proc setPassword*(self: Controller, value: string) =
|
proc setPassword*(self: Controller, value: string) =
|
||||||
self.tmpPassword = value
|
self.tmpPassword = value
|
||||||
|
|
||||||
@ -307,7 +314,7 @@ proc terminateCurrentFlow*(self: Controller, lastStepInTheCurrentFlow: bool) =
|
|||||||
var data = SharedKeycarModuleFlowTerminatedArgs(uniqueIdentifier: self.uniqueIdentifier,
|
var data = SharedKeycarModuleFlowTerminatedArgs(uniqueIdentifier: self.uniqueIdentifier,
|
||||||
lastStepInTheCurrentFlow: lastStepInTheCurrentFlow)
|
lastStepInTheCurrentFlow: lastStepInTheCurrentFlow)
|
||||||
if lastStepInTheCurrentFlow:
|
if lastStepInTheCurrentFlow:
|
||||||
data.data = self.tmpPassword
|
data.password = self.tmpPassword
|
||||||
data.keyUid = flowEvent.keyUid
|
data.keyUid = flowEvent.keyUid
|
||||||
data.txR = flowEvent.txSignature.r
|
data.txR = flowEvent.txSignature.r
|
||||||
data.txS = flowEvent.txSignature.s
|
data.txS = flowEvent.txSignature.s
|
||||||
|
@ -15,6 +15,7 @@ method executePrimaryCommand*(self: BiometricsPinInvalidState, controller: Contr
|
|||||||
method executeSecondaryCommand*(self: BiometricsPinInvalidState, controller: Controller) =
|
method executeSecondaryCommand*(self: BiometricsPinInvalidState, controller: Controller) =
|
||||||
if self.flowType == FlowType.Authentication:
|
if self.flowType == FlowType.Authentication:
|
||||||
controller.setUsePinFromBiometrics(true)
|
controller.setUsePinFromBiometrics(true)
|
||||||
|
controller.setOfferToStoreUpdatedPinToKeychain(true)
|
||||||
|
|
||||||
method getNextSecondaryState*(self: BiometricsPinInvalidState, controller: Controller): State =
|
method getNextSecondaryState*(self: BiometricsPinInvalidState, controller: Controller): State =
|
||||||
if self.flowType == FlowType.Authentication:
|
if self.flowType == FlowType.Authentication:
|
||||||
|
@ -98,6 +98,8 @@ method resolveKeycardNextState*(self: EnterPinState, keycardFlowType: string, ke
|
|||||||
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
|
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
|
||||||
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
|
||||||
if keycardEvent.error.len == 0:
|
if keycardEvent.error.len == 0:
|
||||||
|
if controller.offerToStoreUpdatedPinToKeychain():
|
||||||
|
controller.tryToStoreDataToKeychain(controller.getPin())
|
||||||
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
|
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
|
||||||
return nil
|
return nil
|
||||||
if self.flowType == FlowType.DisplayKeycardContent:
|
if self.flowType == FlowType.DisplayKeycardContent:
|
||||||
|
@ -8,13 +8,33 @@ const SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED* = "sharedKeycarModuleFlowTer
|
|||||||
const SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER* = "sharedKeycarModuleAuthenticateUser"
|
const SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER* = "sharedKeycarModuleAuthenticateUser"
|
||||||
const SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED* = "sharedKeycarModuleUserAuthenticated"
|
const SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED* = "sharedKeycarModuleUserAuthenticated"
|
||||||
|
|
||||||
|
## Authentication in the app is a global thing and may be used from any part of the app. How to achieve that... it's enough just to send
|
||||||
|
## `SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER` signal with properly set `SharedKeycarModuleAuthenticationArgs` and props there:
|
||||||
|
## -- `uniqueIdentifier` - some unique string, for the readability usually name of the module which needs authentication,
|
||||||
|
## -- in case of non keycard user (regular) user that's enough,
|
||||||
|
## -- in case of keycard user we want to authenticate it with a card that his profile is migrated to, that means apart of `uniqueIdentifier`
|
||||||
|
## we need to set `keyUid` as well,
|
||||||
|
## -- in case we want to sign a transaction for a certain wallet's account, then apart of `uniqueIdentifier` and `keyUid`of a key pair that
|
||||||
|
## account belongs to, we need to set and `bip44Path` and `txHash`
|
||||||
|
##
|
||||||
|
## `SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER` will be handled in the `mainModule` (shared keycard popup module will be run) and as a
|
||||||
|
## result, when authentication gets done `SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED` signal with properly set `SharedKeycarModuleArgs`
|
||||||
|
## and props there will be emitted:
|
||||||
|
## -- `uniqueIdentifier` - will be the same as one used for running authentication process
|
||||||
|
## -- in case of success of a regular user authentication `password` will be sent, otherwise it will be empty
|
||||||
|
## -- in case of success of a keycard user authentication `keyUid` will be sent, otherwise it will be empty
|
||||||
|
## -- in case of success of a signing a transaction `keyUid`, `txR` and `txS` will be sent, otherwise it will be empty
|
||||||
|
##
|
||||||
|
## TLDR: when you need to authenticate user, from the module where it's needed you have to send `SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER`
|
||||||
|
## signal to run authentication process and connect to `SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED` signal to get the results of it.
|
||||||
|
|
||||||
type
|
type
|
||||||
SharedKeycarModuleBaseArgs* = ref object of Args
|
SharedKeycarModuleBaseArgs* = ref object of Args
|
||||||
uniqueIdentifier*: string
|
uniqueIdentifier*: string
|
||||||
|
|
||||||
type
|
type
|
||||||
SharedKeycarModuleArgs* = ref object of SharedKeycarModuleBaseArgs
|
SharedKeycarModuleArgs* = ref object of SharedKeycarModuleBaseArgs
|
||||||
data*: string
|
password*: string
|
||||||
keyUid*: string
|
keyUid*: string
|
||||||
txR*: string
|
txR*: string
|
||||||
txS*: string
|
txS*: string
|
||||||
|
@ -590,8 +590,6 @@ proc verifyAccountPassword*(self: Service, account: string, password: string): b
|
|||||||
|
|
||||||
proc convertToKeycardAccount*(self: Service, keyUid: string, password: string): bool =
|
proc convertToKeycardAccount*(self: Service, keyUid: string, password: string): bool =
|
||||||
try:
|
try:
|
||||||
self.setKeyStoreDir(keyUid)
|
|
||||||
|
|
||||||
var accountDataJson = %* {
|
var accountDataJson = %* {
|
||||||
"name": self.getLoggedInAccount().name,
|
"name": self.getLoggedInAccount().name,
|
||||||
"key-uid": keyUid
|
"key-uid": keyUid
|
||||||
|
@ -244,11 +244,11 @@ Item {
|
|||||||
This function resets the text input validation and text.
|
This function resets the text input validation and text.
|
||||||
*/
|
*/
|
||||||
function reset() {
|
function reset() {
|
||||||
|
statusBaseInput.text = ""
|
||||||
|
root.errorMessage = ""
|
||||||
statusBaseInput.valid = false
|
statusBaseInput.valid = false
|
||||||
statusBaseInput.dirty = false
|
statusBaseInput.dirty = false
|
||||||
statusBaseInput.pristine = true
|
statusBaseInput.pristine = true
|
||||||
statusBaseInput.text = ""
|
|
||||||
root.errorMessage = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
property string _previousText: text
|
property string _previousText: text
|
||||||
|
@ -24,8 +24,6 @@ StatusModal {
|
|||||||
|
|
||||||
property int minPswLen: 10
|
property int minPswLen: 10
|
||||||
readonly property int marginBetweenInputs: 38
|
readonly property int marginBetweenInputs: 38
|
||||||
readonly property alias passwordValidationError: d.passwordValidationError
|
|
||||||
|
|
||||||
property var emojiPopup: null
|
property var emojiPopup: null
|
||||||
|
|
||||||
header.title: qsTr("Generate an account")
|
header.title: qsTr("Generate an account")
|
||||||
@ -33,14 +31,6 @@ StatusModal {
|
|||||||
|
|
||||||
signal afterAddAccount()
|
signal afterAddAccount()
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: waitTimer
|
|
||||||
|
|
||||||
interval: 1000
|
|
||||||
running: false
|
|
||||||
onTriggered: d.getDerivedAddressList()
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: emojiPopup
|
target: emojiPopup
|
||||||
enabled: root.opened
|
enabled: root.opened
|
||||||
@ -51,16 +41,15 @@ StatusModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: RootStore
|
target: walletSectionAccounts
|
||||||
enabled: root.opened
|
onUserAuthenticaionSuccess: {
|
||||||
|
validationError.text = ""
|
||||||
function onDerivedAddressesListChanged() {
|
d.password = password
|
||||||
d.isPasswordCorrect = RootStore.derivedAddressesError.length === 0
|
d.getDerivedAddressList()
|
||||||
}
|
}
|
||||||
|
onUserAuthentiactionFail: {
|
||||||
function onDerivedAddressesErrorChanged() {
|
d.password = ""
|
||||||
if(Utils.isInvalidPasswordMessage(RootStore.derivedAddressesError))
|
validationError.text = qsTr("An authentication failed")
|
||||||
d.passwordValidationError = qsTr("Password must be at least %n character(s) long", "", root.minPswLen);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,33 +59,27 @@ StatusModal {
|
|||||||
readonly property int numOfItems: 100
|
readonly property int numOfItems: 100
|
||||||
readonly property int pageNumber: 1
|
readonly property int pageNumber: 1
|
||||||
|
|
||||||
property string passwordValidationError: ""
|
property string password: ""
|
||||||
property bool isPasswordCorrect: false
|
property int selectedAccountType: SelectGeneratedAccount.AddAccountType.GenerateNew
|
||||||
|
readonly property bool authenticationNeeded: d.selectedAccountType !== SelectGeneratedAccount.AddAccountType.WatchOnly &&
|
||||||
|
d.password === ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function getDerivedAddressList() {
|
function getDerivedAddressList() {
|
||||||
if(advancedSelection.expandableItem.addAccountType === SelectGeneratedAccount.AddAccountType.ImportSeedPhrase
|
if(d.selectedAccountType === SelectGeneratedAccount.AddAccountType.ImportSeedPhrase
|
||||||
&& !!advancedSelection.expandableItem.path
|
&& !!advancedSelection.expandableItem.path
|
||||||
&& !!advancedSelection.expandableItem.mnemonicText) {
|
&& !!advancedSelection.expandableItem.mnemonicText) {
|
||||||
RootStore.getDerivedAddressListForMnemonic(advancedSelection.expandableItem.mnemonicText,
|
RootStore.getDerivedAddressListForMnemonic(advancedSelection.expandableItem.mnemonicText,
|
||||||
advancedSelection.expandableItem.path, numOfItems, pageNumber)
|
advancedSelection.expandableItem.path, numOfItems, pageNumber)
|
||||||
} else if(!!advancedSelection.expandableItem.path && !!advancedSelection.expandableItem.derivedFromAddress
|
} else if(!!advancedSelection.expandableItem.path && !!advancedSelection.expandableItem.derivedFromAddress
|
||||||
&& (passwordInput.text.length > 0)) {
|
&& (d.password.length > 0)) {
|
||||||
RootStore.getDerivedAddressList(passwordInput.text, advancedSelection.expandableItem.derivedFromAddress,
|
RootStore.getDerivedAddressList(d.password, advancedSelection.expandableItem.derivedFromAddress,
|
||||||
advancedSelection.expandableItem.path, numOfItems, pageNumber)
|
advancedSelection.expandableItem.path, numOfItems, pageNumber)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPasswordError(errMessage) {
|
|
||||||
if (errMessage) {
|
|
||||||
if (Utils.isInvalidPasswordMessage(errMessage)) {
|
|
||||||
d.passwordValidationError = qsTr("Wrong password")
|
|
||||||
scroll.contentY = -scroll.padding
|
|
||||||
} else {
|
|
||||||
console.warn(`Unhandled error case. Status-go message: ${errMessage}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateNewAccount() {
|
function generateNewAccount() {
|
||||||
// TODO the loading doesn't work because the function freezes the view. Might need to use threads
|
// TODO the loading doesn't work because the function freezes the view. Might need to use threads
|
||||||
nextButton.loading = true
|
nextButton.loading = true
|
||||||
@ -107,19 +90,19 @@ StatusModal {
|
|||||||
|
|
||||||
let errMessage = ""
|
let errMessage = ""
|
||||||
|
|
||||||
switch(advancedSelection.expandableItem.addAccountType) {
|
switch(d.selectedAccountType) {
|
||||||
case SelectGeneratedAccount.AddAccountType.GenerateNew:
|
case SelectGeneratedAccount.AddAccountType.GenerateNew:
|
||||||
errMessage = RootStore.generateNewAccount(passwordInput.text, accountNameInput.text, colorSelectionGrid.selectedColor,
|
errMessage = RootStore.generateNewAccount(d.password, accountNameInput.text, colorSelectionGrid.selectedColor,
|
||||||
accountNameInput.input.asset.emoji, advancedSelection.expandableItem.completePath,
|
accountNameInput.input.asset.emoji, advancedSelection.expandableItem.completePath,
|
||||||
advancedSelection.expandableItem.derivedFromAddress)
|
advancedSelection.expandableItem.derivedFromAddress)
|
||||||
break
|
break
|
||||||
case SelectGeneratedAccount.AddAccountType.ImportSeedPhrase:
|
case SelectGeneratedAccount.AddAccountType.ImportSeedPhrase:
|
||||||
errMessage = RootStore.addAccountsFromSeed(advancedSelection.expandableItem.mnemonicText, passwordInput.text,
|
errMessage = RootStore.addAccountsFromSeed(advancedSelection.expandableItem.mnemonicText, d.password,
|
||||||
accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.asset.emoji,
|
accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.asset.emoji,
|
||||||
advancedSelection.expandableItem.completePath)
|
advancedSelection.expandableItem.completePath)
|
||||||
break
|
break
|
||||||
case SelectGeneratedAccount.AddAccountType.ImportPrivateKey:
|
case SelectGeneratedAccount.AddAccountType.ImportPrivateKey:
|
||||||
errMessage = RootStore.addAccountsFromPrivateKey(advancedSelection.expandableItem.privateKey, passwordInput.text,
|
errMessage = RootStore.addAccountsFromPrivateKey(advancedSelection.expandableItem.privateKey, d.password,
|
||||||
accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.asset.emoji)
|
accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.asset.emoji)
|
||||||
break
|
break
|
||||||
case SelectGeneratedAccount.AddAccountType.WatchOnly:
|
case SelectGeneratedAccount.AddAccountType.WatchOnly:
|
||||||
@ -131,23 +114,33 @@ StatusModal {
|
|||||||
nextButton.loading = false
|
nextButton.loading = false
|
||||||
|
|
||||||
if (errMessage) {
|
if (errMessage) {
|
||||||
d.showPasswordError(errMessage)
|
console.warn(`Unhandled error case. Status-go message: ${errMessage}`)
|
||||||
} else {
|
} else {
|
||||||
root.afterAddAccount()
|
root.afterAddAccount()
|
||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nextButtonClicked() {
|
||||||
|
if (d.authenticationNeeded) {
|
||||||
|
d.password = ""
|
||||||
|
RootStore.authenticateUser()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
d.generateNewAccount()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
accountNameInput.input.asset.emoji = StatusQUtils.Emoji.getRandomEmoji(StatusQUtils.Emoji.size.verySmall)
|
accountNameInput.input.asset.emoji = StatusQUtils.Emoji.getRandomEmoji(StatusQUtils.Emoji.size.verySmall)
|
||||||
colorSelectionGrid.selectedColorIndex = Math.floor(Math.random() * colorSelectionGrid.model.length)
|
colorSelectionGrid.selectedColorIndex = Math.floor(Math.random() * colorSelectionGrid.model.length)
|
||||||
passwordInput.forceActiveFocus(Qt.MouseFocusReason)
|
accountNameInput.input.edit.forceActiveFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
onClosed: {
|
onClosed: {
|
||||||
d.passwordValidationError = ""
|
d.password = ""
|
||||||
passwordInput.text = ""
|
validationError.text = ""
|
||||||
accountNameInput.reset()
|
accountNameInput.reset()
|
||||||
advancedSelection.expanded = false
|
advancedSelection.expanded = false
|
||||||
advancedSelection.reset()
|
advancedSelection.reset()
|
||||||
@ -169,34 +162,15 @@ StatusModal {
|
|||||||
spacing: Style.current.halfPadding
|
spacing: Style.current.halfPadding
|
||||||
topPadding: 20
|
topPadding: 20
|
||||||
|
|
||||||
// To-Do Password hidden option not supported in StatusQ StatusInput
|
StatusBaseText {
|
||||||
Item {
|
id: validationError
|
||||||
|
visible: text !== ""
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: passwordInput.height
|
height: 16
|
||||||
visible: advancedSelection.expandableItem.addAccountType !== SelectGeneratedAccount.AddAccountType.WatchOnly
|
horizontalAlignment: Text.AlignHCenter
|
||||||
Input {
|
font.pixelSize: 12
|
||||||
id: passwordInput
|
color: Style.current.danger
|
||||||
anchors.fill: parent
|
wrapMode: TextEdit.Wrap
|
||||||
|
|
||||||
placeholderText: qsTr("Enter your password...")
|
|
||||||
label: qsTr("Password")
|
|
||||||
textField.echoMode: TextInput.Password
|
|
||||||
validationError: d.passwordValidationError
|
|
||||||
textField.objectName: "accountModalPassword"
|
|
||||||
inputLabel.font.pixelSize: 15
|
|
||||||
inputLabel.font.weight: Font.Normal
|
|
||||||
onTextChanged: {
|
|
||||||
d.isPasswordCorrect = false
|
|
||||||
d.passwordValidationError = ""
|
|
||||||
waitTimer.restart()
|
|
||||||
}
|
|
||||||
onKeyPressed: {
|
|
||||||
if(event.key === Qt.Key_Tab) {
|
|
||||||
accountNameInput.input.edit.forceActiveFocus(Qt.MouseFocusReason)
|
|
||||||
event.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusInput {
|
StatusInput {
|
||||||
@ -206,6 +180,7 @@ StatusModal {
|
|||||||
input.isIconSelectable: true
|
input.isIconSelectable: true
|
||||||
input.asset.color: colorSelectionGrid.selectedColor ? colorSelectionGrid.selectedColor : Theme.palette.directColor1
|
input.asset.color: colorSelectionGrid.selectedColor ? colorSelectionGrid.selectedColor : Theme.palette.directColor1
|
||||||
input.leftPadding: Style.current.padding
|
input.leftPadding: Style.current.padding
|
||||||
|
enabled: !d.authenticationNeeded
|
||||||
onIconClicked: {
|
onIconClicked: {
|
||||||
root.emojiPopup.open()
|
root.emojiPopup.open()
|
||||||
root.emojiPopup.emojiSize = StatusQUtils.Emoji.size.verySmall
|
root.emojiPopup.emojiSize = StatusQUtils.Emoji.size.verySmall
|
||||||
@ -231,6 +206,7 @@ StatusModal {
|
|||||||
StatusColorSelectorGrid {
|
StatusColorSelectorGrid {
|
||||||
id: colorSelectionGrid
|
id: colorSelectionGrid
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
enabled: !d.authenticationNeeded
|
||||||
titleText: qsTr("color").toUpperCase()
|
titleText: qsTr("color").toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +238,15 @@ StatusModal {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Component.onCompleted: advancedSelection.isValid = Qt.binding(() => isValid)
|
|
||||||
|
onAddAccountTypeChanged: {
|
||||||
|
d.selectedAccountType = addAccountType
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
d.selectedAccountType = addAccountType
|
||||||
|
advancedSelection.isValid = Qt.binding(() => isValid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,21 +256,42 @@ StatusModal {
|
|||||||
StatusButton {
|
StatusButton {
|
||||||
id: nextButton
|
id: nextButton
|
||||||
|
|
||||||
text: loading ? qsTr("Loading...") : qsTr("Add account")
|
text: {
|
||||||
|
if (d.authenticationNeeded) {
|
||||||
|
return qsTr("Authenticate")
|
||||||
|
}
|
||||||
|
if (loading) {
|
||||||
|
return qsTr("Loading...")
|
||||||
|
}
|
||||||
|
return qsTr("Add account")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enabled: {
|
enabled: {
|
||||||
|
if (d.authenticationNeeded) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return accountNameInput.text !== "" && advancedSelection.isValid
|
||||||
|
}
|
||||||
|
|
||||||
return (advancedSelection.expandableItem.addAccountType === SelectGeneratedAccount.AddAccountType.WatchOnly || d.isPasswordCorrect)
|
icon.name: {
|
||||||
&& accountNameInput.text !== "" && advancedSelection.isValid
|
if (d.authenticationNeeded) {
|
||||||
|
if (RootStore.loggedInUserUsesBiometricLogin())
|
||||||
|
return "touch-id"
|
||||||
|
if (RootStore.isProfileKeyPairMigrated())
|
||||||
|
return "keycard"
|
||||||
|
return "password"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
highlighted: focus
|
highlighted: focus
|
||||||
|
|
||||||
Keys.onReturnPressed: d.generateNewAccount()
|
Keys.onReturnPressed: d.nextButtonClicked()
|
||||||
onClicked : d.generateNewAccount()
|
onClicked : d.nextButtonClicked()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -222,4 +222,16 @@ QtObject {
|
|||||||
function getNextSelectableDerivedAddressIndex() {
|
function getNextSelectableDerivedAddressIndex() {
|
||||||
return walletSectionAccounts.getNextSelectableDerivedAddressIndex()
|
return walletSectionAccounts.getNextSelectableDerivedAddressIndex()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function authenticateUser() {
|
||||||
|
walletSectionAccounts.authenticateUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
function loggedInUserUsesBiometricLogin() {
|
||||||
|
return walletSectionAccounts.loggedInUserUsesBiometricLogin()
|
||||||
|
}
|
||||||
|
|
||||||
|
function isProfileKeyPairMigrated() {
|
||||||
|
return walletSectionAccounts.isProfileKeyPairMigrated()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,10 +439,11 @@ StatusModal {
|
|||||||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsReadyToSign ||
|
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsReadyToSign ||
|
||||||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
|
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
|
||||||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
|
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
|
||||||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid ||
|
|
||||||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
|
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
|
||||||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty)
|
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty)
|
||||||
return qsTr("Use PIN")
|
return qsTr("Use PIN")
|
||||||
|
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid)
|
||||||
|
return qsTr("Update PIN")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
|
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
|
||||||
|
@ -826,7 +826,9 @@ Item {
|
|||||||
}
|
}
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: message
|
target: message
|
||||||
text: ""
|
text: qsTr("The PIN length doesn't match Keycard's PIN length")
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize2
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
2
vendor/status-go
vendored
2
vendor/status-go
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 698c32f3e3684dd5918b8f38aa55fc568e1e7639
|
Subproject commit d89c0c8d9e333dc9b0c4ea36fd9c49c09a9b7d19
|
Loading…
x
Reference in New Issue
Block a user