feat(@desktop/syncing): make a not operable account fully operable, part 2
- handles import keypairs (without syncing via qr) Closes the second part of #11779
This commit is contained in:
parent
7d4df690c5
commit
4c6af4f1ad
|
@ -5,6 +5,7 @@ import app/core/eventemitter
|
||||||
import app_service/service/accounts/service as accounts_service
|
import app_service/service/accounts/service as accounts_service
|
||||||
import app_service/service/wallet_account/service as wallet_account_service
|
import app_service/service/wallet_account/service as wallet_account_service
|
||||||
|
|
||||||
|
import app/modules/shared_models/[keypair_item]
|
||||||
import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module
|
import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
|
@ -94,3 +95,9 @@ proc makePrivateKeyKeypairFullyOperable*(self: Controller, keyUid, privateKey, p
|
||||||
|
|
||||||
proc makeSeedPhraseKeypairFullyOperable*(self: Controller, keyUid, mnemonic, password: string, doPasswordHashing: bool): string =
|
proc makeSeedPhraseKeypairFullyOperable*(self: Controller, keyUid, mnemonic, password: string, doPasswordHashing: bool): string =
|
||||||
return self.walletAccountService.makeSeedPhraseKeypairFullyOperable(keyUid, mnemonic, password, doPasswordHashing)
|
return self.walletAccountService.makeSeedPhraseKeypairFullyOperable(keyUid, mnemonic, password, doPasswordHashing)
|
||||||
|
|
||||||
|
proc getKeypairs*(self: Controller): seq[wallet_account_service.KeypairDto] =
|
||||||
|
return self.walletAccountService.getKeypairs()
|
||||||
|
|
||||||
|
proc getSelectedKeypair*(self: Controller): KeyPairItem =
|
||||||
|
return self.delegate.getSelectedKeypair()
|
|
@ -0,0 +1,12 @@
|
||||||
|
type
|
||||||
|
ScanQrState* = ref object of State
|
||||||
|
|
||||||
|
proc newScanQrState*(backState: State): ScanQrState =
|
||||||
|
result = ScanQrState()
|
||||||
|
result.setup(StateType.ScanQr, backState)
|
||||||
|
|
||||||
|
proc delete*(self: ScanQrState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: ScanQrState, controller: Controller): State =
|
||||||
|
discard
|
|
@ -0,0 +1,20 @@
|
||||||
|
type
|
||||||
|
SelectImportMethodState* = ref object of State
|
||||||
|
|
||||||
|
proc newSelectImportMethodState*(backState: State): SelectImportMethodState =
|
||||||
|
result = SelectImportMethodState()
|
||||||
|
result.setup(StateType.SelectImportMethod, backState)
|
||||||
|
|
||||||
|
proc delete*(self: SelectImportMethodState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: SelectImportMethodState, controller: Controller): State =
|
||||||
|
return createState(StateType.ScanQr, self)
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: SelectImportMethodState, controller: Controller): State =
|
||||||
|
let kp = controller.getSelectedKeypair()
|
||||||
|
if kp.getPairType() == KeyPairType.SeedImport.int:
|
||||||
|
return createState(StateType.ImportSeedPhrase, self)
|
||||||
|
if kp.getPairType() == KeyPairType.PrivateKeyImport.int:
|
||||||
|
return createState(StateType.ImportPrivateKey, self)
|
||||||
|
error "ki_unsupported keypair type"
|
|
@ -0,0 +1,12 @@
|
||||||
|
type
|
||||||
|
SelectKeypairState* = ref object of State
|
||||||
|
|
||||||
|
proc newSelectKeypairState*(backState: State): SelectKeypairState =
|
||||||
|
result = SelectKeypairState()
|
||||||
|
result.setup(StateType.SelectKeypair, backState)
|
||||||
|
|
||||||
|
proc delete*(self: SelectKeypairState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: SelectKeypairState, controller: Controller): State =
|
||||||
|
return createState(StateType.SelectImportMethod, self)
|
|
@ -2,7 +2,9 @@ import ../controller
|
||||||
|
|
||||||
type StateType* {.pure.} = enum
|
type StateType* {.pure.} = enum
|
||||||
NoState = "NoState"
|
NoState = "NoState"
|
||||||
Main = "Main"
|
SelectKeypair = "SelectKeypair"
|
||||||
|
SelectImportMethod = "SelectImportMethod"
|
||||||
|
ScanQr = "ScanQr"
|
||||||
ImportSeedPhrase = "ImportSeedPhrase"
|
ImportSeedPhrase = "ImportSeedPhrase"
|
||||||
ImportPrivateKey = "ImportPrivateKey"
|
ImportPrivateKey = "ImportPrivateKey"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import chronicles
|
import chronicles
|
||||||
import ../controller
|
import ../controller
|
||||||
|
import app/modules/shared_models/[keypair_item]
|
||||||
|
|
||||||
import state
|
import state
|
||||||
|
|
||||||
|
@ -9,10 +10,19 @@ logScope:
|
||||||
# Forward declaration
|
# Forward declaration
|
||||||
proc createState*(stateToBeCreated: StateType, backState: State): State
|
proc createState*(stateToBeCreated: StateType, backState: State): State
|
||||||
|
|
||||||
|
include select_keypair_state
|
||||||
|
include select_import_method_state
|
||||||
|
include scan_qr_state
|
||||||
include import_private_key_state
|
include import_private_key_state
|
||||||
include import_seed_phrase_state
|
include import_seed_phrase_state
|
||||||
|
|
||||||
proc createState*(stateToBeCreated: StateType, backState: State): State =
|
proc createState*(stateToBeCreated: StateType, backState: State): State =
|
||||||
|
if stateToBeCreated == StateType.SelectKeypair:
|
||||||
|
return newSelectKeypairState(backState)
|
||||||
|
if stateToBeCreated == StateType.SelectImportMethod:
|
||||||
|
return newSelectImportMethodState(backState)
|
||||||
|
if stateToBeCreated == StateType.ScanQr:
|
||||||
|
return newScanQrState(backState)
|
||||||
if stateToBeCreated == StateType.ImportPrivateKey:
|
if stateToBeCreated == StateType.ImportPrivateKey:
|
||||||
return newImportPrivateKeyState(backState)
|
return newImportPrivateKeyState(backState)
|
||||||
if stateToBeCreated == StateType.ImportSeedPhrase:
|
if stateToBeCreated == StateType.ImportSeedPhrase:
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import Tables, NimQml
|
import Tables, NimQml
|
||||||
|
|
||||||
|
import app/modules/shared_models/[keypair_item]
|
||||||
import app_service/service/wallet_account/dto/derived_address_dto
|
import app_service/service/wallet_account/dto/derived_address_dto
|
||||||
|
|
||||||
type ImportOption* {.pure.}= enum
|
type ImportOption* {.pure.}= enum
|
||||||
SeedPhrase = 1,
|
SelectKeypair = 1
|
||||||
PrivateKey = 2
|
SeedPhrase
|
||||||
|
PrivateKey
|
||||||
|
QrCode
|
||||||
|
|
||||||
type
|
type
|
||||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||||
|
@ -27,6 +30,9 @@ method onBackActionClicked*(self: AccessInterface) {.base.} =
|
||||||
method onPrimaryActionClicked*(self: AccessInterface) {.base.} =
|
method onPrimaryActionClicked*(self: AccessInterface) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onSecondaryActionClicked*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method onCancelActionClicked*(self: AccessInterface) {.base.} =
|
method onCancelActionClicked*(self: AccessInterface) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
@ -45,6 +51,12 @@ method validSeedPhrase*(self: AccessInterface, seedPhrase: string): bool {.base.
|
||||||
method onAddressDetailsFetched*(self: AccessInterface, derivedAddresses: seq[DerivedAddressDto], error: string) {.base.} =
|
method onAddressDetailsFetched*(self: AccessInterface, derivedAddresses: seq[DerivedAddressDto], error: string) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method getSelectedKeypair*(self: AccessInterface): KeyPairItem {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method setSelectedKeyPairByKeyUid*(self: AccessInterface, keyUid: string) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
type
|
type
|
||||||
DelegateInterface* = concept c
|
DelegateInterface* = concept c
|
||||||
c.onKeypairImportModuleLoaded()
|
c.onKeypairImportModuleLoaded()
|
||||||
|
|
|
@ -7,7 +7,7 @@ import internal/[state, state_factory]
|
||||||
import app/global/global_singleton
|
import app/global/global_singleton
|
||||||
import app/core/eventemitter
|
import app/core/eventemitter
|
||||||
import app/modules/shared/keypairs
|
import app/modules/shared/keypairs
|
||||||
import app/modules/shared_models/[derived_address_model]
|
import app/modules/shared_models/[keypair_model, derived_address_model]
|
||||||
import app_service/service/accounts/service as accounts_service
|
import app_service/service/accounts/service as accounts_service
|
||||||
import app_service/service/wallet_account/service as wallet_account_service
|
import app_service/service/wallet_account/service as wallet_account_service
|
||||||
|
|
||||||
|
@ -48,17 +48,23 @@ method getModuleAsVariant*[T](self: Module[T]): QVariant =
|
||||||
|
|
||||||
method load*[T](self: Module[T], keyUid: string, importOption: ImportOption) =
|
method load*[T](self: Module[T], keyUid: string, importOption: ImportOption) =
|
||||||
self.controller.init()
|
self.controller.init()
|
||||||
|
if importOption == ImportOption.SelectKeypair:
|
||||||
|
let items = keypairs.buildKeyPairsList(self.controller.getKeypairs(), excludeAlreadyMigratedPairs = true,
|
||||||
|
excludePrivateKeyKeypairs = false)
|
||||||
|
self.view.createKeypairModel(items)
|
||||||
|
self.view.setCurrentState(newSelectKeypairState(nil))
|
||||||
|
else:
|
||||||
let keypair = self.controller.getKeypairByKeyUid(keyUid)
|
let keypair = self.controller.getKeypairByKeyUid(keyUid)
|
||||||
if keypair.isNil:
|
if keypair.isNil:
|
||||||
error "trying to import an unknown keypair"
|
error "ki_trying to import an unknown keypair"
|
||||||
self.closeKeypairImportPopup()
|
self.closeKeypairImportPopup()
|
||||||
return
|
return
|
||||||
let keypairItem = buildKeypairItem(keypair, areTestNetworksEnabled = false) # testnetworks are irrelevant in this context
|
let keypairItem = buildKeypairItem(keypair, areTestNetworksEnabled = false) # testnetworks are irrelevant in this context
|
||||||
if keypairItem.isNil:
|
if keypairItem.isNil:
|
||||||
error "cannot generate keypair item for provided keypair"
|
error "ki_cannot generate keypair item for provided keypair"
|
||||||
self.closeKeypairImportPopup()
|
self.closeKeypairImportPopup()
|
||||||
return
|
return
|
||||||
self.view.setSelectedKeypair(keypairItem)
|
self.view.setSelectedKeypairItem(keypairItem)
|
||||||
if importOption == ImportOption.PrivateKey:
|
if importOption == ImportOption.PrivateKey:
|
||||||
self.view.setCurrentState(newImportPrivateKeyState(nil))
|
self.view.setCurrentState(newImportPrivateKeyState(nil))
|
||||||
elif importOption == ImportOption.SeedPhrase:
|
elif importOption == ImportOption.SeedPhrase:
|
||||||
|
@ -99,16 +105,40 @@ method onPrimaryActionClicked*[T](self: Module[T]) =
|
||||||
self.view.setCurrentState(nextState)
|
self.view.setCurrentState(nextState)
|
||||||
debug "ki_primary_action - set state", setCurrState=nextState.stateType()
|
debug "ki_primary_action - set state", setCurrState=nextState.stateType()
|
||||||
|
|
||||||
|
method onSecondaryActionClicked*[T](self: Module[T]) =
|
||||||
|
let currStateObj = self.view.currentStateObj()
|
||||||
|
if currStateObj.isNil:
|
||||||
|
error "ki_cannot resolve current state"
|
||||||
|
return
|
||||||
|
debug "ki_secondary_action", currState=currStateObj.stateType()
|
||||||
|
currStateObj.executePreSecondaryStateCommand(self.controller)
|
||||||
|
let nextState = currStateObj.getNextSecondaryState(self.controller)
|
||||||
|
if nextState.isNil:
|
||||||
|
return
|
||||||
|
self.view.setCurrentState(nextState)
|
||||||
|
debug "ki_secondary_action - set state", setCurrState=nextState.stateType()
|
||||||
|
|
||||||
proc authenticateLoggedInUser[T](self: Module[T]) =
|
proc authenticateLoggedInUser[T](self: Module[T]) =
|
||||||
self.controller.authenticateLoggedInUser()
|
self.controller.authenticateLoggedInUser()
|
||||||
|
|
||||||
|
method getSelectedKeypair*[T](self: Module[T]): KeyPairItem =
|
||||||
|
return self.view.getSelectedKeypair()
|
||||||
|
|
||||||
|
method setSelectedKeyPairByKeyUid*[T](self: Module[T], keyUid: string) =
|
||||||
|
let item = self.view.keypairModel().findItemByKeyUid(keyUid)
|
||||||
|
if item.isNil:
|
||||||
|
error "ki_cannot generate keypair item for provided keypair"
|
||||||
|
self.closeKeypairImportPopup()
|
||||||
|
return
|
||||||
|
self.view.setSelectedKeypairItem(item)
|
||||||
|
|
||||||
method changePrivateKey*[T](self: Module[T], privateKey: string) =
|
method changePrivateKey*[T](self: Module[T], privateKey: string) =
|
||||||
self.view.setPrivateKeyAccAddress(newDerivedAddressItem())
|
self.view.setPrivateKeyAccAddress(newDerivedAddressItem())
|
||||||
if privateKey.len == 0:
|
if privateKey.len == 0:
|
||||||
return
|
return
|
||||||
let genAccDto = self.controller.createAccountFromPrivateKey(privateKey)
|
let genAccDto = self.controller.createAccountFromPrivateKey(privateKey)
|
||||||
if genAccDto.address.len == 0:
|
if genAccDto.address.len == 0:
|
||||||
error "unable to resolve an address from the provided private key"
|
error "ki_unable to resolve an address from the provided private key"
|
||||||
return
|
return
|
||||||
let kp = self.view.getSelectedKeypair()
|
let kp = self.view.getSelectedKeypair()
|
||||||
if kp.isNil:
|
if kp.isNil:
|
||||||
|
@ -116,7 +146,7 @@ method changePrivateKey*[T](self: Module[T], privateKey: string) =
|
||||||
return
|
return
|
||||||
if kp.getKeyUid() != genAccDto.keyUid:
|
if kp.getKeyUid() != genAccDto.keyUid:
|
||||||
self.view.setEnteredPrivateKeyMatchTheKeypair(false)
|
self.view.setEnteredPrivateKeyMatchTheKeypair(false)
|
||||||
error "entered private key doesn't refer to a keyapir being imported"
|
error "ki_entered private key doesn't refer to a keyapir being imported"
|
||||||
return
|
return
|
||||||
self.view.setEnteredPrivateKeyMatchTheKeypair(true)
|
self.view.setEnteredPrivateKeyMatchTheKeypair(true)
|
||||||
self.view.setPrivateKeyAccAddress(newDerivedAddressItem(order = 0, address = genAccDto.address, publicKey = genAccDto.publicKey))
|
self.view.setPrivateKeyAccAddress(newDerivedAddressItem(order = 0, address = genAccDto.address, publicKey = genAccDto.publicKey))
|
||||||
|
@ -127,7 +157,7 @@ method changeSeedPhrase*[T](self: Module[T], seedPhrase: string) =
|
||||||
return
|
return
|
||||||
let genAccDto = self.controller.createAccountFromSeedPhrase(seedPhrase)
|
let genAccDto = self.controller.createAccountFromSeedPhrase(seedPhrase)
|
||||||
if seedPhrase.len > 0 and genAccDto.address.len == 0:
|
if seedPhrase.len > 0 and genAccDto.address.len == 0:
|
||||||
error "unable to create an account from the provided seed phrase"
|
error "ki_unable to create an account from the provided seed phrase"
|
||||||
return
|
return
|
||||||
|
|
||||||
method validSeedPhrase*[T](self: Module[T], seedPhrase: string): bool =
|
method validSeedPhrase*[T](self: Module[T], seedPhrase: string): bool =
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import io_interface
|
import io_interface
|
||||||
import internal/[state, state_wrapper]
|
import internal/[state, state_wrapper]
|
||||||
import app/modules/shared_models/[keypair_item, derived_address_model]
|
import app/modules/shared_models/[keypair_model, derived_address_model]
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
@ -9,6 +9,8 @@ QtObject:
|
||||||
delegate: io_interface.AccessInterface
|
delegate: io_interface.AccessInterface
|
||||||
currentState: StateWrapper
|
currentState: StateWrapper
|
||||||
currentStateVariant: QVariant
|
currentStateVariant: QVariant
|
||||||
|
keypairModel: KeyPairModel
|
||||||
|
keypairModelVariant: QVariant
|
||||||
selectedKeypair: KeyPairItem
|
selectedKeypair: KeyPairItem
|
||||||
selectedKeypairVariant: QVariant
|
selectedKeypairVariant: QVariant
|
||||||
privateKeyAccAddress: DerivedAddressItem
|
privateKeyAccAddress: DerivedAddressItem
|
||||||
|
@ -20,6 +22,10 @@ QtObject:
|
||||||
self.currentState.delete
|
self.currentState.delete
|
||||||
self.selectedKeypair.delete
|
self.selectedKeypair.delete
|
||||||
self.selectedKeypairVariant.delete
|
self.selectedKeypairVariant.delete
|
||||||
|
if not self.keypairModel.isNil:
|
||||||
|
self.keypairModel.delete
|
||||||
|
if not self.keypairModelVariant.isNil:
|
||||||
|
self.keypairModelVariant.delete
|
||||||
self.QObject.delete
|
self.QObject.delete
|
||||||
|
|
||||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||||
|
@ -37,6 +43,7 @@ QtObject:
|
||||||
signalConnect(result.currentState, "backActionClicked()", result, "onBackActionClicked()", 2)
|
signalConnect(result.currentState, "backActionClicked()", result, "onBackActionClicked()", 2)
|
||||||
signalConnect(result.currentState, "cancelActionClicked()", result, "onCancelActionClicked()", 2)
|
signalConnect(result.currentState, "cancelActionClicked()", result, "onCancelActionClicked()", 2)
|
||||||
signalConnect(result.currentState, "primaryActionClicked()", result, "onPrimaryActionClicked()", 2)
|
signalConnect(result.currentState, "primaryActionClicked()", result, "onPrimaryActionClicked()", 2)
|
||||||
|
signalConnect(result.currentState, "secondaryActionClicked()", result, "onSecondaryActionClicked()", 2)
|
||||||
|
|
||||||
proc currentStateObj*(self: View): State =
|
proc currentStateObj*(self: View): State =
|
||||||
return self.currentState.getStateObj()
|
return self.currentState.getStateObj()
|
||||||
|
@ -57,6 +64,9 @@ QtObject:
|
||||||
proc onPrimaryActionClicked*(self: View) {.slot.} =
|
proc onPrimaryActionClicked*(self: View) {.slot.} =
|
||||||
self.delegate.onPrimaryActionClicked()
|
self.delegate.onPrimaryActionClicked()
|
||||||
|
|
||||||
|
proc onSecondaryActionClicked*(self: View) {.slot.} =
|
||||||
|
self.delegate.onSecondaryActionClicked()
|
||||||
|
|
||||||
proc getSelectedKeypair*(self: View): KeyPairItem =
|
proc getSelectedKeypair*(self: View): KeyPairItem =
|
||||||
return self.selectedKeypair
|
return self.selectedKeypair
|
||||||
proc getSelectedKeypairAsVariant*(self: View): QVariant {.slot.} =
|
proc getSelectedKeypairAsVariant*(self: View): QVariant {.slot.} =
|
||||||
|
@ -64,9 +74,12 @@ QtObject:
|
||||||
QtProperty[QVariant] selectedKeypair:
|
QtProperty[QVariant] selectedKeypair:
|
||||||
read = getSelectedKeypairAsVariant
|
read = getSelectedKeypairAsVariant
|
||||||
|
|
||||||
proc setSelectedKeypair*(self: View, item: KeyPairItem) =
|
proc setSelectedKeypairItem*(self: View, item: KeyPairItem) =
|
||||||
self.selectedKeypair.setItem(item)
|
self.selectedKeypair.setItem(item)
|
||||||
|
|
||||||
|
proc setSelectedKeyPair*(self: View, keyUid: string) {.slot.} =
|
||||||
|
self.delegate.setSelectedKeyPairByKeyUid(keyUid)
|
||||||
|
|
||||||
proc getPrivateKeyAccAddress*(self: View): DerivedAddressItem =
|
proc getPrivateKeyAccAddress*(self: View): DerivedAddressItem =
|
||||||
return self.privateKeyAccAddress
|
return self.privateKeyAccAddress
|
||||||
|
|
||||||
|
@ -100,3 +113,23 @@ QtObject:
|
||||||
proc setEnteredPrivateKeyMatchTheKeypair*(self: View, value: bool) =
|
proc setEnteredPrivateKeyMatchTheKeypair*(self: View, value: bool) =
|
||||||
self.enteredPrivateKeyMatchTheKeypair = value
|
self.enteredPrivateKeyMatchTheKeypair = value
|
||||||
self.enteredPrivateKeyMatchTheKeypairChanged()
|
self.enteredPrivateKeyMatchTheKeypairChanged()
|
||||||
|
|
||||||
|
proc keypairModel*(self: View): KeyPairModel =
|
||||||
|
return self.keypairModel
|
||||||
|
|
||||||
|
proc keypairModelChanged(self: View) {.signal.}
|
||||||
|
proc getKeypairModel(self: View): QVariant {.slot.} =
|
||||||
|
if self.keypairModelVariant.isNil:
|
||||||
|
return newQVariant()
|
||||||
|
return self.keypairModelVariant
|
||||||
|
QtProperty[QVariant] keypairModel:
|
||||||
|
read = getKeypairModel
|
||||||
|
notify = keypairModelChanged
|
||||||
|
|
||||||
|
proc createKeypairModel*(self: View, items: seq[KeyPairItem]) =
|
||||||
|
if self.keypairModel.isNil:
|
||||||
|
self.keypairModel = newKeyPairModel()
|
||||||
|
if self.keypairModelVariant.isNil:
|
||||||
|
self.keypairModelVariant = newQVariant(self.keypairModel)
|
||||||
|
self.keypairModel.setItems(items)
|
||||||
|
self.keypairModelChanged()
|
|
@ -10,8 +10,7 @@ const KeypairTypeProfile* = "profile"
|
||||||
const KeypairTypeSeed* = "seed"
|
const KeypairTypeSeed* = "seed"
|
||||||
const KeypairTypeKey* = "key"
|
const KeypairTypeKey* = "key"
|
||||||
|
|
||||||
const SyncedFromBackup* = "backup" # means a account is coming from backed up data
|
const SyncedFromBackup* = "backup" # means a keypair is coming from backed up data
|
||||||
const SyncedFromLocalPairing* = "local-pairing" # means a account is coming from another device when user is reocovering Status account
|
|
||||||
|
|
||||||
type
|
type
|
||||||
KeypairDto* = ref object of RootObj
|
KeypairDto* = ref object of RootObj
|
||||||
|
|
|
@ -591,6 +591,10 @@ proc handleWalletAccount(self: Service, account: WalletAccountDto, notify: bool
|
||||||
proc handleKeypair(self: Service, keypair: KeypairDto) =
|
proc handleKeypair(self: Service, keypair: KeypairDto) =
|
||||||
let localKp = self.getKeypairByKeyUid(keypair.keyUid)
|
let localKp = self.getKeypairByKeyUid(keypair.keyUid)
|
||||||
if not localKp.isNil:
|
if not localKp.isNil:
|
||||||
|
# sotore only keypair fields which may change
|
||||||
|
localKp.name = keypair.name
|
||||||
|
localKp.lastUsedDerivationIndex = keypair.lastUsedDerivationIndex
|
||||||
|
localKp.syncedFrom = keypair.syncedFrom
|
||||||
# - first remove removed accounts from the UI
|
# - first remove removed accounts from the UI
|
||||||
for localAcc in localKp.accounts:
|
for localAcc in localKp.accounts:
|
||||||
let accAddress = localAcc.address
|
let accAddress = localAcc.address
|
||||||
|
|
|
@ -20,10 +20,12 @@ StatusMenu {
|
||||||
enabled: !!root.keyPair &&
|
enabled: !!root.keyPair &&
|
||||||
root.keyPair.pairType !== Constants.keypair.type.profile &&
|
root.keyPair.pairType !== Constants.keypair.type.profile &&
|
||||||
!root.keyPair.migratedToKeycard &&
|
!root.keyPair.migratedToKeycard &&
|
||||||
root.keyPair.operability === Constants.keypair.operability.fullyOperable
|
root.keyPair.operability !== Constants.keypair.operability.nonOperable
|
||||||
icon.name: "qr"
|
icon.name: "qr"
|
||||||
icon.color: Theme.palette.primaryColor1
|
icon.color: Theme.palette.primaryColor1
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
// in this case we need to check if any account of a keypair is partially operable and not migrated to a keycard
|
||||||
|
// and if so we need to create a keystore for them first and then proceed with qr code
|
||||||
console.warn("TODO: show encrypted QR")
|
console.warn("TODO: show encrypted QR")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +48,7 @@ StatusMenu {
|
||||||
text: enabled? qsTr("Import keypair from device via encrypted QR") : ""
|
text: enabled? qsTr("Import keypair from device via encrypted QR") : ""
|
||||||
enabled: !!root.keyPair &&
|
enabled: !!root.keyPair &&
|
||||||
root.keyPair.pairType !== Constants.keypair.type.profile &&
|
root.keyPair.pairType !== Constants.keypair.type.profile &&
|
||||||
|
!root.keyPair.migratedToKeycard &&
|
||||||
root.keyPair.operability === Constants.keypair.operability.nonOperable &&
|
root.keyPair.operability === Constants.keypair.operability.nonOperable &&
|
||||||
root.keyPair.syncedFrom !== Constants.keypair.syncedFrom.backup
|
root.keyPair.syncedFrom !== Constants.keypair.syncedFrom.backup
|
||||||
icon.name: "qr-scan"
|
icon.name: "qr-scan"
|
||||||
|
@ -58,6 +61,7 @@ StatusMenu {
|
||||||
StatusAction {
|
StatusAction {
|
||||||
text: enabled? root.keyPair.pairType === Constants.keypair.type.privateKeyImport? qsTr("Import via entering private key") : qsTr("Import via entering seed phrase") : ""
|
text: enabled? root.keyPair.pairType === Constants.keypair.type.privateKeyImport? qsTr("Import via entering private key") : qsTr("Import via entering seed phrase") : ""
|
||||||
enabled: !!root.keyPair &&
|
enabled: !!root.keyPair &&
|
||||||
|
!root.keyPair.migratedToKeycard &&
|
||||||
root.keyPair.operability === Constants.keypair.operability.nonOperable &&
|
root.keyPair.operability === Constants.keypair.operability.nonOperable &&
|
||||||
(root.keyPair.pairType === Constants.keypair.type.seedImport ||
|
(root.keyPair.pairType === Constants.keypair.type.seedImport ||
|
||||||
root.keyPair.pairType === Constants.keypair.type.privateKeyImport)
|
root.keyPair.pairType === Constants.keypair.type.privateKeyImport)
|
||||||
|
|
|
@ -130,6 +130,51 @@ Column {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: importMissingKeypairs
|
||||||
|
visible: importMissingKeypairs.unimportedKeypairs > 0
|
||||||
|
height: 102
|
||||||
|
width: parent.width
|
||||||
|
color: Theme.palette.transparent
|
||||||
|
radius: 8
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.baseColor5
|
||||||
|
|
||||||
|
readonly property int unimportedKeypairs: {
|
||||||
|
let total = 0
|
||||||
|
for (var i = 0; i < keypairsRepeater.count; i++) {
|
||||||
|
let kp = keypairsRepeater.itemAt(i).keyPair
|
||||||
|
if (kp.migratedToKeycard ||
|
||||||
|
kp.operability === Constants.keypair.operability.fullyOperable ||
|
||||||
|
kp.operability === Constants.keypair.operability.partiallyOperable) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
total++
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
padding: 16
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
text: qsTr("%n keypair(s) require import to use on this device", "", importMissingKeypairs.unimportedKeypairs)
|
||||||
|
font.pixelSize: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
text: qsTr("Import missing keypairs")
|
||||||
|
type: StatusBaseButton.Type.Warning
|
||||||
|
icon.name: "download"
|
||||||
|
onClicked: {
|
||||||
|
root.walletStore.runKeypairImportPopup("", Constants.keypairImportPopup.importOption.selectKeypair)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spacer {
|
Spacer {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
@ -138,6 +183,7 @@ Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: 24
|
spacing: 24
|
||||||
Repeater {
|
Repeater {
|
||||||
|
id: keypairsRepeater
|
||||||
objectName: "generatedAccounts"
|
objectName: "generatedAccounts"
|
||||||
model: walletStore.originModel
|
model: walletStore.originModel
|
||||||
delegate: WalletKeyPairDelegate {
|
delegate: WalletKeyPairDelegate {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import QtQuick 2.14
|
||||||
import QtQml.Models 2.14
|
import QtQml.Models 2.14
|
||||||
import QtQuick.Controls 2.14
|
import QtQuick.Controls 2.14
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Core.Utils 0.1
|
import StatusQ.Core.Utils 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
|
@ -12,12 +13,12 @@ import utils 1.0
|
||||||
StatusListItem {
|
StatusListItem {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var sharedKeycardModule
|
|
||||||
property ButtonGroup buttonGroup
|
property ButtonGroup buttonGroup
|
||||||
property bool usedAsSelectOption: false
|
property bool usedAsSelectOption: false
|
||||||
property bool tagClickable: false
|
property bool tagClickable: false
|
||||||
property bool tagDisplayRemoveAccountButton: false
|
property bool tagDisplayRemoveAccountButton: false
|
||||||
property bool canBeSelected: true
|
property bool canBeSelected: true
|
||||||
|
property bool displayRadioButtonForSelection: true
|
||||||
|
|
||||||
property int keyPairType: Constants.keycard.keyPairType.unknown
|
property int keyPairType: Constants.keycard.keyPairType.unknown
|
||||||
property string keyPairKeyUid: ""
|
property string keyPairKeyUid: ""
|
||||||
|
@ -115,23 +116,38 @@ StatusListItem {
|
||||||
property list<Item> components: [
|
property list<Item> components: [
|
||||||
StatusRadioButton {
|
StatusRadioButton {
|
||||||
id: radioButton
|
id: radioButton
|
||||||
visible: root.usedAsSelectOption
|
visible: root.usedAsSelectOption && root.displayRadioButtonForSelection
|
||||||
ButtonGroup.group: root.buttonGroup
|
ButtonGroup.group: root.buttonGroup
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if (!root.usedAsSelectOption || !root.canBeSelected)
|
d.doAction(checked)
|
||||||
return
|
}
|
||||||
if (checked) {
|
},
|
||||||
root.sharedKeycardModule.setSelectedKeyPair(root.keyPairKeyUid)
|
StatusIcon {
|
||||||
root.keyPairSelected()
|
visible: root.usedAsSelectOption && !root.displayRadioButtonForSelection
|
||||||
|
icon: "next"
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
d.doAction(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
function doAction(checked){
|
||||||
|
if (!root.usedAsSelectOption || !root.canBeSelected)
|
||||||
|
return
|
||||||
|
if (checked) {
|
||||||
|
root.keyPairSelected()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.usedAsSelectOption || !root.canBeSelected)
|
if (!root.usedAsSelectOption || !root.canBeSelected)
|
||||||
return
|
return
|
||||||
radioButton.checked = true
|
d.doAction(!root.displayRadioButtonForSelection || radioButton.checked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,25 +8,18 @@ import SortFilterProxyModel 0.2
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var sharedKeycardModule
|
|
||||||
property bool filterProfilePair: false
|
|
||||||
property var keyPairModel
|
property var keyPairModel
|
||||||
property ButtonGroup buttonGroup
|
property ButtonGroup buttonGroup
|
||||||
|
property bool disableSelectionForKeypairsWithNonDefaultDerivationPath: true
|
||||||
|
property bool displayRadioButtonForSelection: true
|
||||||
|
property string optionLabel: ""
|
||||||
|
property alias modelFilters: proxyModel.filters
|
||||||
|
|
||||||
signal keyPairSelected()
|
signal keyPairSelected(string keyUid)
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: d
|
|
||||||
readonly property string profilePairTypeValue: Constants.keycard.keyPairType.profile
|
|
||||||
}
|
|
||||||
|
|
||||||
SortFilterProxyModel {
|
SortFilterProxyModel {
|
||||||
id: proxyModel
|
id: proxyModel
|
||||||
sourceModel: root.keyPairModel
|
sourceModel: root.keyPairModel
|
||||||
filters: ExpressionFilter {
|
|
||||||
expression: model.keyPair.pairType == d.profilePairTypeValue
|
|
||||||
inverted: !root.filterProfilePair
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
|
@ -37,10 +30,12 @@ Item {
|
||||||
delegate: KeyPairItem {
|
delegate: KeyPairItem {
|
||||||
width: ListView.view.width
|
width: ListView.view.width
|
||||||
|
|
||||||
sharedKeycardModule: root.sharedKeycardModule
|
label: root.optionLabel
|
||||||
buttonGroup: root.buttonGroup
|
buttonGroup: root.buttonGroup
|
||||||
usedAsSelectOption: true
|
usedAsSelectOption: true
|
||||||
canBeSelected: !model.keyPair.containsPathOutOfTheDefaultStatusDerivationTree()
|
canBeSelected: !root.disableSelectionForKeypairsWithNonDefaultDerivationPath ||
|
||||||
|
!model.keyPair.containsPathOutOfTheDefaultStatusDerivationTree()
|
||||||
|
displayRadioButtonForSelection: root.displayRadioButtonForSelection
|
||||||
|
|
||||||
keyPairType: model.keyPair.pairType
|
keyPairType: model.keyPair.pairType
|
||||||
keyPairKeyUid: model.keyPair.keyUid
|
keyPairKeyUid: model.keyPair.keyUid
|
||||||
|
@ -51,7 +46,7 @@ Item {
|
||||||
keyPairAccounts: model.keyPair.accounts
|
keyPairAccounts: model.keyPair.accounts
|
||||||
|
|
||||||
onKeyPairSelected: {
|
onKeyPairSelected: {
|
||||||
root.keyPairSelected()
|
root.keyPairSelected(model.keyPair.keyUid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import StatusQ.Controls 0.1
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
import shared.status 1.0
|
import shared.status 1.0
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
import "../helpers"
|
import "../helpers"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -20,6 +22,11 @@ Item {
|
||||||
|
|
||||||
signal keyPairSelected()
|
signal keyPairSelected()
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
readonly property string profilePairTypeValue: Constants.keycard.keyPairType.profile
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.topMargin: Style.current.xlPadding
|
anchors.topMargin: Style.current.xlPadding
|
||||||
|
@ -77,12 +84,14 @@ Item {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.preferredWidth: parent.width
|
Layout.preferredWidth: parent.width
|
||||||
|
|
||||||
sharedKeycardModule: root.sharedKeycardModule
|
modelFilters: ExpressionFilter {
|
||||||
filterProfilePair: true
|
expression: model.keyPair.pairType == d.profilePairTypeValue
|
||||||
|
}
|
||||||
keyPairModel: root.sharedKeycardModule.keyPairModel
|
keyPairModel: root.sharedKeycardModule.keyPairModel
|
||||||
buttonGroup: keyPairsButtonGroup
|
buttonGroup: keyPairsButtonGroup
|
||||||
|
|
||||||
onKeyPairSelected: {
|
onKeyPairSelected: {
|
||||||
|
root.sharedKeycardModule.setSelectedKeyPair(keyUid)
|
||||||
root.keyPairSelected()
|
root.keyPairSelected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,11 +116,15 @@ Item {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.preferredWidth: parent.width
|
Layout.preferredWidth: parent.width
|
||||||
|
|
||||||
sharedKeycardModule: root.sharedKeycardModule
|
modelFilters: ExpressionFilter {
|
||||||
|
expression: model.keyPair.pairType == d.profilePairTypeValue
|
||||||
|
inverted: true
|
||||||
|
}
|
||||||
keyPairModel: root.sharedKeycardModule.keyPairModel
|
keyPairModel: root.sharedKeycardModule.keyPairModel
|
||||||
buttonGroup: keyPairsButtonGroup
|
buttonGroup: keyPairsButtonGroup
|
||||||
|
|
||||||
onKeyPairSelected: {
|
onKeyPairSelected: {
|
||||||
|
root.sharedKeycardModule.setSelectedKeyPair(keyUid)
|
||||||
root.keyPairSelected()
|
root.keyPairSelected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import StatusQ.Controls 0.1
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
import "./stores"
|
import "./stores"
|
||||||
|
import "./states"
|
||||||
import "../common"
|
import "../common"
|
||||||
|
|
||||||
StatusModal {
|
StatusModal {
|
||||||
|
@ -20,6 +21,15 @@ StatusModal {
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
headerSettings.title: {
|
headerSettings.title: {
|
||||||
|
switch (root.store.currentState.stateType) {
|
||||||
|
case Constants.keypairImportPopup.state.selectKeypair:
|
||||||
|
return qsTr("Import missing keypairs")
|
||||||
|
case Constants.keypairImportPopup.state.selectImportMethod:
|
||||||
|
return qsTr("Import %1 keypair").arg(root.store.selectedKeypair.name)
|
||||||
|
case Constants.keypairImportPopup.state.scanQr:
|
||||||
|
return qsTr("Scan encrypted QR")
|
||||||
|
}
|
||||||
|
|
||||||
return qsTr("Import %1 keypair").arg(root.store.selectedKeypair.name)
|
return qsTr("Import %1 keypair").arg(root.store.selectedKeypair.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +56,12 @@ StatusModal {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
sourceComponent: {
|
sourceComponent: {
|
||||||
switch (root.store.currentState.stateType) {
|
switch (root.store.currentState.stateType) {
|
||||||
|
case Constants.keypairImportPopup.state.selectKeypair:
|
||||||
|
return selectKeypairComponent
|
||||||
|
case Constants.keypairImportPopup.state.selectImportMethod:
|
||||||
|
return selectImportMethodComponent
|
||||||
|
case Constants.keypairImportPopup.state.scanQr:
|
||||||
|
return scanQrComponent
|
||||||
case Constants.keypairImportPopup.state.importPrivateKey:
|
case Constants.keypairImportPopup.state.importPrivateKey:
|
||||||
return keypairImportPrivateKeyComponent
|
return keypairImportPrivateKeyComponent
|
||||||
case Constants.keypairImportPopup.state.importSeedPhrase:
|
case Constants.keypairImportPopup.state.importSeedPhrase:
|
||||||
|
@ -60,6 +76,28 @@ StatusModal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: selectKeypairComponent
|
||||||
|
SelectKeypair {
|
||||||
|
height: Constants.keypairImportPopup.contentHeight
|
||||||
|
store: root.store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: selectImportMethodComponent
|
||||||
|
SelectImportMethod {
|
||||||
|
height: Constants.keypairImportPopup.contentHeight
|
||||||
|
store: root.store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: scanQrComponent
|
||||||
|
Item {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: keypairImportPrivateKeyComponent
|
id: keypairImportPrivateKeyComponent
|
||||||
EnterPrivateKey {
|
EnterPrivateKey {
|
||||||
|
@ -97,6 +135,8 @@ StatusModal {
|
||||||
text: {
|
text: {
|
||||||
switch (root.store.currentState.stateType) {
|
switch (root.store.currentState.stateType) {
|
||||||
|
|
||||||
|
case Constants.keypairImportPopup.state.scanQr:
|
||||||
|
return qsTr("Done")
|
||||||
case Constants.keypairImportPopup.state.importPrivateKey:
|
case Constants.keypairImportPopup.state.importPrivateKey:
|
||||||
case Constants.keypairImportPopup.state.importSeedPhrase:
|
case Constants.keypairImportPopup.state.importSeedPhrase:
|
||||||
return qsTr("Import %1 keypair").arg(root.store.selectedKeypair.name)
|
return qsTr("Import %1 keypair").arg(root.store.selectedKeypair.name)
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property KeypairImportStore store
|
||||||
|
|
||||||
|
implicitHeight: layout.implicitHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 16
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Import method")
|
||||||
|
font.pixelSize: Constants.keypairImportPopup.labelFontSize1
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusListItem {
|
||||||
|
title: qsTr("Import via scanning encrypted QR")
|
||||||
|
|
||||||
|
asset {
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
name: "qr"
|
||||||
|
}
|
||||||
|
|
||||||
|
components: [
|
||||||
|
StatusIcon {
|
||||||
|
icon: "next"
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
root.store.currentState.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.store.currentState.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusListItem {
|
||||||
|
title: root.store.selectedKeypair.pairType === Constants.keypair.type.seedImport?
|
||||||
|
qsTr("Import via entering seed phrase") :
|
||||||
|
qsTr("Import via entering private key")
|
||||||
|
|
||||||
|
asset {
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
name: root.store.selectedKeypair.pairType === Constants.keypair.type.seedImport?
|
||||||
|
"key_pair_seed_phrase" :
|
||||||
|
"objects"
|
||||||
|
}
|
||||||
|
|
||||||
|
components: [
|
||||||
|
StatusIcon {
|
||||||
|
icon: "next"
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
root.store.currentState.doSecondaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.store.currentState.doSecondaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
|
||||||
|
import shared.popups.keycard.helpers 1.0
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property KeypairImportStore store
|
||||||
|
|
||||||
|
implicitHeight: layout.implicitHeight
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property string fullyOperableValue: Constants.keypair.operability.fullyOperable
|
||||||
|
readonly property string partiallyOperableValue: Constants.keypair.operability.partiallyOperable
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 16
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("To use the associated accounts on this device, you need to import their keypairs.")
|
||||||
|
font.pixelSize: Constants.keypairImportPopup.labelFontSize1
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyPairList {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
|
||||||
|
optionLabel: qsTr("import")
|
||||||
|
disableSelectionForKeypairsWithNonDefaultDerivationPath: false
|
||||||
|
displayRadioButtonForSelection: false
|
||||||
|
modelFilters: ExpressionFilter {
|
||||||
|
expression: model.keyPair.migratedToKeycard ||
|
||||||
|
model.keyPair.operability == d.fullyOperableValue ||
|
||||||
|
model.keyPair.operability == d.partiallyOperableValue
|
||||||
|
inverted: true
|
||||||
|
}
|
||||||
|
keyPairModel: root.store.keypairImportModule.keypairModel
|
||||||
|
|
||||||
|
onKeyPairSelected: {
|
||||||
|
root.store.keypairImportModule.setSelectedKeyPair(keyUid)
|
||||||
|
root.store.currentState.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -487,8 +487,7 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property QtObject syncedFrom: QtObject {
|
readonly property QtObject syncedFrom: QtObject {
|
||||||
readonly property string backup: "backup" // means a account is coming from backed up data
|
readonly property string backup: "backup" // means an account is coming from backed up data
|
||||||
readonly property string localPairing: "local-pairing" // means a account is coming from another device when user is reocovering Status account
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,14 +742,20 @@ QtObject {
|
||||||
readonly property int popupWidth: 480
|
readonly property int popupWidth: 480
|
||||||
readonly property int contentHeight: 626
|
readonly property int contentHeight: 626
|
||||||
readonly property int footerButtonsHeight: 44
|
readonly property int footerButtonsHeight: 44
|
||||||
|
readonly property int labelFontSize1: 15
|
||||||
|
|
||||||
readonly property QtObject importOption: QtObject {
|
readonly property QtObject importOption: QtObject {
|
||||||
readonly property int seedPhrase: 1
|
readonly property int selectKeypair: 1
|
||||||
readonly property int privateKey: 2
|
readonly property int seedPhrase: 2
|
||||||
|
readonly property int privateKey: 3
|
||||||
|
readonly property int qrCode: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property QtObject state: QtObject {
|
readonly property QtObject state: QtObject {
|
||||||
readonly property string noState: "NoState"
|
readonly property string noState: "NoState"
|
||||||
|
readonly property string selectKeypair: "SelectKeypair"
|
||||||
|
readonly property string selectImportMethod: "SelectImportMethod"
|
||||||
|
readonly property string scanQr: "ScanQr"
|
||||||
readonly property string importSeedPhrase: "ImportSeedPhrase"
|
readonly property string importSeedPhrase: "ImportSeedPhrase"
|
||||||
readonly property string importPrivateKey: "ImportPrivateKey"
|
readonly property string importPrivateKey: "ImportPrivateKey"
|
||||||
}
|
}
|
||||||
|
|
|
@ -856,8 +856,7 @@ QtObject {
|
||||||
return qsTr("Restored from backup. Re-enter private key to use.")
|
return qsTr("Restored from backup. Re-enter private key to use.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (keypair.syncedFrom !== "" &&
|
if (keypair.syncedFrom !== "") {
|
||||||
keypair.syncedFrom !== Constants.keypair.syncedFrom.localPairing) {
|
|
||||||
return qsTr("Synced from %1").arg(keypair.syncedFrom)
|
return qsTr("Synced from %1").arg(keypair.syncedFrom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit b4b0d26aa4c3f11ee66e3aeb404834cd8c5e48c8
|
Subproject commit 6ee70388093f674c9d98e8d03f452ec4542d5c2f
|
Loading…
Reference in New Issue