feat(@desktop/syncing): make a not operable account fully operable, part 3
Closes the third part of #11779
This commit is contained in:
parent
85d4bfdfea
commit
a1bf7bed19
|
@ -414,6 +414,13 @@ proc connectForNotificationsOnly[T](self: Module[T]) =
|
|||
let args = KeypairArgs(e)
|
||||
self.view.showToastKeypairRemoved(args.keyPairName)
|
||||
|
||||
self.events.on(SIGNAL_IMPORTED_KEYPAIRS) do(e:Args):
|
||||
let args = KeypairsArgs(e)
|
||||
var kpName: string
|
||||
if args.keypairs.len > 0:
|
||||
kpName = args.keypairs[0].name
|
||||
self.view.showToastKeypairsImported(kpName, args.keypairs.len, args.error)
|
||||
|
||||
method load*[T](
|
||||
self: Module[T],
|
||||
events: EventEmitter,
|
||||
|
|
|
@ -56,6 +56,8 @@ proc init*(self: Controller) =
|
|||
|
||||
self.events.on(SIGNAL_LOCAL_PAIRING_STATUS_UPDATE) do(e: Args):
|
||||
let args = LocalPairingStatus(e)
|
||||
if args.pairingType != PairingType.AppSync:
|
||||
return
|
||||
self.delegate.onLocalPairingStatusUpdate(args)
|
||||
|
||||
|
||||
|
|
|
@ -111,7 +111,8 @@ proc newModule*(delegate: delegate_interface.AccessInterface,
|
|||
result.keycardModule = keycard_module.newModule(result, events, keycardService, settingsService, networkService,
|
||||
privacyService, accountsService, walletAccountService, keychainService)
|
||||
|
||||
result.walletModule = wallet_module.newModule(result, events, accountsService, walletAccountService, settingsService, networkService)
|
||||
result.walletModule = wallet_module.newModule(result, events, accountsService, walletAccountService, settingsService,
|
||||
networkService, devicesService)
|
||||
|
||||
singletonInstance.engine.setRootContextProperty("profileSectionModule", result.viewVariant)
|
||||
|
||||
|
|
|
@ -145,9 +145,12 @@ method load*(self: Module) =
|
|||
let areTestNetworksEnabled = self.controller.areTestNetworksEnabled()
|
||||
self.view.onUpdatedAccount(walletAccountToWalletAccountItem(args.account, keycardAccount, areTestNetworksEnabled))
|
||||
|
||||
self.events.on(SIGNAL_KEYPAIR_OPERABILITY_CHANGED) do(e:Args):
|
||||
let args = KeypairArgs(e)
|
||||
self.view.onUpdatedKeypairOperability(args.keypair.keyUid, AccountFullyOperable)
|
||||
self.events.on(SIGNAL_IMPORTED_KEYPAIRS) do(e:Args):
|
||||
let args = KeypairsArgs(e)
|
||||
if args.error.len != 0:
|
||||
return
|
||||
for kp in args.keypairs:
|
||||
self.view.onUpdatedKeypairOperability(kp.keyUid, AccountFullyOperable)
|
||||
|
||||
self.events.on(SIGNAL_NEW_KEYCARD_SET) do(e: Args):
|
||||
let args = KeycardArgs(e)
|
||||
|
|
|
@ -46,7 +46,7 @@ method onKeypairImportModuleLoaded*(self: AccessInterface) {.base.} =
|
|||
method destroyKeypairImportPopup*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method runKeypairImportPopup*(self: AccessInterface, keyUid: string, importOption: ImportOption) {.base.} =
|
||||
method runKeypairImportPopup*(self: AccessInterface, keyUid: string, mode: ImportKeypairModuleMode) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method hasPairedDevices*(self: AccessInterface): bool {.base.} =
|
||||
|
|
|
@ -32,6 +32,7 @@ type
|
|||
moduleLoaded: bool
|
||||
accountsService: accounts_service.Service
|
||||
walletAccountService: wallet_account_service.Service
|
||||
devicesService: devices_service.Service
|
||||
accountsModule: accounts_module.AccessInterface
|
||||
networksModule: networks_module.AccessInterface
|
||||
keypairImportModule: keypair_import_module.AccessInterface
|
||||
|
@ -43,6 +44,7 @@ proc newModule*(
|
|||
walletAccountService: wallet_account_service.Service,
|
||||
settingsService: settings_service.Service,
|
||||
networkService: network_service.Service,
|
||||
devicesService: devices_service.Service
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
|
@ -53,6 +55,7 @@ proc newModule*(
|
|||
result.moduleLoaded = false
|
||||
result.accountsService = accountsService
|
||||
result.walletAccountService = walletAccountService
|
||||
result.devicesService = devicesService
|
||||
result.accountsModule = accounts_module.newModule(result, events, walletAccountService, networkService)
|
||||
result.networksModule = networks_module.newModule(result, events, networkService, walletAccountService, settingsService)
|
||||
|
||||
|
@ -111,9 +114,10 @@ method destroyKeypairImportPopup*(self: Module) =
|
|||
self.keypairImportModule.delete
|
||||
self.keypairImportModule = nil
|
||||
|
||||
method runKeypairImportPopup*(self: Module, keyUid: string, importOption: ImportOption) =
|
||||
self.keypairImportModule = keypair_import_module.newModule(self, self.events, self.accountsService, self.walletAccountService)
|
||||
self.keypairImportModule.load(keyUid, importOption)
|
||||
method runKeypairImportPopup*(self: Module, keyUid: string, mode: ImportKeypairModuleMode) =
|
||||
self.keypairImportModule = keypair_import_module.newModule(self, self.events, self.accountsService,
|
||||
self.walletAccountService, self.devicesService)
|
||||
self.keypairImportModule.load(keyUid, mode)
|
||||
|
||||
method getKeypairImportModule*(self: Module): QVariant =
|
||||
if self.keypairImportModule.isNil:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import NimQml
|
||||
|
||||
import ./io_interface
|
||||
from app/modules/shared_modules/keypair_import/module import ImportOption
|
||||
from app/modules/shared_modules/keypair_import/module import ImportKeypairModuleMode
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -31,8 +31,8 @@ QtObject:
|
|||
QtProperty[QVariant] collectiblesModel:
|
||||
read = getCollectiblesModel
|
||||
|
||||
proc runKeypairImportPopup*(self: View, keyUid: string, importOption: int) {.slot.} =
|
||||
self.delegate.runKeypairImportPopup(keyUid, ImportOption(importOption))
|
||||
proc runKeypairImportPopup*(self: View, keyUid: string, mode: int) {.slot.} =
|
||||
self.delegate.runKeypairImportPopup(keyUid, ImportKeypairModuleMode(mode))
|
||||
|
||||
proc getKeypairImportModule(self: View): QVariant {.slot.} =
|
||||
return self.delegate.getKeypairImportModule()
|
||||
|
|
|
@ -277,3 +277,4 @@ QtObject:
|
|||
proc showNetworkEndpointUpdated*(self: View, name: string, isTest: bool) {.signal.}
|
||||
proc showIncludeWatchOnlyAccountUpdated*(self: View, includeWatchOnly: bool) {.signal.}
|
||||
proc showToastKeypairRemoved*(self: View, keypairName: string) {.signal.}
|
||||
proc showToastKeypairsImported*(self: View, keypairName: string, keypairsCount: int, error: string) {.signal.}
|
|
@ -1,9 +1,11 @@
|
|||
import times, chronicles
|
||||
import uuids
|
||||
import io_interface
|
||||
|
||||
import app/core/eventemitter
|
||||
import app_service/service/accounts/service as accounts_service
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
import app_service/service/devices/service as devices_service
|
||||
|
||||
import app/modules/shared_models/[keypair_item]
|
||||
import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module
|
||||
|
@ -19,6 +21,8 @@ type
|
|||
events: EventEmitter
|
||||
accountsService: accounts_service.Service
|
||||
walletAccountService: wallet_account_service.Service
|
||||
devicesService: devices_service.Service
|
||||
connectionIds: seq[UUID]
|
||||
uniqueFetchingDetailsId: string
|
||||
tmpPrivateKey: string
|
||||
tmpSeedPhrase: string
|
||||
|
@ -27,29 +31,37 @@ type
|
|||
proc newController*(delegate: io_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
accountsService: accounts_service.Service,
|
||||
walletAccountService: wallet_account_service.Service):
|
||||
walletAccountService: wallet_account_service.Service,
|
||||
devicesService: devices_service.Service):
|
||||
Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.accountsService = accountsService
|
||||
result.walletAccountService = walletAccountService
|
||||
result.devicesService = devicesService
|
||||
|
||||
proc disconnectAll*(self: Controller) =
|
||||
for id in self.connectionIds:
|
||||
self.events.disconnect(id)
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
discard
|
||||
self.disconnectAll()
|
||||
|
||||
proc init*(self: Controller) =
|
||||
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
|
||||
var handlerId = self.events.onWithUUID(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
|
||||
let args = SharedKeycarModuleArgs(e)
|
||||
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_KEYPAIR_IMPORT_MODULE_IDENTIFIER:
|
||||
return
|
||||
self.delegate.onUserAuthenticated(args.pin, args.password, args.keyUid)
|
||||
self.connectionIds.add(handlerId)
|
||||
|
||||
self.events.on(SIGNAL_WALLET_ACCOUNT_ADDRESS_DETAILS_FETCHED) do(e:Args):
|
||||
handlerId = self.events.onWithUUID(SIGNAL_WALLET_ACCOUNT_ADDRESS_DETAILS_FETCHED) do(e:Args):
|
||||
var args = DerivedAddressesArgs(e)
|
||||
if args.uniqueId != self.uniqueFetchingDetailsId:
|
||||
return
|
||||
self.delegate.onAddressDetailsFetched(args.derivedAddresses, args.error)
|
||||
self.connectionIds.add(handlerId)
|
||||
|
||||
proc closeKeypairImportPopup*(self: Controller) =
|
||||
self.delegate.closeKeypairImportPopup()
|
||||
|
@ -100,4 +112,19 @@ proc getKeypairs*(self: Controller): seq[wallet_account_service.KeypairDto] =
|
|||
return self.walletAccountService.getKeypairs()
|
||||
|
||||
proc getSelectedKeypair*(self: Controller): KeyPairItem =
|
||||
return self.delegate.getSelectedKeypair()
|
||||
return self.delegate.getSelectedKeypair()
|
||||
|
||||
proc clearSelectedKeypair*(self: Controller) =
|
||||
self.delegate.clearSelectedKeypair()
|
||||
|
||||
proc setConnectionString*(self: Controller, connectionString: string) =
|
||||
self.delegate.setConnectionString(connectionString)
|
||||
|
||||
proc generateConnectionStringForExportingKeypairsKeystores*(self: Controller, keyUids: seq[string], password: string): tuple[res: string, err: string] =
|
||||
return self.devicesService.generateConnectionStringForExportingKeypairsKeystores(keyUids, password)
|
||||
|
||||
proc validateConnectionString*(self: Controller, connectionString: string): string =
|
||||
return self.devicesService.validateConnectionString(connectionString)
|
||||
|
||||
proc inputConnectionStringForImportingKeypairsKeystores*(self: Controller, keyUids: seq[string], connectionString: string, password: string): string =
|
||||
return self.devicesService.inputConnectionStringForImportingKeypairsKeystores(keyUids, connectionString, password)
|
|
@ -0,0 +1,9 @@
|
|||
type
|
||||
DisplayInstructionsState* = ref object of State
|
||||
|
||||
proc newDisplayInstructionsState*(backState: State): DisplayInstructionsState =
|
||||
result = DisplayInstructionsState()
|
||||
result.setup(StateType.DisplayInstructions, backState)
|
||||
|
||||
proc delete*(self: DisplayInstructionsState) =
|
||||
self.State.delete
|
|
@ -0,0 +1,12 @@
|
|||
type
|
||||
ExportKeypairState* = ref object of State
|
||||
|
||||
proc newExportKeypairState*(backState: State): ExportKeypairState =
|
||||
result = ExportKeypairState()
|
||||
result.setup(StateType.ExportKeypair, backState)
|
||||
|
||||
proc delete*(self: ExportKeypairState) =
|
||||
self.State.delete
|
||||
|
||||
method executePrePrimaryStateCommand*(self: ExportKeypairState, controller: Controller) =
|
||||
controller.closeKeypairImportPopup()
|
|
@ -0,0 +1,21 @@
|
|||
type
|
||||
ImportQrState* = ref object of State
|
||||
|
||||
proc newImportQrState*(backState: State): ImportQrState =
|
||||
result = ImportQrState()
|
||||
result.setup(StateType.ImportQr, backState)
|
||||
|
||||
proc delete*(self: ImportQrState) =
|
||||
self.State.delete
|
||||
|
||||
method executePreBackStateCommand*(self: ImportQrState, controller: Controller) =
|
||||
controller.setConnectionString("")
|
||||
|
||||
method getNextPrimaryState*(self: ImportQrState, controller: Controller): State =
|
||||
controller.authenticateLoggedInUser()
|
||||
|
||||
method executePreSecondaryStateCommand*(self: ImportQrState, controller: Controller) =
|
||||
controller.setConnectionString("")
|
||||
|
||||
method getNextSecondaryState*(self: ImportQrState, controller: Controller): State =
|
||||
return createState(StateType.DisplayInstructions, self)
|
|
@ -1,12 +0,0 @@
|
|||
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
|
|
@ -8,8 +8,11 @@ proc newSelectImportMethodState*(backState: State): SelectImportMethodState =
|
|||
proc delete*(self: SelectImportMethodState) =
|
||||
self.State.delete
|
||||
|
||||
method executePreBackStateCommand*(self: SelectImportMethodState, controller: Controller) =
|
||||
controller.clearSelectedKeypair()
|
||||
|
||||
method getNextPrimaryState*(self: SelectImportMethodState, controller: Controller): State =
|
||||
return createState(StateType.ScanQr, self)
|
||||
return createState(StateType.ImportQr, self)
|
||||
|
||||
method getNextSecondaryState*(self: SelectImportMethodState, controller: Controller): State =
|
||||
let kp = controller.getSelectedKeypair()
|
||||
|
|
|
@ -8,5 +8,11 @@ proc newSelectKeypairState*(backState: State): SelectKeypairState =
|
|||
proc delete*(self: SelectKeypairState) =
|
||||
self.State.delete
|
||||
|
||||
method executePreBackStateCommand*(self: SelectKeypairState, controller: Controller) =
|
||||
controller.clearSelectedKeypair()
|
||||
|
||||
method getNextPrimaryState*(self: SelectKeypairState, controller: Controller): State =
|
||||
return createState(StateType.SelectImportMethod, self)
|
||||
return createState(StateType.SelectImportMethod, self)
|
||||
|
||||
method getNextSecondaryState*(self: SelectKeypairState, controller: Controller): State =
|
||||
return createState(StateType.ImportQr, self)
|
|
@ -4,9 +4,11 @@ type StateType* {.pure.} = enum
|
|||
NoState = "NoState"
|
||||
SelectKeypair = "SelectKeypair"
|
||||
SelectImportMethod = "SelectImportMethod"
|
||||
ScanQr = "ScanQr"
|
||||
ExportKeypair = "ExportKeypair"
|
||||
ImportQr = "ImportQr"
|
||||
ImportSeedPhrase = "ImportSeedPhrase"
|
||||
ImportPrivateKey = "ImportPrivateKey"
|
||||
DisplayInstructions = "DisplayInstructions"
|
||||
|
||||
|
||||
## This is the base class for all states
|
||||
|
|
|
@ -12,20 +12,26 @@ proc createState*(stateToBeCreated: StateType, backState: State): State
|
|||
|
||||
include select_keypair_state
|
||||
include select_import_method_state
|
||||
include scan_qr_state
|
||||
include export_keypair_state
|
||||
include import_qr_state
|
||||
include import_private_key_state
|
||||
include import_seed_phrase_state
|
||||
include display_instructions_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.ExportKeypair:
|
||||
return newExportKeypairState(backState)
|
||||
if stateToBeCreated == StateType.ImportQr:
|
||||
return newImportQrState(backState)
|
||||
if stateToBeCreated == StateType.ImportPrivateKey:
|
||||
return newImportPrivateKeyState(backState)
|
||||
if stateToBeCreated == StateType.ImportSeedPhrase:
|
||||
return newImportSeedPhraseState(backState)
|
||||
if stateToBeCreated == StateType.DisplayInstructions:
|
||||
return newDisplayInstructionsState(backState)
|
||||
|
||||
error "Keypair import - no implementation available for state", state=stateToBeCreated
|
||||
|
|
|
@ -3,11 +3,12 @@ import Tables, NimQml
|
|||
import app/modules/shared_models/[keypair_item]
|
||||
import app_service/service/wallet_account/dto/derived_address_dto
|
||||
|
||||
type ImportOption* {.pure.}= enum
|
||||
type ImportKeypairModuleMode* {.pure.}= enum
|
||||
SelectKeypair = 1
|
||||
SeedPhrase
|
||||
PrivateKey
|
||||
QrCode
|
||||
ImportViaSeedPhrase
|
||||
ImportViaPrivateKey
|
||||
ImportViaQr
|
||||
ExportKeypairQr
|
||||
|
||||
type
|
||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||
|
@ -15,7 +16,7 @@ type
|
|||
method delete*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method load*(self: AccessInterface, keyUid: string, importOption: ImportOption) {.base.} =
|
||||
method load*(self: AccessInterface, keyUid: string, mode: ImportKeypairModuleMode) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method closeKeypairImportPopup*(self: AccessInterface) {.base.} =
|
||||
|
@ -54,9 +55,22 @@ method onAddressDetailsFetched*(self: AccessInterface, derivedAddresses: seq[Der
|
|||
method getSelectedKeypair*(self: AccessInterface): KeyPairItem {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method clearSelectedKeypair*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setConnectionString*(self: AccessInterface, connectionString: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setSelectedKeyPairByKeyUid*(self: AccessInterface, keyUid: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method generateConnectionStringForExporting*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method validateConnectionString*(self: AccessInterface, connectionString: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
|
||||
type
|
||||
DelegateInterface* = concept c
|
||||
c.onKeypairImportModuleLoaded()
|
||||
|
|
|
@ -10,6 +10,7 @@ import app/modules/shared/keypairs
|
|||
import app/modules/shared_models/[keypair_model, derived_address_model]
|
||||
import app_service/service/accounts/service as accounts_service
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
import app_service/service/devices/service as devices_service
|
||||
|
||||
export io_interface
|
||||
|
||||
|
@ -23,17 +24,19 @@ type
|
|||
view: View
|
||||
viewVariant: QVariant
|
||||
controller: Controller
|
||||
tmpPassword: string
|
||||
|
||||
proc newModule*[T](delegate: T,
|
||||
events: EventEmitter,
|
||||
accountsService: accounts_service.Service,
|
||||
walletAccountService: wallet_account_service.Service):
|
||||
walletAccountService: wallet_account_service.Service,
|
||||
devicesService: devices_service.Service):
|
||||
Module[T] =
|
||||
result = Module[T]()
|
||||
result.delegate = delegate
|
||||
result.view = newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, events, accountsService, walletAccountService)
|
||||
result.controller = controller.newController(result, events, accountsService, walletAccountService, devicesService)
|
||||
|
||||
method delete*[T](self: Module[T]) =
|
||||
self.view.delete
|
||||
|
@ -46,14 +49,23 @@ method closeKeypairImportPopup*[T](self: Module[T]) =
|
|||
method getModuleAsVariant*[T](self: Module[T]): QVariant =
|
||||
return self.viewVariant
|
||||
|
||||
method load*[T](self: Module[T], keyUid: string, importOption: ImportOption) =
|
||||
method load*[T](self: Module[T], keyUid: string, mode: ImportKeypairModuleMode) =
|
||||
self.controller.init()
|
||||
if importOption == ImportOption.SelectKeypair:
|
||||
if mode == ImportKeypairModuleMode.SelectKeypair:
|
||||
let items = keypairs.buildKeyPairsList(self.controller.getKeypairs(), excludeAlreadyMigratedPairs = true,
|
||||
excludePrivateKeyKeypairs = false)
|
||||
self.view.createKeypairModel(items)
|
||||
self.view.setCurrentState(newSelectKeypairState(nil))
|
||||
else:
|
||||
self.view.setSelectedKeypairItem(newKeyPairItem(keyUid = keyUid))
|
||||
if mode == ImportKeypairModuleMode.ExportKeypairQr:
|
||||
self.view.setCurrentState(newExportKeypairState(nil))
|
||||
self.controller.authenticateLoggedInUser()
|
||||
return
|
||||
elif mode == ImportKeypairModuleMode.ImportViaQr:
|
||||
self.view.setCurrentState(newImportQrState(nil))
|
||||
self.delegate.onKeypairImportModuleLoaded()
|
||||
return
|
||||
let keypair = self.controller.getKeypairByKeyUid(keyUid)
|
||||
if keypair.isNil:
|
||||
error "ki_trying to import an unknown keypair"
|
||||
|
@ -65,9 +77,9 @@ method load*[T](self: Module[T], keyUid: string, importOption: ImportOption) =
|
|||
self.closeKeypairImportPopup()
|
||||
return
|
||||
self.view.setSelectedKeypairItem(keypairItem)
|
||||
if importOption == ImportOption.PrivateKey:
|
||||
if mode == ImportKeypairModuleMode.ImportViaPrivateKey:
|
||||
self.view.setCurrentState(newImportPrivateKeyState(nil))
|
||||
elif importOption == ImportOption.SeedPhrase:
|
||||
elif mode == ImportKeypairModuleMode.ImportViaSeedPhrase:
|
||||
self.view.setCurrentState(newImportSeedPhraseState(nil))
|
||||
self.delegate.onKeypairImportModuleLoaded()
|
||||
|
||||
|
@ -118,12 +130,12 @@ method onSecondaryActionClicked*[T](self: Module[T]) =
|
|||
self.view.setCurrentState(nextState)
|
||||
debug "ki_secondary_action - set state", setCurrState=nextState.stateType()
|
||||
|
||||
proc authenticateLoggedInUser[T](self: Module[T]) =
|
||||
self.controller.authenticateLoggedInUser()
|
||||
|
||||
method getSelectedKeypair*[T](self: Module[T]): KeyPairItem =
|
||||
return self.view.getSelectedKeypair()
|
||||
|
||||
method clearSelectedKeypair*[T](self: Module[T]) =
|
||||
self.view.setSelectedKeypairItem(newKeyPairItem())
|
||||
|
||||
method setSelectedKeyPairByKeyUid*[T](self: Module[T], keyUid: string) =
|
||||
let item = self.view.keypairModel().findItemByKeyUid(keyUid)
|
||||
if item.isNil:
|
||||
|
@ -191,6 +203,30 @@ method onAddressDetailsFetched*[T](self: Module[T], derivedAddresses: seq[Derive
|
|||
return
|
||||
error "ki_unknown error, since the length of the response is not expected", length=derivedAddresses.len
|
||||
|
||||
method setConnectionString*[T](self: Module[T], connectionString: string) =
|
||||
self.view.setConnectionString(connectionString)
|
||||
|
||||
method generateConnectionStringForExporting*[T](self: Module[T]) =
|
||||
self.view.setConnectionString("")
|
||||
self.view.setConnectionStringError("")
|
||||
let currStateObj = self.view.currentStateObj()
|
||||
if currStateObj.isNil:
|
||||
error "ki_cannot resolve current state"
|
||||
return
|
||||
if currStateObj.stateType() == StateType.ExportKeypair:
|
||||
var keyUids: seq[string]
|
||||
let keyUid = self.view.getSelectedKeypair().getKeyUid()
|
||||
if keyUid.len > 0:
|
||||
keyUids.add(keyUid)
|
||||
let (connectionString, err) = self.controller.generateConnectionStringForExportingKeypairsKeystores(keyUids, self.tmpPassword)
|
||||
if err.len > 0:
|
||||
self.view.setConnectionStringError(err)
|
||||
return
|
||||
self.view.setConnectionString(connectionString)
|
||||
|
||||
method validateConnectionString*[T](self: Module[T], connectionString: string): string =
|
||||
return self.controller.validateConnectionString(connectionString)
|
||||
|
||||
method onUserAuthenticated*[T](self: Module[T], pin: string, password: string, keyUid: string) =
|
||||
if password.len == 0:
|
||||
info "ki_unsuccessful authentication"
|
||||
|
@ -199,13 +235,27 @@ method onUserAuthenticated*[T](self: Module[T], pin: string, password: string, k
|
|||
if currStateObj.isNil:
|
||||
error "ki_cannot resolve current state"
|
||||
return
|
||||
if currStateObj.stateType() == StateType.ExportKeypair:
|
||||
self.tmpPassword = password
|
||||
self.generateConnectionStringForExporting()
|
||||
self.delegate.onKeypairImportModuleLoaded()
|
||||
return
|
||||
if currStateObj.stateType() == StateType.ImportQr:
|
||||
var keyUids: seq[string]
|
||||
let keyUid = self.view.getSelectedKeypair().getKeyUid()
|
||||
if keyUid.len > 0:
|
||||
keyUids.add(keyUid)
|
||||
let res = self.controller.inputConnectionStringForImportingKeypairsKeystores(keyUids, self.view.getConnectionString(), password)
|
||||
if res.len > 0:
|
||||
error "ki_unable to make a keypair operable", errDesription=res
|
||||
return
|
||||
if currStateObj.stateType() == StateType.ImportPrivateKey:
|
||||
let res = self.controller.makePrivateKeyKeypairFullyOperable(self.controller.getGeneratedAccount().keyUid,
|
||||
self.controller.getPrivateKey(),
|
||||
password,
|
||||
doPasswordHashing = not singletonInstance.userProfile.getIsKeycardUser())
|
||||
if res.len > 0:
|
||||
error "ki_unable to make a keypair operable"
|
||||
error "ki_unable to make a keypair operable", errDesription=res
|
||||
return
|
||||
if currStateObj.stateType() == StateType.ImportSeedPhrase:
|
||||
let res = self.controller.makeSeedPhraseKeypairFullyOperable(self.controller.getGeneratedAccount().keyUid,
|
||||
|
@ -213,6 +263,6 @@ method onUserAuthenticated*[T](self: Module[T], pin: string, password: string, k
|
|||
password,
|
||||
doPasswordHashing = not singletonInstance.userProfile.getIsKeycardUser())
|
||||
if res.len > 0:
|
||||
error "ki_unable to make a keypair operable"
|
||||
error "ki_unable to make a keypair operable", errDesription=res
|
||||
return
|
||||
self.closeKeypairImportPopup()
|
||||
self.closeKeypairImportPopup()
|
||||
|
|
|
@ -16,6 +16,8 @@ QtObject:
|
|||
privateKeyAccAddress: DerivedAddressItem
|
||||
privateKeyAccAddressVariant: QVariant
|
||||
enteredPrivateKeyMatchTheKeypair: bool
|
||||
connectionString: string
|
||||
connectionStringError: string
|
||||
|
||||
proc delete*(self: View) =
|
||||
self.currentStateVariant.delete
|
||||
|
@ -132,4 +134,31 @@ QtObject:
|
|||
if self.keypairModelVariant.isNil:
|
||||
self.keypairModelVariant = newQVariant(self.keypairModel)
|
||||
self.keypairModel.setItems(items)
|
||||
self.keypairModelChanged()
|
||||
self.keypairModelChanged()
|
||||
|
||||
proc connectionStringChanged(self: View) {.signal.}
|
||||
proc getConnectionString*(self: View): string {.slot.} =
|
||||
return self.connectionString
|
||||
proc setConnectionString*(self: View, connectionScreen: string) {.slot.} =
|
||||
self.connectionString = connectionScreen
|
||||
self.connectionStringChanged()
|
||||
QtProperty[string] connectionString:
|
||||
read = getConnectionString
|
||||
write = setConnectionString
|
||||
notify = connectionStringChanged
|
||||
|
||||
proc connectionStringErrorChanged(self: View) {.signal.}
|
||||
proc getConnectionStringError*(self: View): string {.slot.} =
|
||||
return self.connectionStringError
|
||||
QtProperty[string] connectionStringError:
|
||||
read = getConnectionStringError
|
||||
notify = connectionStringErrorChanged
|
||||
proc setConnectionStringError*(self: View, error: string) =
|
||||
self.connectionStringError = error
|
||||
self.connectionStringErrorChanged()
|
||||
|
||||
proc generateConnectionStringForExporting*(self: View) {.slot.} =
|
||||
self.delegate.generateConnectionStringForExporting()
|
||||
|
||||
proc validateConnectionString*(self: View, connectionString: string): string {.slot.} =
|
||||
return self.delegate.validateConnectionString(connectionString)
|
|
@ -168,7 +168,10 @@ proc init*(self: Controller) =
|
|||
self.connectionIds.add(handlerId)
|
||||
|
||||
handlerId = self.events.onWithUUID(SIGNAL_LOCAL_PAIRING_STATUS_UPDATE) do(e: Args):
|
||||
self.localPairingStatus = LocalPairingStatus(e)
|
||||
let args = LocalPairingStatus(e)
|
||||
if args.pairingType != PairingType.AppSync:
|
||||
return
|
||||
self.localPairingStatus = args
|
||||
self.delegate.onLocalPairingStatusUpdate(self.localPairingStatus)
|
||||
self.connectionIds.add(handlerId)
|
||||
|
||||
|
|
|
@ -13,6 +13,19 @@ Item {
|
|||
implicitWidth: layout.implicitWidth
|
||||
implicitHeight: layout.implicitHeight
|
||||
|
||||
Timer {
|
||||
id: nextStateDelay
|
||||
|
||||
property string connectionString
|
||||
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
root.startupStore.setConnectionString(nextStateDelay.connectionString)
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
SyncingEnterCode {
|
||||
id: layout
|
||||
|
||||
|
@ -26,8 +39,8 @@ Item {
|
|||
}
|
||||
|
||||
onProceed: {
|
||||
root.startupStore.setConnectionString(connectionString)
|
||||
root.startupStore.doPrimaryAction()
|
||||
nextStateDelay.connectionString = connectionString
|
||||
nextStateDelay.start()
|
||||
}
|
||||
|
||||
onDisplayInstructions: {
|
||||
|
|
|
@ -15,12 +15,15 @@ Rectangle {
|
|||
id: root
|
||||
|
||||
property var keyPair
|
||||
property bool hasPairedDevices
|
||||
property var getNetworkShortNames: function(chainIds){}
|
||||
property string userProfilePublicKey
|
||||
property bool includeWatchOnlyAccount
|
||||
|
||||
signal goToAccountView(var account)
|
||||
signal toggleIncludeWatchOnlyAccount()
|
||||
signal runExportQrFlow()
|
||||
signal runImportViaQrFlow()
|
||||
signal runImportViaSeedPhraseFlow()
|
||||
signal runImportViaPrivateKeyFlow()
|
||||
signal runRenameKeypairFlow()
|
||||
|
@ -82,6 +85,9 @@ Rectangle {
|
|||
menuLoader.active = false
|
||||
}
|
||||
keyPair: root.keyPair
|
||||
hasPairedDevices: root.hasPairedDevices
|
||||
onRunExportQrFlow: root.runExportQrFlow()
|
||||
onRunImportViaQrFlow: root.runImportViaQrFlow()
|
||||
onRunImportViaSeedPhraseFlow: root.runImportViaSeedPhraseFlow()
|
||||
onRunImportViaPrivateKeyFlow: root.runImportViaPrivateKeyFlow()
|
||||
onRunRenameKeypairFlow: root.runRenameKeypairFlow()
|
||||
|
|
|
@ -196,8 +196,12 @@ StatusDialog {
|
|||
id: displaySyncCodeView
|
||||
anchors.fill: parent
|
||||
visible: displaySyncCodeState.active
|
||||
|
||||
connectionStringLabel: qsTr("Sync code")
|
||||
connectionString: d.connectionString
|
||||
secondsTimeout: 5 * 60
|
||||
importCodeInstructions: qsTr("On your other device, navigate to the Syncing<br>screen and select Enter Sync Code.")
|
||||
codeExpiredMessage: qsTr("Your QR and Sync Code have expired.")
|
||||
|
||||
onRequestConnectionString: {
|
||||
d.generateConnectionString()
|
||||
}
|
||||
|
@ -225,7 +229,8 @@ StatusDialog {
|
|||
anchors.fill: parent
|
||||
visible: errorState.active
|
||||
primaryText: qsTr("Failed to generate sync code")
|
||||
secondaryText: d.errorMessage
|
||||
secondaryText: qsTr("Failed to start pairing server")
|
||||
errorDetails: d.errorMessage
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,24 +9,26 @@ StatusMenu {
|
|||
id: root
|
||||
|
||||
property var keyPair
|
||||
property bool hasPairedDevices: false
|
||||
|
||||
signal runExportQrFlow()
|
||||
signal runImportViaQrFlow()
|
||||
signal runImportViaSeedPhraseFlow()
|
||||
signal runImportViaPrivateKeyFlow()
|
||||
signal runRenameKeypairFlow()
|
||||
signal runRemoveKeypairFlow()
|
||||
|
||||
StatusAction {
|
||||
text: enabled? qsTr("Show encrypted QR of keypairs on device") : ""
|
||||
enabled: !!root.keyPair &&
|
||||
text: enabled? qsTr("Show encrypted QR on device") : ""
|
||||
enabled: root.hasPairedDevices &&
|
||||
!!root.keyPair &&
|
||||
root.keyPair.pairType !== Constants.keypair.type.profile &&
|
||||
!root.keyPair.migratedToKeycard &&
|
||||
root.keyPair.operability !== Constants.keypair.operability.nonOperable
|
||||
icon.name: "qr"
|
||||
icon.color: Theme.palette.primaryColor1
|
||||
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")
|
||||
root.runExportQrFlow()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,15 +48,15 @@ StatusMenu {
|
|||
|
||||
StatusAction {
|
||||
text: enabled? qsTr("Import keypair from device via encrypted QR") : ""
|
||||
enabled: !!root.keyPair &&
|
||||
enabled: root.hasPairedDevices &&
|
||||
!!root.keyPair &&
|
||||
root.keyPair.pairType !== Constants.keypair.type.profile &&
|
||||
!root.keyPair.migratedToKeycard &&
|
||||
root.keyPair.operability === Constants.keypair.operability.nonOperable &&
|
||||
root.keyPair.syncedFrom !== Constants.keypair.syncedFrom.backup
|
||||
root.keyPair.operability === Constants.keypair.operability.nonOperable
|
||||
icon.name: "qr-scan"
|
||||
icon.color: Theme.palette.primaryColor1
|
||||
onTriggered: {
|
||||
console.warn("TODO: run import via encrypted QR")
|
||||
root.runImportViaQrFlow()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ QtObject {
|
|||
}
|
||||
|
||||
property string userProfilePublicKey: userProfile.pubKey
|
||||
|
||||
|
||||
function deleteAccount(address) {
|
||||
return accountsModule.deleteAccount(address)
|
||||
}
|
||||
|
@ -67,8 +67,8 @@ QtObject {
|
|||
walletSection.runAddAccountPopup(false)
|
||||
}
|
||||
|
||||
function runKeypairImportPopup(keyUid, importOption) {
|
||||
root.walletModule.runKeypairImportPopup(keyUid, importOption)
|
||||
function runKeypairImportPopup(keyUid, mode) {
|
||||
root.walletModule.runKeypairImportPopup(keyUid, mode)
|
||||
}
|
||||
|
||||
function evaluateRpcEndPoint(url) {
|
||||
|
|
|
@ -291,6 +291,7 @@ ColumnLayout {
|
|||
WalletKeypairAccountMenu {
|
||||
id: keycardMenu
|
||||
keyPair: root.keyPair
|
||||
hasPairedDevices: root.walletStore.walletModule.hasPairedDevices
|
||||
onRunRenameKeypairFlow: root.runRenameKeypairFlow()
|
||||
onRunRemoveKeypairFlow: root.runRemoveKeypairFlow()
|
||||
}
|
||||
|
|
|
@ -42,6 +42,40 @@ Column {
|
|||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property int unimportedNonProfileKeypairs: {
|
||||
let total = 0
|
||||
for (var i = 0; i < keypairsRepeater.count; i++) {
|
||||
let kp = keypairsRepeater.itemAt(i).keyPair
|
||||
if (kp.migratedToKeycard ||
|
||||
kp.pairType === Constants.keypair.type.profile ||
|
||||
kp.pairType === Constants.keypair.type.watchOnly ||
|
||||
kp.operability === Constants.keypair.operability.fullyOperable ||
|
||||
kp.operability === Constants.keypair.operability.partiallyOperable) {
|
||||
continue
|
||||
}
|
||||
total++
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
readonly property int allNonProfileKeypairsMigratedToAKeycard: {
|
||||
for (var i = 0; i < keypairsRepeater.count; i++) {
|
||||
let kp = keypairsRepeater.itemAt(i).keyPair
|
||||
if (!kp.migratedToKeycard &&
|
||||
kp.pairType !== Constants.keypair.type.profile &&
|
||||
kp.pairType !== Constants.keypair.type.watchOnly &&
|
||||
kp.operability !== Constants.keypair.operability.nonOperable) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
component Spacer: Item {
|
||||
height: 8
|
||||
}
|
||||
|
@ -102,7 +136,8 @@ Column {
|
|||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.walletStore.walletModule.hasPairedDevices
|
||||
visible: root.walletStore.walletModule.hasPairedDevices &&
|
||||
!d.allNonProfileKeypairsMigratedToAKeycard
|
||||
height: 102
|
||||
width: parent.width
|
||||
color: Theme.palette.transparent
|
||||
|
@ -124,15 +159,15 @@ Column {
|
|||
text: qsTr("Show encrypted QR of keypairs on device")
|
||||
icon.name: "qr"
|
||||
onClicked: {
|
||||
console.warn("TODO: run generate qr code flow...")
|
||||
root.walletStore.runKeypairImportPopup("", Constants.keypairImportPopup.mode.exportKeypairQr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: importMissingKeypairs
|
||||
visible: importMissingKeypairs.unimportedKeypairs > 0
|
||||
visible: root.walletStore.walletModule.hasPairedDevices &&
|
||||
d.unimportedNonProfileKeypairs > 0
|
||||
height: 102
|
||||
width: parent.width
|
||||
color: Theme.palette.transparent
|
||||
|
@ -140,27 +175,13 @@ Column {
|
|||
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)
|
||||
text: qsTr("%n keypair(s) require import to use on this device", "", d.unimportedNonProfileKeypairs)
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
|
@ -169,7 +190,7 @@ Column {
|
|||
type: StatusBaseButton.Type.Warning
|
||||
icon.name: "download"
|
||||
onClicked: {
|
||||
root.walletStore.runKeypairImportPopup("", Constants.keypairImportPopup.importOption.selectKeypair)
|
||||
root.walletStore.runKeypairImportPopup("", Constants.keypairImportPopup.mode.selectKeypair)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,6 +210,7 @@ Column {
|
|||
delegate: WalletKeyPairDelegate {
|
||||
width: parent.width
|
||||
keyPair: model.keyPair
|
||||
hasPairedDevices: root.walletStore.walletModule.hasPairedDevices
|
||||
getNetworkShortNames: walletStore.getNetworkShortNames
|
||||
userProfilePublicKey: walletStore.userProfilePublicKey
|
||||
includeWatchOnlyAccount: walletStore.includeWatchOnlyAccount
|
||||
|
@ -197,10 +219,16 @@ Column {
|
|||
onRunRenameKeypairFlow: root.runRenameKeypairFlow(model)
|
||||
onRunRemoveKeypairFlow: root.runRemoveKeypairFlow(model)
|
||||
onRunImportViaSeedPhraseFlow: {
|
||||
root.walletStore.runKeypairImportPopup(model.keyPair.keyUid, Constants.keypairImportPopup.importOption.seedPhrase)
|
||||
root.walletStore.runKeypairImportPopup(model.keyPair.keyUid, Constants.keypairImportPopup.mode.importViaSeedPhrase)
|
||||
}
|
||||
onRunImportViaPrivateKeyFlow: {
|
||||
root.walletStore.runKeypairImportPopup(model.keyPair.keyUid, Constants.keypairImportPopup.importOption.privateKey)
|
||||
root.walletStore.runKeypairImportPopup(model.keyPair.keyUid, Constants.keypairImportPopup.mode.importViaPrivateKey)
|
||||
}
|
||||
onRunExportQrFlow: {
|
||||
root.walletStore.runKeypairImportPopup(model.keyPair.keyUid, Constants.keypairImportPopup.mode.exportKeypairQr)
|
||||
}
|
||||
onRunImportViaQrFlow: {
|
||||
root.walletStore.runKeypairImportPopup(model.keyPair.keyUid, Constants.keypairImportPopup.mode.importViaQr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,6 +148,29 @@ Item {
|
|||
""
|
||||
)
|
||||
}
|
||||
|
||||
function onShowToastKeypairsImported(keypairName: string, keypairsCount: int, error: string) {
|
||||
let notification = qsTr("Please re-generate QR code and try importing again")
|
||||
if (error !== "") {
|
||||
if (error.startsWith("one or more expected keystore files are not found among the sent files")) {
|
||||
notification = qsTr("Make sure you're importing the exported keypair on paired device")
|
||||
}
|
||||
}
|
||||
else {
|
||||
notification = qsTr("%1 keypair successfully imported").arg(keypairName)
|
||||
if (keypairsCount > 1) {
|
||||
notification = qsTr("%n keypair(s) successfully imported", "", keypairsCount)
|
||||
}
|
||||
}
|
||||
Global.displayToastMessage(
|
||||
notification,
|
||||
"",
|
||||
error!==""? "info" : "checkmark-circle",
|
||||
false,
|
||||
error!==""? Constants.ephemeralNotificationType.normal : Constants.ephemeralNotificationType.success,
|
||||
""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
|
|
|
@ -6,9 +6,13 @@ import StatusQ.Components 0.1
|
|||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import shared.views 1.0
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property int type: SyncingCodeInstructions.Type.AppSync
|
||||
|
||||
spacing: 4
|
||||
|
||||
QtObject {
|
||||
|
@ -45,7 +49,6 @@ Column {
|
|||
color: Theme.palette.directColor1
|
||||
text: qsTr("Settings")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
@ -58,11 +61,21 @@ Column {
|
|||
text: qsTr("3. Navigate to the ")
|
||||
}
|
||||
StatusRoundIcon {
|
||||
asset.name: "rotate"
|
||||
asset.name: {
|
||||
if (root.type === SyncingCodeInstructions.Type.KeypairSync) {
|
||||
return "wallet"
|
||||
}
|
||||
return "rotate"
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("Syncing tab")
|
||||
text: {
|
||||
if (root.type === SyncingCodeInstructions.Type.KeypairSync) {
|
||||
return qsTr("Wallet tab")
|
||||
}
|
||||
return qsTr("Syncing tab")
|
||||
}
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
@ -79,7 +92,12 @@ Column {
|
|||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("Setup Syncing")
|
||||
text: {
|
||||
if (root.type === SyncingCodeInstructions.Type.KeypairSync) {
|
||||
return qsTr("Import missing keypairs")
|
||||
}
|
||||
return qsTr("Setup Syncing")
|
||||
}
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
@ -90,7 +108,35 @@ Column {
|
|||
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("5. Scan or enter the code ")
|
||||
text: qsTr("5.")
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("Enable camera")
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("on this device")
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
height: d.listItemHeight
|
||||
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: {
|
||||
if (root.type === SyncingCodeInstructions.Type.KeypairSync) {
|
||||
return qsTr("6. Scan or enter the encrypted key with this device")
|
||||
}
|
||||
return qsTr("6. Scan or enter the code")
|
||||
}
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
|
|
|
@ -6,9 +6,13 @@ import StatusQ.Components 0.1
|
|||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import shared.views 1.0
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property int type: SyncingCodeInstructions.Type.AppSync
|
||||
|
||||
spacing: 4
|
||||
|
||||
QtObject {
|
||||
|
@ -34,7 +38,7 @@ Column {
|
|||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
text: qsTr("2. Open your")
|
||||
text: qsTr("2. Open")
|
||||
}
|
||||
StatusRoundIcon {
|
||||
asset.name: "profile"
|
||||
|
@ -43,9 +47,8 @@ Column {
|
|||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
text: qsTr("Profile")
|
||||
text: qsTr("Settings")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
@ -58,11 +61,21 @@ Column {
|
|||
text: qsTr("3. Go to")
|
||||
}
|
||||
StatusRoundIcon {
|
||||
asset.name: "rotate"
|
||||
asset.name: {
|
||||
if (root.type === SyncingCodeInstructions.Type.KeypairSync) {
|
||||
return "wallet"
|
||||
}
|
||||
return "rotate"
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("Syncing")
|
||||
text: {
|
||||
if (root.type === SyncingCodeInstructions.Type.KeypairSync) {
|
||||
return qsTr("Wallet")
|
||||
}
|
||||
return qsTr("Syncing")
|
||||
}
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
@ -79,7 +92,12 @@ Column {
|
|||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("Sync new device")
|
||||
text: {
|
||||
if (root.type === SyncingCodeInstructions.Type.KeypairSync) {
|
||||
return qsTr("Import missing keypairs")
|
||||
}
|
||||
return qsTr("Sync new device")
|
||||
}
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
@ -90,7 +108,35 @@ Column {
|
|||
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("5. Scan or enter the code ")
|
||||
text: qsTr("5.")
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("Enable camera")
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: qsTr("on this device")
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
height: d.listItemHeight
|
||||
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: {
|
||||
if (root.type === SyncingCodeInstructions.Type.KeypairSync) {
|
||||
return qsTr("6. Scan or enter the encrypted key with this device")
|
||||
}
|
||||
return qsTr("6. Scan or enter the code")
|
||||
}
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
|
|
|
@ -14,17 +14,10 @@ StatusInput {
|
|||
ReadMode
|
||||
}
|
||||
|
||||
property int mode: StatusSyncCodeInput.Mode.WriteMode
|
||||
required property int mode
|
||||
property bool readOnly: false
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property bool writeMode: root.mode === StatusSyncCodeInput.Mode.WriteMode
|
||||
}
|
||||
|
||||
label: d.writeMode ? qsTr("Paste sync code") : qsTr("Sync code")
|
||||
input.edit.readOnly: root.readOnly
|
||||
input.placeholderText: d.writeMode ? qsTr("eg. %1").arg("0x2Ef19") : ""
|
||||
input.font: Theme.palette.monoFont.name
|
||||
input.placeholderFont: root.input.font
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ StatusListItem {
|
|||
property bool tagDisplayRemoveAccountButton: false
|
||||
property bool canBeSelected: true
|
||||
property bool displayRadioButtonForSelection: true
|
||||
property bool useTransparentItemBackgroundColor: false
|
||||
|
||||
property int keyPairType: Constants.keycard.keyPairType.unknown
|
||||
property string keyPairKeyUid: ""
|
||||
|
@ -33,7 +34,17 @@ StatusListItem {
|
|||
signal removeAccount(int index, string name)
|
||||
signal accountClicked(int index)
|
||||
|
||||
color: root.keyPairCardLocked? Theme.palette.dangerColor3 : Theme.palette.baseColor2
|
||||
color: {
|
||||
if (!root.useTransparentItemBackgroundColor) {
|
||||
return root.keyPairCardLocked? Theme.palette.dangerColor3 : Theme.palette.baseColor2
|
||||
}
|
||||
|
||||
if (sensor.containsMouse) {
|
||||
return Theme.palette.baseColor2
|
||||
}
|
||||
|
||||
return Theme.palette.transparent
|
||||
}
|
||||
title: root.keyPairName
|
||||
statusListItemTitleAside.textFormat: Text.RichText
|
||||
statusListItemTitleAside.visible: !!statusListItemTitleAside.text
|
||||
|
|
|
@ -12,6 +12,7 @@ Item {
|
|||
property ButtonGroup buttonGroup
|
||||
property bool disableSelectionForKeypairsWithNonDefaultDerivationPath: true
|
||||
property bool displayRadioButtonForSelection: true
|
||||
property bool useTransparentItemBackgroundColor: false
|
||||
property string optionLabel: ""
|
||||
property alias modelFilters: proxyModel.filters
|
||||
|
||||
|
@ -36,6 +37,7 @@ Item {
|
|||
canBeSelected: !root.disableSelectionForKeypairsWithNonDefaultDerivationPath ||
|
||||
!model.keyPair.containsPathOutOfTheDefaultStatusDerivationTree()
|
||||
displayRadioButtonForSelection: root.displayRadioButtonForSelection
|
||||
useTransparentItemBackgroundColor: root.useTransparentItemBackgroundColor
|
||||
|
||||
keyPairType: model.keyPair.pairType
|
||||
keyPairKeyUid: model.keyPair.keyUid
|
||||
|
|
|
@ -26,8 +26,12 @@ StatusModal {
|
|||
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:
|
||||
case Constants.keypairImportPopup.state.exportKeypair:
|
||||
return qsTr("Encrypted QR for keypairs on this device")
|
||||
case Constants.keypairImportPopup.state.importQr:
|
||||
return qsTr("Scan encrypted QR")
|
||||
case Constants.keypairImportPopup.state.displayInstructions:
|
||||
return qsTr("How to generate the encrypted QR")
|
||||
}
|
||||
|
||||
return qsTr("Import %1 keypair").arg(root.store.selectedKeypair.name)
|
||||
|
@ -60,12 +64,16 @@ StatusModal {
|
|||
return selectKeypairComponent
|
||||
case Constants.keypairImportPopup.state.selectImportMethod:
|
||||
return selectImportMethodComponent
|
||||
case Constants.keypairImportPopup.state.scanQr:
|
||||
return scanQrComponent
|
||||
case Constants.keypairImportPopup.state.exportKeypair:
|
||||
return exportKeypairComponent
|
||||
case Constants.keypairImportPopup.state.importQr:
|
||||
return keypairImportQrComponent
|
||||
case Constants.keypairImportPopup.state.importPrivateKey:
|
||||
return keypairImportPrivateKeyComponent
|
||||
case Constants.keypairImportPopup.state.importSeedPhrase:
|
||||
return keypairImportSeedPhraseComponent
|
||||
case Constants.keypairImportPopup.state.displayInstructions:
|
||||
return displayInstructionsComponent
|
||||
}
|
||||
|
||||
return undefined
|
||||
|
@ -93,8 +101,18 @@ StatusModal {
|
|||
}
|
||||
|
||||
Component {
|
||||
id: scanQrComponent
|
||||
Item {
|
||||
id: exportKeypairComponent
|
||||
ExportKeypair {
|
||||
height: Constants.keypairImportPopup.contentHeight
|
||||
store: root.store
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: keypairImportQrComponent
|
||||
ScanOrEnterQrCode {
|
||||
height: Constants.keypairImportPopup.contentHeight
|
||||
store: root.store
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +131,13 @@ StatusModal {
|
|||
store: root.store
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: displayInstructionsComponent
|
||||
DisplayInstructions {
|
||||
height: Constants.keypairImportPopup.contentHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,8 +160,9 @@ StatusModal {
|
|||
text: {
|
||||
switch (root.store.currentState.stateType) {
|
||||
|
||||
case Constants.keypairImportPopup.state.scanQr:
|
||||
case Constants.keypairImportPopup.state.exportKeypair:
|
||||
return qsTr("Done")
|
||||
case Constants.keypairImportPopup.state.importQr:
|
||||
case Constants.keypairImportPopup.state.importPrivateKey:
|
||||
case Constants.keypairImportPopup.state.importSeedPhrase:
|
||||
return qsTr("Import %1 keypair").arg(root.store.selectedKeypair.name)
|
||||
|
@ -148,6 +174,10 @@ StatusModal {
|
|||
enabled: root.store.primaryPopupButtonEnabled
|
||||
|
||||
icon.name: {
|
||||
if (root.store.currentState.stateType === Constants.keypairImportPopup.state.exportKeypair) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if (root.store.userProfileUsingBiometricLogin) {
|
||||
return "touch-id"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
|
||||
import shared.views 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitHeight: layout.implicitHeight
|
||||
|
||||
SyncingCodeInstructions {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 24
|
||||
|
||||
type: SyncingCodeInstructions.Type.KeypairSync
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
|
||||
import shared.views 1.0
|
||||
|
||||
import "../stores"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property KeypairImportStore store
|
||||
|
||||
implicitHeight: layout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
|
||||
SyncingDisplayCode {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
visible: !!root.store.keypairImportModule.connectionString
|
||||
|
||||
connectionStringLabel: qsTr("Encrypted keypairs code")
|
||||
connectionString: root.store.keypairImportModule.connectionString
|
||||
importCodeInstructions: qsTr("On your other device, navigate to the Wallet screen<br>and select ‘Import missing keypairs’. For security reasons,<br>do not save this code anywhere.")
|
||||
codeExpiredMessage: qsTr("Your QR and encrypted keypairs code have expired.")
|
||||
|
||||
onConnectionStringChanged: {
|
||||
if (!!connectionString) {
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
onRequestConnectionString: {
|
||||
root.store.generateConnectionStringForExporting()
|
||||
}
|
||||
}
|
||||
|
||||
SyncingErrorMessage {
|
||||
Layout.fillWidth: true
|
||||
visible: !!root.store.keypairImportModule.connectionStringError
|
||||
primaryText: qsTr("Failed to generate sync code")
|
||||
secondaryText: qsTr("Failed to start pairing server")
|
||||
errorDetails: root.store.keypairImportModule.connectionStringError
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
|
||||
import shared.views 1.0
|
||||
|
||||
import "../stores"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property KeypairImportStore store
|
||||
|
||||
implicitHeight: layout.implicitHeight
|
||||
|
||||
SyncingEnterCode {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 24
|
||||
spacing: 32
|
||||
|
||||
firstTabName: qsTr("Scan encrypted QR code")
|
||||
secondTabName: qsTr("Enter encrypted key")
|
||||
syncQrErrorMessage: qsTr("This does not look like the correct keypair QR code")
|
||||
syncCodeErrorMessage: qsTr("This does not look like an encrypted keypair code")
|
||||
firstInstructionButtonName: qsTr("How to generate the QR")
|
||||
secondInstructionButtonName: qsTr("How to generate the key")
|
||||
syncCodeLabel: qsTr("Paste encrypted key")
|
||||
|
||||
validateConnectionString: function(connectionString) {
|
||||
const result = root.store.validateConnectionString(connectionString)
|
||||
return result === ""
|
||||
}
|
||||
|
||||
onProceed: {
|
||||
root.store.keypairImportModule.connectionString = connectionString
|
||||
if (!syncViaQr) {
|
||||
return
|
||||
}
|
||||
root.store.submitPopup()
|
||||
}
|
||||
|
||||
onDisplayInstructions: {
|
||||
root.store.currentState.doSecondaryAction()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,8 +3,11 @@ import QtQuick.Controls 2.14
|
|||
import QtQuick.Layouts 1.14
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import shared.popups.keycard.helpers 1.0
|
||||
import shared.status 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
|
@ -24,6 +27,12 @@ Item {
|
|||
|
||||
readonly property string fullyOperableValue: Constants.keypair.operability.fullyOperable
|
||||
readonly property string partiallyOperableValue: Constants.keypair.operability.partiallyOperable
|
||||
readonly property string profileTypeValue: Constants.keypair.type.profile
|
||||
readonly property int margin: 16
|
||||
|
||||
function importKeypairs() {
|
||||
root.store.currentState.doSecondaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
@ -34,21 +43,66 @@ Item {
|
|||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: d.margin
|
||||
Layout.rightMargin: d.margin
|
||||
text: qsTr("To use the associated accounts on this device, you need to import their keypairs.")
|
||||
font.pixelSize: Constants.keypairImportPopup.labelFontSize1
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
StatusSectionHeadline {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: d.margin
|
||||
Layout.rightMargin: d.margin
|
||||
text: qsTr("Import keypairs from your other device")
|
||||
}
|
||||
|
||||
StatusListItem {
|
||||
title: qsTr("Import via scanning encrypted QR")
|
||||
asset {
|
||||
width: 24
|
||||
height: 24
|
||||
name: "qr"
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
d.importKeypairs()
|
||||
}
|
||||
|
||||
components: [
|
||||
StatusIcon {
|
||||
icon: "next"
|
||||
color: Theme.palette.baseColor1
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
d.importKeypairs()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
StatusSectionHeadline {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: d.margin
|
||||
Layout.rightMargin: d.margin
|
||||
text: qsTr("Import individual keys")
|
||||
}
|
||||
|
||||
KeyPairList {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
optionLabel: qsTr("import")
|
||||
disableSelectionForKeypairsWithNonDefaultDerivationPath: false
|
||||
displayRadioButtonForSelection: false
|
||||
useTransparentItemBackgroundColor: true
|
||||
|
||||
modelFilters: ExpressionFilter {
|
||||
expression: model.keyPair.migratedToKeycard ||
|
||||
model.keyPair.pairType == d.profileTypeValue ||
|
||||
model.keyPair.operability == d.fullyOperableValue ||
|
||||
model.keyPair.operability == d.partiallyOperableValue
|
||||
inverted: true
|
||||
|
|
|
@ -50,6 +50,11 @@ BasePopupStore {
|
|||
}
|
||||
|
||||
readonly property bool primaryPopupButtonEnabled: {
|
||||
if (root.currentState.stateType === Constants.keypairImportPopup.state.importQr) {
|
||||
return !!root.keypairImportModule.connectionString &&
|
||||
!root.keypairImportModule.connectionStringError
|
||||
}
|
||||
|
||||
if (root.currentState.stateType === Constants.keypairImportPopup.state.importPrivateKey) {
|
||||
return root.enteredPrivateKeyIsValid &&
|
||||
root.enteredPrivateKeyMatchTheKeypair &&
|
||||
|
@ -65,4 +70,12 @@ BasePopupStore {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
function generateConnectionStringForExporting() {
|
||||
root.keypairImportModule.generateConnectionStringForExporting()
|
||||
}
|
||||
|
||||
function validateConnectionString(connectionString) {
|
||||
return root.keypairImportModule.validateConnectionString(connectionString)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,13 @@ import shared.controls 1.0
|
|||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
enum Source {
|
||||
Mobile,
|
||||
Desktop
|
||||
enum Type {
|
||||
AppSync,
|
||||
KeypairSync
|
||||
}
|
||||
|
||||
property int type: SyncingCodeInstructions.Type.AppSync
|
||||
|
||||
spacing: 0
|
||||
|
||||
StatusSwitchTabBar {
|
||||
|
@ -47,6 +49,8 @@ ColumnLayout {
|
|||
Layout.fillHeight: true
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
type: root.type
|
||||
}
|
||||
|
||||
GetSyncCodeDesktopInstructions {
|
||||
|
@ -54,6 +58,8 @@ ColumnLayout {
|
|||
Layout.fillHeight: true
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
type: root.type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,10 @@ ColumnLayout {
|
|||
id: root
|
||||
|
||||
property int secondsTimeout: 5 * 60
|
||||
property string connectionStringLabel: ""
|
||||
property string connectionString: ""
|
||||
property string importCodeInstructions : ""
|
||||
property string codeExpiredMessage: ""
|
||||
|
||||
signal requestConnectionString()
|
||||
|
||||
|
@ -156,27 +159,27 @@ ColumnLayout {
|
|||
Layout.fillWidth: true
|
||||
Layout.topMargin: 12
|
||||
Layout.bottomMargin: 7
|
||||
text: qsTr("Sync code")
|
||||
text: root.connectionStringLabel
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 13
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
||||
Input {
|
||||
StatusPasswordInput {
|
||||
id: syncCodeInput
|
||||
|
||||
property bool showPassword
|
||||
readonly property bool effectiveShowPassword: showPassword && !d.codeExpired
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 88
|
||||
Layout.bottomMargin: 24
|
||||
readOnly: true
|
||||
keepHeight: true
|
||||
textField.echoMode: effectiveShowPassword ? TextInput.Normal : TextInput.Password
|
||||
textField.rightPadding: syncCodeButtons.width + Style.current.padding / 2
|
||||
textField.color: Style.current.textColor
|
||||
textField.selectByMouse: !d.codeExpired
|
||||
selectByMouse: !d.codeExpired
|
||||
text: root.connectionString
|
||||
rightPadding: syncCodeButtons.width + Style.current.padding / 2
|
||||
wrapMode: TextEdit.Wrap
|
||||
echoMode: effectiveShowPassword ? TextInput.Normal : TextInput.Password
|
||||
|
||||
Row {
|
||||
id: syncCodeButtons
|
||||
|
@ -206,9 +209,9 @@ ColumnLayout {
|
|||
onClicked: {
|
||||
const showPassword = syncCodeInput.showPassword
|
||||
syncCodeInput.showPassword = true
|
||||
syncCodeInput.textField.selectAll()
|
||||
syncCodeInput.textField.copy()
|
||||
syncCodeInput.textField.deselect()
|
||||
syncCodeInput.selectAll()
|
||||
syncCodeInput.copy()
|
||||
syncCodeInput.deselect()
|
||||
syncCodeInput.showPassword = showPassword
|
||||
}
|
||||
}
|
||||
|
@ -222,7 +225,7 @@ ColumnLayout {
|
|||
visible: !d.codeExpired
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
text: qsTr("On your other device, navigate to the Syncing<br>screen and select Enter Sync Code.")
|
||||
text: root.importCodeInstructions
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
|
@ -232,8 +235,6 @@ ColumnLayout {
|
|||
visible: d.codeExpired
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
text: qsTr("Your QR and Sync Code has expired.")
|
||||
text: root.codeExpiredMessage
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -11,31 +11,25 @@ ColumnLayout {
|
|||
|
||||
property string firstTabName: qsTr("Scan QR code")
|
||||
property string secondTabName: qsTr("Enter sync code")
|
||||
property string firstInstructionButtonName: qsTr("How to get a sync code")
|
||||
property string secondInstructionButtonName: qsTr("How to get a sync code")
|
||||
property string syncQrErrorMessage: qsTr("This does not look like a sync QR code")
|
||||
property string syncCodeErrorMessage: qsTr("This does not look like a sync code")
|
||||
property string instructionButtonName: qsTr("How to get a sync code")
|
||||
property string syncCodeLabel: qsTr("Paste sync code")
|
||||
|
||||
property var validateConnectionString: function(){}
|
||||
|
||||
readonly property bool syncViaQr: !switchTabBar.currentIndex
|
||||
|
||||
signal displayInstructions()
|
||||
signal proceed(string connectionString)
|
||||
|
||||
Timer {
|
||||
id: nextStateDelay
|
||||
|
||||
property string connectionString
|
||||
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
root.proceed(connectionString)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StatusSwitchTabBar {
|
||||
id: switchTabBar
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
currentIndex: 0
|
||||
|
||||
StatusSwitchTabButton {
|
||||
|
@ -49,21 +43,17 @@ ColumnLayout {
|
|||
|
||||
StackLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Math.max(mobileSync.implicitHeight, desktopSync.implicitHeight)
|
||||
Layout.preferredHeight: Math.max(syncQr.implicitHeight, syncCode.implicitHeight)
|
||||
currentIndex: switchTabBar.currentIndex
|
||||
|
||||
// StackLayout doesn't support alignment, so we create an `Item` wrappers
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
StatusSyncCodeScan {
|
||||
id: mobileSync
|
||||
id: syncQr
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
validators: [
|
||||
StatusValidator {
|
||||
|
@ -79,17 +69,15 @@ ColumnLayout {
|
|||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
StatusSyncCodeInput {
|
||||
id: desktopSync
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
readOnly: nextStateDelay.running
|
||||
id: syncCode
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 424
|
||||
|
||||
mode: StatusSyncCodeInput.Mode.WriteMode
|
||||
label:root.syncCodeLabel
|
||||
input.placeholderText: qsTr("eg. %1").arg("0x2Ef19")
|
||||
|
||||
validators: [
|
||||
StatusValidator {
|
||||
name: "isSyncCode"
|
||||
|
@ -100,8 +88,7 @@ ColumnLayout {
|
|||
input.onValidChanged: {
|
||||
if (!input.valid)
|
||||
return
|
||||
nextStateDelay.connectionString = desktopSync.text
|
||||
nextStateDelay.start()
|
||||
root.proceed(syncCode.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,10 +96,18 @@ ColumnLayout {
|
|||
|
||||
StatusFlatButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: !!root.instructionButtonName
|
||||
text: root.instructionButtonName
|
||||
visible: switchTabBar.currentIndex == 0 && !!root.firstInstructionButtonName ||
|
||||
switchTabBar.currentIndex == 1 && !!root.secondInstructionButtonName
|
||||
text: switchTabBar.currentIndex == 0?
|
||||
root.firstInstructionButtonName :
|
||||
root.secondInstructionButtonName
|
||||
onClicked: {
|
||||
root.displayInstructions()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,40 +13,24 @@ ColumnLayout {
|
|||
|
||||
property string primaryText
|
||||
property string secondaryText
|
||||
property string errorDetails
|
||||
|
||||
spacing: 12
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredHeight: parent.height / 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
text: root.primaryText
|
||||
font.pixelSize: 17
|
||||
color: Theme.palette.dangerColor1
|
||||
}
|
||||
|
||||
Item {
|
||||
ErrorDetails {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 60
|
||||
Layout.rightMargin: 60
|
||||
Layout.preferredWidth: 360
|
||||
Layout.preferredHeight: parent.height / 2
|
||||
Layout.minimumHeight: detailsView.implicitHeight
|
||||
|
||||
ErrorDetails {
|
||||
id: detailsView
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
title: qsTr("Failed to start pairing server")
|
||||
details: root.secondaryText
|
||||
}
|
||||
Layout.preferredHeight: implicitHeight
|
||||
title: root.secondaryText
|
||||
details: root.errorDetails
|
||||
}
|
||||
}
|
||||
|
|
|
@ -744,20 +744,23 @@ QtObject {
|
|||
readonly property int footerButtonsHeight: 44
|
||||
readonly property int labelFontSize1: 15
|
||||
|
||||
readonly property QtObject importOption: QtObject {
|
||||
readonly property QtObject mode: QtObject {
|
||||
readonly property int selectKeypair: 1
|
||||
readonly property int seedPhrase: 2
|
||||
readonly property int privateKey: 3
|
||||
readonly property int qrCode: 4
|
||||
readonly property int importViaSeedPhrase: 2
|
||||
readonly property int importViaPrivateKey: 3
|
||||
readonly property int importViaQr: 4
|
||||
readonly property int exportKeypairQr: 5
|
||||
}
|
||||
|
||||
readonly property QtObject state: QtObject {
|
||||
readonly property string noState: "NoState"
|
||||
readonly property string selectKeypair: "SelectKeypair"
|
||||
readonly property string selectImportMethod: "SelectImportMethod"
|
||||
readonly property string scanQr: "ScanQr"
|
||||
readonly property string exportKeypair: "ExportKeypair"
|
||||
readonly property string importQr: "ImportQr"
|
||||
readonly property string importSeedPhrase: "ImportSeedPhrase"
|
||||
readonly property string importPrivateKey: "ImportPrivateKey"
|
||||
readonly property string displayInstructions: "DisplayInstructions"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -853,7 +853,7 @@ QtObject {
|
|||
}
|
||||
}
|
||||
if (keypair.syncedFrom !== "") {
|
||||
return qsTr("Synced from %1").arg(keypair.syncedFrom)
|
||||
return qsTr("Requires import to use")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue