chore(@desktop/wallet): old add account modal and corresponding methods from `walletSectionAccounts` module removed
This commit is contained in:
parent
388fab4a4a
commit
7b16a93cc0
|
@ -1,9 +1,7 @@
|
|||
import sugar, sequtils, tables
|
||||
import io_interface
|
||||
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
|
||||
import ../../../../../app_service/service/accounts/service as accounts_service
|
||||
import ../../../../../app_service/service/network/service as network_service
|
||||
import ../../../../../app_service/service/token/service as token_service
|
||||
import ../../../../../app_service/service/currency/service as currency_service
|
||||
import ../../../../../app_service/service/currency/dto as currency_dto
|
||||
|
||||
|
@ -12,7 +10,6 @@ import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_modu
|
|||
|
||||
import ../../../../core/eventemitter
|
||||
|
||||
const UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER* = "WalletSection-AccountsModule"
|
||||
const UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER* = "WalletSection-AccountsModule-Authentication"
|
||||
|
||||
type
|
||||
|
@ -20,27 +17,21 @@ type
|
|||
delegate: io_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
walletAccountService: wallet_account_service.Service
|
||||
accountsService: accounts_service.Service
|
||||
networkService: network_service.Service
|
||||
tokenService: token_service.Service
|
||||
currencyService: currency_service.Service
|
||||
|
||||
proc newController*(
|
||||
delegate: io_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
walletAccountService: wallet_account_service.Service,
|
||||
accountsService: accounts_service.Service,
|
||||
networkService: network_service.Service,
|
||||
tokenService: token_service.Service,
|
||||
currencyService: currency_service.Service,
|
||||
): Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.walletAccountService = walletAccountService
|
||||
result.accountsService = accountsService
|
||||
result.networkService = networkService
|
||||
result.tokenService = tokenService
|
||||
result.currencyService = currencyService
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
|
@ -53,65 +44,12 @@ proc init*(self: Controller) =
|
|||
return
|
||||
self.delegate.onUserAuthenticated(args.pin, args.password, args.keyUid)
|
||||
|
||||
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED_AND_WALLET_ADDRESS_GENERATED) do(e: Args):
|
||||
let args = SharedKeycarModuleUserAuthenticatedAndWalletAddressGeneratedArgs(e)
|
||||
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER:
|
||||
return
|
||||
self.delegate.onUserAuthenticatedAndWalletAddressGenerated(args.address, args.publicKey, args.derivedFrom, args.password)
|
||||
|
||||
self.events.on(SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_DETAILS_FETCHED) do(e: Args):
|
||||
let args = DerivedAddressesArgs(e)
|
||||
var derivedAddress: DerivedAddressDto
|
||||
if args.derivedAddresses.len > 0:
|
||||
derivedAddress = args.derivedAddresses[0]
|
||||
self.delegate.addressDetailsFetched(derivedAddress, args.error)
|
||||
|
||||
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED) do(e: Args):
|
||||
let args = SharedKeycarModuleFlowTerminatedArgs(e)
|
||||
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER:
|
||||
return
|
||||
self.delegate.onSharedKeycarModuleFlowTerminated(args.lastStepInTheCurrentFlow)
|
||||
|
||||
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
|
||||
return self.walletAccountService.getWalletAccounts()
|
||||
|
||||
proc generateNewAccount*(self: Controller, password: string, accountName: string, color: string, emoji: string,
|
||||
path: string, derivedFrom: string, skipPasswordVerification: bool): string =
|
||||
return self.walletAccountService.generateNewAccount(password, accountName, color, emoji, path, derivedFrom, skipPasswordVerification)
|
||||
|
||||
proc addAccountsFromPrivateKey*(self: Controller, privateKey: string, password: string, accountName: string, color: string,
|
||||
emoji: string, skipPasswordVerification: bool): string =
|
||||
return self.walletAccountService.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji, skipPasswordVerification)
|
||||
|
||||
proc addAccountsFromSeed*(self: Controller, seedPhrase: string, password: string, accountName: string, color: string,
|
||||
emoji: string, path: string, skipPasswordVerification: bool): string =
|
||||
return self.walletAccountService.addAccountsFromSeed(seedPhrase, password, accountName, color, emoji, path, skipPasswordVerification)
|
||||
|
||||
proc addWatchOnlyAccount*(self: Controller, address: string, accountName: string, color: string, emoji: string): string =
|
||||
return self.walletAccountService.addWatchOnlyAccount(address, accountName, color, emoji)
|
||||
|
||||
proc deleteAccount*(self: Controller, address: string, password = "") =
|
||||
self.walletAccountService.deleteAccount(address, password)
|
||||
|
||||
proc fetchDerivedAddressDetails*(self: Controller, address: string) =
|
||||
self.walletAccountService.fetchDerivedAddressDetails(address)
|
||||
|
||||
method getDerivedAddress*(self: Controller, password: string, derivedFrom: string, path: string, hashPassword: bool)=
|
||||
self.walletAccountService.getDerivedAddress(password, derivedFrom, path, hashPassword)
|
||||
|
||||
method getDerivedAddressList*(self: Controller, password: string, derivedFrom: string, path: string, pageSize: int, pageNumber: int, hashPassword: bool)=
|
||||
self.walletAccountService.getDerivedAddressList(password, derivedFrom, path, pageSize, pageNumber, hashPassword)
|
||||
|
||||
method getDerivedAddressListForMnemonic*(self: Controller, mnemonic: string, path: string, pageSize: int, pageNumber: int) =
|
||||
self.walletAccountService.getDerivedAddressListForMnemonic(mnemonic, path, pageSize, pageNumber)
|
||||
|
||||
method getDerivedAddressForPrivateKey*(self: Controller, privateKey: string) =
|
||||
self.walletAccountService.getDerivedAddressForPrivateKey(privateKey)
|
||||
|
||||
proc validSeedPhrase*(self: Controller, seedPhrase: string): bool =
|
||||
let err = self.accountsService.validateMnemonic(seedPhrase)
|
||||
return err.len == 0
|
||||
|
||||
proc authenticateKeyPair*(self: Controller, keyUid = "") =
|
||||
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER,
|
||||
keyUid: keyUid)
|
||||
|
@ -120,11 +58,6 @@ proc authenticateKeyPair*(self: Controller, keyUid = "") =
|
|||
proc getAllMigratedKeyPairs*(self: Controller): seq[KeyPairDto] =
|
||||
return self.walletAccountService.getAllMigratedKeyPairs()
|
||||
|
||||
proc addWalletAccount*(self: Controller, name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid, accountType,
|
||||
color, emoji: string): string =
|
||||
return self.walletAccountService.addWalletAccount(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid,
|
||||
accountType, color, emoji)
|
||||
|
||||
proc getChainIds*(self: Controller): seq[int] =
|
||||
return self.networkService.getNetworks().map(n => n.chainId)
|
||||
|
||||
|
|
|
@ -16,70 +16,20 @@ method isLoaded*(self: AccessInterface): bool {.base.} =
|
|||
method syncKeycard*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method generateNewAccount*(self: AccessInterface, password: string, accountName: string, color: string, emoji: string, path: string, derivedFrom: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method addNewWalletAccountGeneratedFromKeycard*(self: AccessInterface, accountType: string, accountName: string,
|
||||
color: string, emoji: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method addAccountsFromPrivateKey*(self: AccessInterface, privateKey: string, password: string, accountName: string, color: string, emoji: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method addAccountsFromSeed*(self: AccessInterface, seedPhrase: string, password: string, accountName: string, color: string, emoji: string, path: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method addWatchOnlyAccount*(self: AccessInterface, address: string, accountName: string, color: string, emoji: string): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method deleteAccount*(self: AccessInterface, keyUid: string, address: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method refreshWalletAccounts*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getDerivedAddress*(self: AccessInterface, password: string, derivedFrom: string, path: string, hashPassword: bool) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getDerivedAddressList*(self: AccessInterface, password: string, derivedFrom: string, path: string, pageSize: int, pageNumber: int, hashPassword: bool) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getDerivedAddressListForMnemonic*(self: AccessInterface, mnemonic: string, path: string, pageSize: int, pageNumber: int) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getDerivedAddressForPrivateKey*(self: AccessInterface, privateKey: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
# View Delegate Interface
|
||||
# Delegate for the view must be declared here due to use of QtObject and multi
|
||||
# inheritance, which is not well supported in Nim.
|
||||
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method validSeedPhrase*(self: AccessInterface, value: string): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method authenticateUser*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onUserAuthenticated*(self: AccessInterface, pin: string, password: string, keyUid: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method authenticateUserAndDeriveAddressOnKeycardForPath*(self: AccessInterface, keyUid: string, derivationPath: string, searchForFirstAvailableAddress: bool) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method createSharedKeycardModule*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method destroySharedKeycarModule*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onUserAuthenticatedAndWalletAddressGenerated*(self: AccessInterface, address: string, publicKey: string,
|
||||
derivedFrom: string, password: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method addressDetailsFetched*(self: AccessInterface, derivedAddress: DerivedAddressDto, error: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onSharedKeycarModuleFlowTerminated*(self: AccessInterface, lastStepInTheCurrentFlow: bool) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
|
|
@ -7,25 +7,18 @@ import ../../../../core/eventemitter
|
|||
import ../../../../../app_service/common/account_constants
|
||||
import ../../../../../app_service/service/keycard/service as keycard_service
|
||||
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
|
||||
import ../../../../../app_service/service/accounts/service as accounts_service
|
||||
import ../../../../../app_service/service/network/service as network_service
|
||||
import ../../../../../app_service/service/token/service as token_service
|
||||
import ../../../../../app_service/service/currency/service as currency_service
|
||||
import ../../../shared_models/token_model as token_model
|
||||
import ../../../shared_models/token_item as token_item
|
||||
import ../../../shared_modules/keycard_popup/models/key_pair_item as keycard_key_pair_item
|
||||
import ../../../shared_modules/keycard_popup/module as keycard_shared_module
|
||||
import ./compact_item as compact_item
|
||||
import ./compact_model as compact_model
|
||||
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
|
||||
|
||||
export io_interface
|
||||
|
||||
# TODO: remove it completely if after wallet settings part refactore this is not needed.
|
||||
type
|
||||
AuthenticationReason {.pure.} = enum
|
||||
LoggedInUserAuthentication = 0
|
||||
DeriveAccountForKeyPairAuthentication
|
||||
DeleteAccountAuthentication
|
||||
DeleteAccountAuthentication = 0
|
||||
|
||||
# TODO: remove it completely if after wallet settings part refactore this is not needed.
|
||||
type WalletAccountDetails = object
|
||||
address: string
|
||||
path: string
|
||||
|
@ -40,44 +33,29 @@ type
|
|||
view: View
|
||||
controller: Controller
|
||||
moduleLoaded: bool
|
||||
keycardService: keycard_service.Service
|
||||
accountsService: accounts_service.Service
|
||||
walletAccountService: wallet_account_service.Service
|
||||
keycardSharedModule: keycard_shared_module.AccessInterface
|
||||
processingWalletAccount: WalletAccountDetails
|
||||
authentiactionReason: AuthenticationReason
|
||||
|
||||
proc newModule*(
|
||||
delegate: delegate_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
keycardService: keycard_service.Service,
|
||||
walletAccountService: wallet_account_service.Service,
|
||||
accountsService: accounts_service.Service,
|
||||
networkService: network_service.Service,
|
||||
tokenService: token_service.Service,
|
||||
currencyService: currency_service.Service
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.keycardService = keycardService
|
||||
result.accountsService = accountsService
|
||||
result.walletAccountService = walletAccountService
|
||||
result.view = newView(result)
|
||||
result.controller = controller.newController(result, events, walletAccountService, accountsService, networkService, tokenService, currencyService)
|
||||
result.controller = controller.newController(result, events, walletAccountService, networkService, currencyService)
|
||||
result.moduleLoaded = false
|
||||
result.authentiactionReason = AuthenticationReason.LoggedInUserAuthentication
|
||||
result.authentiactionReason = AuthenticationReason.DeleteAccountAuthentication
|
||||
|
||||
method delete*(self: Module) =
|
||||
self.view.delete
|
||||
self.controller.delete
|
||||
if not self.keycardSharedModule.isNil:
|
||||
self.keycardSharedModule.delete
|
||||
|
||||
method onSharedKeycarModuleFlowTerminated*(self: Module, lastStepInTheCurrentFlow: bool) =
|
||||
if not self.keycardSharedModule.isNil:
|
||||
self.keycardSharedModule.delete
|
||||
self.keycardSharedModule = nil
|
||||
|
||||
method refreshWalletAccounts*(self: Module) =
|
||||
let keyPairMigrated = proc(migratedKeyPairs: seq[KeyPairDto], keyUid: string): bool =
|
||||
|
@ -131,10 +109,6 @@ method load*(self: Module) =
|
|||
self.events.on(SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED) do(e:Args):
|
||||
self.refreshWalletAccounts()
|
||||
|
||||
self.events.on(SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_READY) do(e:Args):
|
||||
var args = DerivedAddressesArgs(e)
|
||||
self.view.setDerivedAddresses(args.derivedAddresses, args.error)
|
||||
|
||||
self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args):
|
||||
self.refreshWalletAccounts()
|
||||
|
||||
|
@ -168,24 +142,6 @@ proc tryKeycardSync(self: Module, keyUid, pin: string) =
|
|||
let dataForKeycardToSync = SharedKeycarModuleArgs(pin: pin, keyUid: keyUid)
|
||||
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC, dataForKeycardToSync)
|
||||
|
||||
method generateNewAccount*(self: Module, password: string, accountName: string, color: string, emoji: string,
|
||||
path: string, derivedFrom: string): string =
|
||||
let skipPasswordVerification = singletonInstance.userProfile.getIsKeycardUser()
|
||||
return self.controller.generateNewAccount(password, accountName, color, emoji, path, derivedFrom, skipPasswordVerification)
|
||||
|
||||
method addAccountsFromPrivateKey*(self: Module, privateKey: string, password: string, accountName: string, color: string,
|
||||
emoji: string): string =
|
||||
let skipPasswordVerification = singletonInstance.userProfile.getIsKeycardUser()
|
||||
return self.controller.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji, skipPasswordVerification)
|
||||
|
||||
method addAccountsFromSeed*(self: Module, seedPhrase: string, password: string, accountName: string, color: string,
|
||||
emoji: string, path: string): string =
|
||||
let skipPasswordVerification = singletonInstance.userProfile.getIsKeycardUser()
|
||||
return self.controller.addAccountsFromSeed(seedPhrase, password, accountName, color, emoji, path, skipPasswordVerification)
|
||||
|
||||
method addWatchOnlyAccount*(self: Module, address: string, accountName: string, color: string, emoji: string): string =
|
||||
return self.controller.addWatchOnlyAccount(address, accountName, color, emoji)
|
||||
|
||||
proc authenticateActivityForKeyUid(self: Module, keyUid: string, reason: AuthenticationReason) =
|
||||
self.authentiactionReason = reason
|
||||
let keyPair = self.controller.getMigratedKeyPairByKeyUid(keyUid)
|
||||
|
@ -205,117 +161,15 @@ method deleteAccount*(self: Module, keyUid: string, address: string) =
|
|||
self.processingWalletAccount = WalletAccountDetails(keyUid: keyUid, address: address)
|
||||
self.authenticateActivityForKeyUid(keyUid, AuthenticationReason.DeleteAccountAuthentication)
|
||||
|
||||
method getDerivedAddress*(self: Module, password: string, derivedFrom: string, path: string, hashPassword: bool) =
|
||||
self.controller.getDerivedAddress(password, derivedFrom, path, hashPassword)
|
||||
|
||||
method getDerivedAddressList*(self: Module, password: string, derivedFrom: string, path: string, pageSize: int, pageNumber: int, hashPassword: bool) =
|
||||
self.controller.getDerivedAddressList(password, derivedFrom, path, pageSize, pageNumber, hashPassword)
|
||||
|
||||
method getDerivedAddressListForMnemonic*(self: Module, mnemonic: string, path: string, pageSize: int, pageNumber: int) =
|
||||
self.controller.getDerivedAddressListForMnemonic(mnemonic, path, pageSize, pageNumber)
|
||||
|
||||
method getDerivedAddressForPrivateKey*(self: Module, privateKey: string) =
|
||||
self.controller.getDerivedAddressForPrivateKey(privateKey)
|
||||
|
||||
method validSeedPhrase*(self: Module, value: string): bool =
|
||||
return self.controller.validSeedPhrase(value)
|
||||
|
||||
method authenticateUser*(self: Module) =
|
||||
self.authenticateActivityForKeyUid(singletonInstance.userProfile.getKeyUid(), AuthenticationReason.LoggedInUserAuthentication)
|
||||
|
||||
method onUserAuthenticated*(self: Module, pin: string, password: string, keyUid: string) =
|
||||
if self.authentiactionReason == AuthenticationReason.LoggedInUserAuthentication or
|
||||
self.authentiactionReason == AuthenticationReason.DeriveAccountForKeyPairAuthentication:
|
||||
if password.len > 0:
|
||||
self.view.userAuthenticationSuccess(password)
|
||||
else:
|
||||
self.view.userAuthentiactionFail()
|
||||
elif self.authentiactionReason == AuthenticationReason.DeleteAccountAuthentication:
|
||||
if self.processingWalletAccount.keyUid == keyUid:
|
||||
if self.authentiactionReason == AuthenticationReason.DeleteAccountAuthentication:
|
||||
if self.processingWalletAccount.keyUid != keyUid:
|
||||
error "cannot resolve key uid of an account being deleted", keyUid=keyUid
|
||||
return
|
||||
if pin.len == PINLengthForStatusApp:
|
||||
# keycard keypair authentication
|
||||
self.controller.deleteAccount(self.processingWalletAccount.address)
|
||||
if pin.len == PINLengthForStatusApp:
|
||||
self.tryKeycardSync(keyUid, pin)
|
||||
if password.len > 0 and keyUid.len == 0:
|
||||
self.tryKeycardSync(keyUid, pin)
|
||||
elif password.len > 0:
|
||||
# regular keypair authentication
|
||||
self.controller.deleteAccount(self.processingWalletAccount.address, password)
|
||||
|
||||
method createSharedKeycardModule*(self: Module) =
|
||||
if self.keycardSharedModule.isNil:
|
||||
self.keycardSharedModule = keycard_shared_module.newModule[Module](self, UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER,
|
||||
self.events, self.keycardService, settingsService = nil, networkService = nil, privacyService = nil, self.accountsService,
|
||||
self.walletAccountService, keychainService = nil)
|
||||
|
||||
method destroySharedKeycarModule*(self: Module) =
|
||||
if not self.keycardSharedModule.isNil:
|
||||
let kpForProcessing = self.keycardSharedModule.getKeyPairForProcessing()
|
||||
if not kpForProcessing.isNil:
|
||||
self.tryKeycardSync(kpForProcessing.getKeyUid(), self.keycardSharedModule.getPin())
|
||||
self.keycardSharedModule.delete
|
||||
self.keycardSharedModule = nil
|
||||
|
||||
proc checkIfWalletAccountIsAlreadyCreated(self: Module, keyUid: string, derivationPath: string): bool =
|
||||
let walletAccounts = self.controller.getWalletAccounts()
|
||||
for w in walletAccounts:
|
||||
if w.keyUid == keyUid and w.path == derivationPath:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc findFirstAvaliablePathForWallet(self: Module, keyUid: string): string =
|
||||
let walletAccounts = self.controller.getWalletAccounts()
|
||||
# starting from 1, "0" is already reserved for the default wallet account
|
||||
for i in 1 .. 256: # we hope that nobody will have 256 accounts added, so no need to change derivation tree
|
||||
let path = account_constants.PATH_WALLET_ROOT & "/" & $i
|
||||
var found = false
|
||||
for w in walletAccounts:
|
||||
if w.keyUid == keyUid and w.path == path:
|
||||
found = true
|
||||
break
|
||||
if not found:
|
||||
return path
|
||||
error "we couldn't find available wallet account path"
|
||||
|
||||
method authenticateUserAndDeriveAddressOnKeycardForPath*(self: Module, keyUid: string, derivationPath: string, searchForFirstAvailableAddress: bool) =
|
||||
self.authentiactionReason = AuthenticationReason.DeriveAccountForKeyPairAuthentication
|
||||
var finalPath = derivationPath
|
||||
if searchForFirstAvailableAddress and
|
||||
self.checkIfWalletAccountIsAlreadyCreated(keyUid, finalPath):
|
||||
finalPath = self.findFirstAvaliablePathForWallet(keyUid)
|
||||
if self.keycardSharedModule.isNil:
|
||||
self.createSharedKeycardModule()
|
||||
self.processingWalletAccount = WalletAccountDetails(keyUid: keyUid, path: finalPath)
|
||||
self.view.setDerivedAddressesLoading(true)
|
||||
self.view.setDerivedAddressesError("")
|
||||
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.AuthenticateAndDeriveAccountAddress, keyUid, finalPath)
|
||||
|
||||
method onUserAuthenticatedAndWalletAddressGenerated*(self: Module, address: string, publicKey: string,
|
||||
derivedFrom: string, password: string) =
|
||||
if address.len == 0:
|
||||
self.view.setDerivedAddressesError("wrong-path") # this should be checked on the UI side and we should not allow entering invalid path format
|
||||
if password.len == 0:
|
||||
self.view.setDerivedAddressesLoading(false)
|
||||
return
|
||||
self.onUserAuthenticated(pin = "", password, keyUid = "")
|
||||
self.processingWalletAccount.address = address
|
||||
self.processingWalletAccount.addressAccountIsDerivedFrom = derivedFrom
|
||||
self.processingWalletAccount.publicKey = publicKey
|
||||
self.controller.fetchDerivedAddressDetails(address)
|
||||
|
||||
method addressDetailsFetched*(self: Module, derivedAddress: DerivedAddressDto, error: string) =
|
||||
var derivedAddressDto = derivedAddress
|
||||
if error.len > 0:
|
||||
derivedAddressDto.address = self.processingWalletAccount.address
|
||||
derivedAddressDto.alreadyCreated = true
|
||||
self.view.setDerivedAddresses(@[derivedAddressDto], error)
|
||||
|
||||
method addNewWalletAccountGeneratedFromKeycard*(self: Module, accountType: string, accountName: string, color: string,
|
||||
emoji: string): string =
|
||||
return self.controller.addWalletAccount(accountName,
|
||||
self.processingWalletAccount.address,
|
||||
self.processingWalletAccount.path,
|
||||
self.processingWalletAccount.addressAccountIsDerivedFrom,
|
||||
self.processingWalletAccount.publicKey,
|
||||
self.processingWalletAccount.keyUid,
|
||||
accountType,
|
||||
color,
|
||||
emoji)
|
||||
self.controller.deleteAccount(self.processingWalletAccount.address, password)
|
|
@ -24,15 +24,11 @@ QtObject:
|
|||
watchOnly: Model
|
||||
imported: Model
|
||||
generatedAccounts: GeneratedWalletModel
|
||||
derivedAddresses: DerivedAddressModel
|
||||
derivedAddressesLoading: bool
|
||||
derivedAddressesError: string
|
||||
modelVariant: QVariant
|
||||
generatedVariant: QVariant
|
||||
importedVariant: QVariant
|
||||
watchOnlyVariant: QVariant
|
||||
generatedAccountsVariant: QVariant
|
||||
derivedAddressesVariant: QVariant
|
||||
tmpAddress: string
|
||||
|
||||
proc delete*(self: View) =
|
||||
|
@ -46,8 +42,6 @@ QtObject:
|
|||
self.watchOnlyVariant.delete
|
||||
self.generatedAccounts.delete
|
||||
self.generatedAccountsVariant.delete
|
||||
self.derivedAddresses.delete
|
||||
self.derivedAddressesVariant.delete
|
||||
self.QObject.delete
|
||||
|
||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||
|
@ -64,10 +58,6 @@ QtObject:
|
|||
result.watchOnlyVariant = newQVariant(result.watchOnly)
|
||||
result.generatedAccounts = newGeneratedWalletModel()
|
||||
result.generatedAccountsVariant = newQVariant(result.generatedAccounts)
|
||||
result.derivedAddresses = newDerivedAddressModel()
|
||||
result.derivedAddressesLoading = false
|
||||
result.derivedAddressesError = ""
|
||||
result.derivedAddressesVariant = newQVariant(result.derivedAddresses)
|
||||
|
||||
proc load*(self: View) =
|
||||
self.delegate.viewDidLoad()
|
||||
|
@ -117,44 +107,6 @@ QtObject:
|
|||
read = getGeneratedAccounts
|
||||
notify = generatedAccountsChanged
|
||||
|
||||
proc derivedAddressesChanged*(self: View) {.signal.}
|
||||
|
||||
proc getDerivedAddresses(self: View): QVariant {.slot.} =
|
||||
return self.derivedAddressesVariant
|
||||
|
||||
QtProperty[QVariant] derivedAddresses:
|
||||
read = getDerivedAddresses
|
||||
notify = derivedAddressesChanged
|
||||
|
||||
proc derivedAddressesLoadingChanged*(self: View) {.signal.}
|
||||
|
||||
proc getDerivedAddressesLoading(self: View): bool {.slot.} =
|
||||
return self.derivedAddressesLoading
|
||||
|
||||
proc setDerivedAddressesLoading*(self: View, loading: bool) {.slot.} =
|
||||
if self.derivedAddressesLoading != loading:
|
||||
self.derivedAddressesLoading = loading
|
||||
self.derivedAddressesLoadingChanged()
|
||||
|
||||
QtProperty[bool] derivedAddressesLoading:
|
||||
read = getDerivedAddressesLoading
|
||||
write = setDerivedAddressesLoading
|
||||
notify = derivedAddressesLoadingChanged
|
||||
|
||||
proc derivedAddressErrorChanged*(self: View) {.signal.}
|
||||
|
||||
proc getDerivedAddressesError(self: View): string {.slot.} =
|
||||
return self.derivedAddressesError
|
||||
|
||||
proc setDerivedAddressesError*(self: View, error: string) {.slot.} =
|
||||
self.derivedAddressesError = error
|
||||
self.derivedAddressErrorChanged()
|
||||
|
||||
QtProperty[string] derivedAddressesError:
|
||||
read = getDerivedAddressesError
|
||||
write = setDerivedAddressesError
|
||||
notify = derivedAddressErrorChanged
|
||||
|
||||
proc setItems*(self: View, items: seq[Item]) =
|
||||
self.model.setItems(items)
|
||||
|
||||
|
@ -206,22 +158,6 @@ QtObject:
|
|||
self.generated.setItems(generated)
|
||||
self.generatedAccounts.setItems(generatedAccounts)
|
||||
|
||||
proc generateNewAccount*(self: View, password: string, accountName: string, color: string, emoji: string, path: string, derivedFrom: string): string {.slot.} =
|
||||
return self.delegate.generateNewAccount(password, accountName, color, emoji, path, derivedFrom)
|
||||
|
||||
proc addNewWalletAccountGeneratedFromKeycard*(self: View, accountType: string, accountName: string, color: string,
|
||||
emoji: string): string {.slot.} =
|
||||
return self.delegate.addNewWalletAccountGeneratedFromKeycard(accountType, accountName, color, emoji)
|
||||
|
||||
proc addAccountsFromPrivateKey*(self: View, privateKey: string, password: string, accountName: string, color: string, emoji: string): string {.slot.} =
|
||||
return self.delegate.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji)
|
||||
|
||||
proc addAccountsFromSeed*(self: View, seedPhrase: string, password: string, accountName: string, color: string, emoji: string, path: string): string {.slot.} =
|
||||
return self.delegate.addAccountsFromSeed(seedPhrase, password, accountName, color, emoji, path)
|
||||
|
||||
proc addWatchOnlyAccount*(self: View, address: string, accountName: string, color: string, emoji: string): string {.slot.} =
|
||||
return self.delegate.addWatchOnlyAccount(address, accountName, color, emoji)
|
||||
|
||||
proc deleteAccount*(self: View, keyUid: string, address: string) {.slot.} =
|
||||
self.delegate.deleteAccount(keyUid, address)
|
||||
|
||||
|
@ -235,71 +171,4 @@ QtObject:
|
|||
self.tmpAddress = address
|
||||
|
||||
proc getAccountAssetsByAddress*(self: View): QVariant {.slot.} =
|
||||
return self.model.getAccountAssetsByAddress(self.tmpAddress)
|
||||
|
||||
proc setDerivedAddresses*(self: View, derivedAddresses: seq[wallet_account_service.DerivedAddressDto], error: string) =
|
||||
var items: seq[DerivedAddressItem] = @[]
|
||||
for item in derivedAddresses:
|
||||
items.add(initDerivedAddressItem(item.address, item.path, item.hasActivity, item.alreadyCreated))
|
||||
self.derivedAddresses.setItems(items)
|
||||
self.setDerivedAddressesError(error)
|
||||
self.setDerivedAddressesLoading(false)
|
||||
self.derivedAddressesChanged()
|
||||
|
||||
proc getDerivedAddress*(self: View, password: string, derivedFrom: string, path: string, hashPassword: bool) {.slot.} =
|
||||
self.setDerivedAddressesLoading(true)
|
||||
self.setDerivedAddressesError("")
|
||||
self.delegate.getDerivedAddress(password, derivedfrom, path, hashPassword)
|
||||
|
||||
proc getDerivedAddressList*(self: View, password: string, derivedFrom: string, path: string, pageSize: int, pageNumber: int, hashPassword: bool) {.slot.} =
|
||||
self.setDerivedAddressesLoading(true)
|
||||
self.setDerivedAddressesError("")
|
||||
self.delegate.getDerivedAddressList(password, derivedfrom, path, pageSize, pageNumber, hashPassword)
|
||||
|
||||
proc getDerivedAddressListForMnemonic*(self: View, mnemonic: string, path: string, pageSize: int, pageNumber: int) {.slot.} =
|
||||
self.setDerivedAddressesLoading(true)
|
||||
self.setDerivedAddressesError("")
|
||||
self.delegate.getDerivedAddressListForMnemonic(mnemonic, path, pageSize, pageNumber)
|
||||
|
||||
proc getDerivedAddressForPrivateKey*(self: View, privateKey: string) {.slot.} =
|
||||
self.setDerivedAddressesLoading(true)
|
||||
self.setDerivedAddressesError("")
|
||||
self.delegate.getDerivedAddressForPrivateKey(privateKey)
|
||||
|
||||
proc resetDerivedAddressModel*(self: View) {.slot.} =
|
||||
var items: seq[DerivedAddressItem] = @[]
|
||||
self.derivedAddresses.setItems(items)
|
||||
self.derivedAddressesChanged()
|
||||
|
||||
proc getDerivedAddressAtIndex*(self: View, index: int): string {.slot.} =
|
||||
return self.derivedAddresses.getDerivedAddressAtIndex(index)
|
||||
|
||||
proc getDerivedAddressPathAtIndex*(self: View, index: int): string {.slot.} =
|
||||
return self.derivedAddresses.getDerivedAddressPathAtIndex(index)
|
||||
|
||||
proc getDerivedAddressHasActivityAtIndex*(self: View, index: int): bool {.slot.} =
|
||||
return self.derivedAddresses.getDerivedAddressHasActivityAtIndex(index)
|
||||
|
||||
proc getDerivedAddressAlreadyCreatedAtIndex*(self: View, index: int): bool {.slot.} =
|
||||
return self.derivedAddresses.getDerivedAddressAlreadyCreatedAtIndex(index)
|
||||
|
||||
proc getNextSelectableDerivedAddressIndex*(self: View): int {.slot.} =
|
||||
return self.derivedAddresses.getNextSelectableDerivedAddressIndex()
|
||||
|
||||
proc validSeedPhrase*(self: View, value: string): bool {.slot.} =
|
||||
return self.delegate.validSeedPhrase(value)
|
||||
|
||||
proc userAuthenticationSuccess*(self: View, password: string) {.signal.}
|
||||
proc userAuthentiactionFail*(self: View) {.signal.}
|
||||
|
||||
proc authenticateUser*(self: View) {.slot.} =
|
||||
self.delegate.authenticateUser()
|
||||
|
||||
proc createSharedKeycardModule*(self: View) {.slot.} =
|
||||
self.delegate.createSharedKeycardModule()
|
||||
|
||||
proc destroySharedKeycarModule*(self: View) {.slot.} =
|
||||
self.delegate.destroySharedKeycarModule()
|
||||
|
||||
proc authenticateUserAndDeriveAddressOnKeycardForPath*(self: View, keyUid: string, derivationPath: string, searchForFirstAvailableAddress: bool) {.slot.} =
|
||||
self.delegate.authenticateUserAndDeriveAddressOnKeycardForPath(keyUid, derivationPath, searchForFirstAvailableAddress)
|
||||
return self.model.getAccountAssetsByAddress(self.tmpAddress)
|
|
@ -4,7 +4,7 @@ import ./controller, ./view
|
|||
import ./io_interface as io_interface
|
||||
import ../io_interface as delegate_interface
|
||||
|
||||
import ./accounts/module as accountsModule
|
||||
import ./accounts/module as accounts_module
|
||||
import ./all_tokens/module as all_tokens_module
|
||||
import ./collectibles/module as collectibles_module
|
||||
import ./current_account/module as current_account_module
|
||||
|
@ -70,7 +70,7 @@ proc newModule*(
|
|||
result.controller = newController(result, settingsService, walletAccountService, currencyService)
|
||||
result.view = newView(result)
|
||||
|
||||
result.accountsModule = accounts_module.newModule(result, events, keycardService, walletAccountService, accountsService, networkService, tokenService, currencyService)
|
||||
result.accountsModule = accounts_module.newModule(result, events, walletAccountService, networkService, currencyService)
|
||||
result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService)
|
||||
result.collectiblesModule = collectibles_module.newModule(result, events, collectibleService, walletAccountService, networkService, nodeService, networkConnectionService)
|
||||
result.currentAccountModule = current_account_module.newModule(result, events, walletAccountService, networkService, tokenService, currencyService)
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.14
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
import utils 1.0
|
||||
import "../stores"
|
||||
|
||||
ColumnLayout {
|
||||
id: derivationPathSelect
|
||||
|
||||
property string path: ""
|
||||
property bool useFullyCustomPath: true
|
||||
|
||||
function reset() {
|
||||
if (derivationPathSelect.useFullyCustomPath) {
|
||||
derivationPathFullyCustomInput.text = _internal.customDerivationRootPath
|
||||
}
|
||||
else {
|
||||
derivationPathStatusDefaultInput.text = _internal.defaultDerivationIndex
|
||||
}
|
||||
if (!_internal.userInputTimer.running) {
|
||||
_internal.userInputTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _internal
|
||||
readonly property string defaultDerivationIndex: "1"
|
||||
property var userInputTimer: Timer {
|
||||
// 1 second wait after each key press
|
||||
interval: 1000
|
||||
running: false
|
||||
onTriggered: {
|
||||
if (derivationPathSelect.useFullyCustomPath) {
|
||||
derivationPathSelect.path = derivationPathFullyCustomInput.text
|
||||
}
|
||||
else {
|
||||
if (derivationPathStatusDefaultInput.text === "") {
|
||||
return
|
||||
}
|
||||
derivationPathSelect.path = _internal.defaultDerivationRootPath + derivationPathStatusDefaultInput.text
|
||||
}
|
||||
}
|
||||
}
|
||||
property bool pathError: Utils.isInvalidPath(RootStore.derivedAddressesError)
|
||||
property bool derivationAddressLoading: RootStore.derivedAddressesLoading
|
||||
property string customDerivationRootPath: "m/44'/60'/0'/0"
|
||||
property string defaultDerivationRootPath: "m/44'/60'/0'/0/"
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loadedIcon
|
||||
StatusIcon {
|
||||
icon: _internal.pathError ? "cancel" : "checkmark"
|
||||
height: 14
|
||||
width: 14
|
||||
color: _internal.pathError ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: loadingIcon
|
||||
StatusLoadingIndicator {
|
||||
color: Theme.palette.directColor4
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fixedLeftPart
|
||||
StatusBaseText {
|
||||
rightPadding: 0
|
||||
text: _internal.defaultDerivationRootPath
|
||||
color: Theme.palette.baseColor1
|
||||
font: derivationPathStatusDefaultInput.font
|
||||
}
|
||||
}
|
||||
|
||||
spacing: 7
|
||||
|
||||
RowLayout {
|
||||
StatusBaseText {
|
||||
id: inputLabel
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Derivation Path")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
StatusButton {
|
||||
id: resetButton
|
||||
Layout.alignment: Qt.AlignTop
|
||||
size: StatusBaseButton.Size.Tiny
|
||||
text: qsTr("Reset")
|
||||
font.pixelSize: 15
|
||||
padding: 0
|
||||
normalColor: "transparent"
|
||||
onClicked: derivationPathSelect.reset()
|
||||
}
|
||||
}
|
||||
StatusInput {
|
||||
id: derivationPathFullyCustomInput
|
||||
Layout.preferredHeight: 64
|
||||
Layout.preferredWidth: parent.width
|
||||
visible: derivationPathSelect.useFullyCustomPath
|
||||
maximumHeight: 64
|
||||
text: _internal.customDerivationRootPath
|
||||
input.color: _internal.pathError ? Theme.palette.dangerColor1 : Theme.palette.directColor1
|
||||
input.rightComponent: _internal.derivationAddressLoading ? loadingIcon : loadedIcon
|
||||
onTextChanged: _internal.userInputTimer.start()
|
||||
}
|
||||
StatusInput {
|
||||
id: derivationPathStatusDefaultInput
|
||||
Layout.preferredHeight: 64
|
||||
Layout.preferredWidth: parent.width
|
||||
visible: !derivationPathSelect.useFullyCustomPath
|
||||
maximumHeight: 64
|
||||
text: _internal.defaultDerivationIndex
|
||||
input.color: _internal.pathError ? Theme.palette.dangerColor1 : Theme.palette.directColor1
|
||||
input.rightComponent: _internal.derivationAddressLoading ? loadingIcon : loadedIcon
|
||||
input.leftComponent: fixedLeftPart
|
||||
onTextChanged: _internal.userInputTimer.start()
|
||||
validationMode: StatusInput.ValidationMode.IgnoreInvalidInput
|
||||
validators: [
|
||||
StatusRegularExpressionValidator {
|
||||
regularExpression: /^[0-9]{0,9}$/
|
||||
errorMessage: ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,254 +0,0 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import "../stores"
|
||||
|
||||
Item {
|
||||
id: derivedAddresses
|
||||
|
||||
property int selectedAccountType: Constants.AddAccountType.GenerateNew
|
||||
property string selectedKeyUid: RootStore.defaultSelectedKeyUid
|
||||
property bool selectedKeyUidMigratedToKeycard: RootStore.defaultSelectedKeyUidMigratedToKeycard
|
||||
property string selectedPath: ""
|
||||
property string pathSubFix: ""
|
||||
property bool isLoading: RootStore.derivedAddressesLoading
|
||||
property bool pathError: Utils.isInvalidPath(RootStore.derivedAddressesError)
|
||||
|
||||
property alias selectedAddress: selectedDerivedAddress.title
|
||||
property alias selectedAddressAvailable: selectedDerivedAddress.enabled
|
||||
|
||||
function reset() {
|
||||
RootStore.resetDerivedAddressModel()
|
||||
_internal.nextSelectableAddressIndex = 0
|
||||
selectedDerivedAddress.pathSubFix = 0
|
||||
selectedDerivedAddress.title = "---"
|
||||
selectedDerivedAddress.subTitle = qsTr("No activity")
|
||||
selectedDerivedAddress.enabled = false
|
||||
}
|
||||
|
||||
onIsLoadingChanged: {
|
||||
if(isLoading) {
|
||||
selectedDerivedAddress.title = qsTr("Pending")
|
||||
selectedDerivedAddress.subTitle = ""
|
||||
}
|
||||
}
|
||||
|
||||
onPathErrorChanged: {
|
||||
if(pathError) {
|
||||
selectedDerivedAddress.title = qsTr("Invalid path")
|
||||
selectedDerivedAddress.subTitle = ""
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _internal
|
||||
|
||||
readonly property bool showEnterPinPassButton: !RootStore.loggedInUserAuthenticated &&
|
||||
derivedAddresses.selectedAccountType !== Constants.AddAccountType.ImportSeedPhrase &&
|
||||
derivedAddresses.selectedAccountType !== Constants.AddAccountType.ImportPrivateKey
|
||||
property int pageSize: 6
|
||||
property int noOfPages: Math.ceil(RootStore.derivedAddressesList.count/pageSize)
|
||||
property int lastPageSize: RootStore.derivedAddressesList.count - ((noOfPages -1) * pageSize)
|
||||
property bool isLastPage: stackLayout.currentIndex == (noOfPages - 1)
|
||||
property int nextSelectableAddressIndex: RootStore.getNextSelectableDerivedAddressIndex()
|
||||
|
||||
onNextSelectableAddressIndexChanged: {
|
||||
stackLayout.currentIndex = nextSelectableAddressIndex/_internal.pageSize
|
||||
if(nextSelectableAddressIndex >= 0 && nextSelectableAddressIndex < RootStore.derivedAddressesList.count) {
|
||||
selectedDerivedAddress.title = RootStore.getDerivedAddressData(nextSelectableAddressIndex)
|
||||
selectedDerivedAddress.hasActivity = RootStore.getDerivedAddressHasActivityData(nextSelectableAddressIndex)
|
||||
selectedDerivedAddress.subTitle = RootStore.getDerivedAddressHasActivityData(nextSelectableAddressIndex) ? qsTr("Has Activity"): qsTr("No Activity")
|
||||
selectedDerivedAddress.enabled = !RootStore.getDerivedAddressAlreadyCreatedData(nextSelectableAddressIndex)
|
||||
selectedDerivedAddress.pathSubFix = nextSelectableAddressIndex
|
||||
}
|
||||
}
|
||||
|
||||
// dimensions
|
||||
property int popupWidth: 359
|
||||
property int maxAddressWidth: 102
|
||||
|
||||
function runAction() {
|
||||
if (derivedAddresses.selectedKeyUidMigratedToKeycard)
|
||||
RootStore.authenticateUserAndDeriveAddressOnKeycardForPath(derivedAddresses.selectedKeyUid, derivedAddresses.selectedPath, false)
|
||||
else
|
||||
RootStore.authenticateUser()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RootStore.derivedAddressesList
|
||||
function onModelReset() {
|
||||
_internal.pageSize = 0
|
||||
_internal.pageSize = 6
|
||||
_internal.nextSelectableAddressIndex = -1
|
||||
_internal.nextSelectableAddressIndex = RootStore.getNextSelectableDerivedAddressIndex()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
width: parent.width
|
||||
spacing: 7
|
||||
StatusBaseText {
|
||||
id: inputLabel
|
||||
width: parent.width
|
||||
text: qsTr("Account")
|
||||
font.pixelSize: 15
|
||||
color: selectedDerivedAddress.enabled ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||
}
|
||||
StatusListItem {
|
||||
id: selectedDerivedAddress
|
||||
property int pathSubFix: 0
|
||||
property bool hasActivity: false
|
||||
implicitWidth: parent.width
|
||||
visible: !_internal.showEnterPinPassButton
|
||||
color: "transparent"
|
||||
border.width: 1
|
||||
border.color: Theme.palette.baseColor2
|
||||
title: "---"
|
||||
subTitle: selectedDerivedAddress.hasActivity ? qsTr("Has Activity"): qsTr("No Activity")
|
||||
statusListItemSubTitle.color: selectedDerivedAddress.hasActivity ? Theme.palette.primaryColor1 : Theme.palette.baseColor1
|
||||
statusListItemTitle.wrapMode: Text.NoWrap
|
||||
statusListItemTitle.width: _internal.maxAddressWidth
|
||||
statusListItemTitle.elide: Qt.ElideMiddle
|
||||
statusListItemTitle.anchors.left: undefined
|
||||
statusListItemTitle.anchors.right: undefined
|
||||
components: [
|
||||
StatusIcon {
|
||||
width: 24
|
||||
height: 24
|
||||
icon: "chevron-down"
|
||||
color: Theme.palette.baseColor1
|
||||
visible: RootStore.derivedAddressesList.count > 1
|
||||
}
|
||||
]
|
||||
onClicked: {
|
||||
if(RootStore.derivedAddressesList.count > 0 && RootStore.derivedAddressesList.count !== 1)
|
||||
derivedAddressPopup.popup(derivedAddresses.x - layout.width - Style.current.bigPadding , derivedAddresses.y + layout.height + 8)
|
||||
}
|
||||
enabled: RootStore.derivedAddressesList.count > 0
|
||||
Component.onCompleted: derivedAddresses.pathSubFix = Qt.binding(function() { return pathSubFix})
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
visible: _internal.showEnterPinPassButton
|
||||
text: qsTr("Preview address")
|
||||
highlighted: focus
|
||||
|
||||
onClicked: _internal.runAction()
|
||||
}
|
||||
}
|
||||
|
||||
StatusMenu {
|
||||
id: derivedAddressPopup
|
||||
width: _internal.popupWidth
|
||||
contentItem: Column {
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
Layout.fillWidth:true
|
||||
Layout.fillHeight: true
|
||||
Repeater {
|
||||
id: pageModel
|
||||
model: _internal.noOfPages
|
||||
delegate: Page {
|
||||
id: page
|
||||
contentItem: ColumnLayout {
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: _internal.isLastPage ? _internal.lastPageSize : _internal.pageSize
|
||||
delegate: StatusListItem {
|
||||
id: element
|
||||
property int actualIndex: index + (stackLayout.currentIndex* _internal.pageSize)
|
||||
property bool hasActivity: {
|
||||
if(actualIndex >= 0 && actualIndex < RootStore.derivedAddressesList.count) {
|
||||
return RootStore.getDerivedAddressHasActivityData(actualIndex)
|
||||
}
|
||||
return false
|
||||
}
|
||||
implicitWidth: derivedAddressPopup.width
|
||||
statusListItemTitle.wrapMode: Text.NoWrap
|
||||
statusListItemTitle.width: _internal.maxAddressWidth
|
||||
statusListItemTitle.elide: Qt.ElideMiddle
|
||||
statusListItemTitle.anchors.left: undefined
|
||||
statusListItemTitle.anchors.right: undefined
|
||||
title: {
|
||||
if(actualIndex >= 0 && actualIndex < RootStore.derivedAddressesList.count) {
|
||||
return RootStore.getDerivedAddressData(actualIndex)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
subTitle: element.hasActivity ? qsTr("Has Activity"): qsTr("No Activity")
|
||||
statusListItemSubTitle.color: element.hasActivity ? Theme.palette.primaryColor1 : Theme.palette.baseColor1
|
||||
enabled: {
|
||||
if(actualIndex >= 0 && actualIndex < RootStore.derivedAddressesList.count) {
|
||||
return !RootStore.getDerivedAddressAlreadyCreatedData(actualIndex)
|
||||
}
|
||||
return true
|
||||
}
|
||||
components: [
|
||||
StatusBaseText {
|
||||
text: element.actualIndex
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
},
|
||||
Rectangle {
|
||||
radius: width/2
|
||||
height: 5
|
||||
width: 5
|
||||
color: Theme.palette.primaryColor1
|
||||
visible: element.hasActivity
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
]
|
||||
onClicked: {
|
||||
selectedDerivedAddress.title = title
|
||||
selectedDerivedAddress.subTitle = subTitle
|
||||
selectedDerivedAddress.pathSubFix = actualIndex
|
||||
selectedDerivedAddress.hasActivity = element.hasActivity
|
||||
derivedAddressPopup.close()
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if(RootStore.derivedAddressesList.count === 1 && index === 0) {
|
||||
selectedDerivedAddress.title = title
|
||||
selectedDerivedAddress.subTitle = subTitle
|
||||
selectedDerivedAddress.pathSubFix = actualIndex
|
||||
selectedDerivedAddress.hasActivity = element.hasActivity
|
||||
selectedDerivedAddress.enabled = !RootStore.getDerivedAddressAlreadyCreatedData(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
implicitWidth: stackLayout.width
|
||||
implicitHeight: stackLayout.height
|
||||
color: Theme.palette.statusMenu.backgroundColor
|
||||
radius: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PageIndicator {
|
||||
id: pageIndicator
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
interactive: true
|
||||
currentIndex: stackLayout.currentIndex
|
||||
count: stackLayout.count
|
||||
onCurrentIndexChanged: stackLayout.currentIndex = currentIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.14
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
|
||||
import utils 1.0
|
||||
import "../stores"
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
property string text: privateKey.text
|
||||
property bool valid: privateKey.valid
|
||||
|
||||
function resetMe() {
|
||||
d.errorString = ""
|
||||
privateKey.text = ""
|
||||
privateKey.reset()
|
||||
}
|
||||
|
||||
function validateMe() {
|
||||
if (privateKey.text === "") {
|
||||
d.errorString = qsTr("You need to enter a private key")
|
||||
} else if (!Utils.isPrivateKey(privateKey.text)) {
|
||||
d.errorString = qsTr("Enter a valid private key (64 characters hexadecimal string)")
|
||||
} else {
|
||||
d.errorString = ""
|
||||
}
|
||||
return d.errorString === "" && !d.invalidPrivateKeyError
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int privateKeyCharLimit: 66
|
||||
property string errorString: ""
|
||||
readonly property bool accountAreadyAddedError: Utils.accountAlreadyExistsError(RootStore.derivedAddressesError)
|
||||
readonly property bool invalidPrivateKeyError: Utils.isInvalidPrivateKey(RootStore.derivedAddressesError)
|
||||
}
|
||||
|
||||
spacing: 24
|
||||
|
||||
StatusInput {
|
||||
id: privateKey
|
||||
|
||||
label: qsTr("Private key")
|
||||
charLimit: d.privateKeyCharLimit
|
||||
input.multiline: true
|
||||
minimumHeight: 80
|
||||
maximumHeight: 108
|
||||
placeholderText: qsTr("Paste the contents of your private key")
|
||||
errorMessage: d.errorString
|
||||
validators: [
|
||||
StatusMinLengthValidator {
|
||||
minLength: 1
|
||||
errorMessage: qsTr("You need to enter a private key")
|
||||
},
|
||||
StatusValidator {
|
||||
property var validate: function (value) {
|
||||
return Utils.isPrivateKey(value)
|
||||
}
|
||||
errorMessage: qsTr("Enter a valid private key (64 characters hexadecimal string)")
|
||||
}
|
||||
]
|
||||
asyncValidators: [
|
||||
StatusAsyncValidator {
|
||||
id: privateKeyAsyncValidator
|
||||
Connections {
|
||||
target: d
|
||||
function onInvalidPrivateKeyErrorChanged() {
|
||||
privateKeyAsyncValidator.validationComplete("", !d.invalidPrivateKeyError)
|
||||
}
|
||||
}
|
||||
validate: (value) => !d.invalidPrivateKeyError
|
||||
name: "asyncPKCheck"
|
||||
errorMessage: qsTr("Enter a valid private key")
|
||||
}
|
||||
]
|
||||
onTextChanged: {
|
||||
if(valid) {
|
||||
RootStore.getDerivedAddressForPrivateKey(text)
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if(visible)
|
||||
privateKey.input.edit.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
|
||||
StatusBaseText {
|
||||
id: inputLabel
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Public address")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
StatusListItem {
|
||||
id: derivedAddress
|
||||
property string address: RootStore.derivedAddressesList.count > 0 ? RootStore.getDerivedAddressData(0) : "---"
|
||||
property bool hasActivity: RootStore.derivedAddressesList.count > 0 ? RootStore.getDerivedAddressHasActivityData(0) : false
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
asset.bgColor: "transparent"
|
||||
border.width: 1
|
||||
border.color: Theme.palette.baseColor2
|
||||
type: d.accountAreadyAddedError ? StatusListItem.Type.Danger : StatusListItem.Type.Primary
|
||||
statusListItemSubTitle.color: derivedAddress.hasActivity ? Theme.palette.primaryColor1 : Theme.palette.baseColor1
|
||||
title: d.accountAreadyAddedError ? qsTr("Account already added") : RootStore.derivedAddressesLoading ? qsTr("Pending") : derivedAddress.address
|
||||
subTitle: RootStore.derivedAddressesLoading || d.accountAreadyAddedError ? "" : derivedAddress.hasActivity ? qsTr("Has Activity"): qsTr("No Activity")
|
||||
sensor.enabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,268 +0,0 @@
|
|||
import QtQuick 2.12
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
|
||||
import utils 1.0
|
||||
import shared.stores 1.0
|
||||
import shared.controls 1.0
|
||||
|
||||
import "../stores"
|
||||
|
||||
StatusGridView {
|
||||
id: grid
|
||||
|
||||
property bool isValid: false
|
||||
property string mnemonicString: ""
|
||||
property int preferredHeight: (cellHeight * model/2) + footerItem.height
|
||||
signal enterPressed()
|
||||
|
||||
function reset() {
|
||||
_internal.errorString = ""
|
||||
mnemonicString = ""
|
||||
_internal.mnemonicInput = [];
|
||||
if (!grid.atXBeginning) {
|
||||
grid.positionViewAtBeginning();
|
||||
}
|
||||
for(var i = 0; i < grid.model; i++) {
|
||||
if(grid.itemAtIndex(i)) {
|
||||
grid.itemAtIndex(i).textEdit.text = ""
|
||||
grid.itemAtIndex(i).textEdit.reset()
|
||||
}
|
||||
}
|
||||
grid.isValid = false
|
||||
}
|
||||
|
||||
function validate() {
|
||||
_internal.errorString = ""
|
||||
if (!Utils.isMnemonic(mnemonicString)) {
|
||||
_internal.errorString = qsTr("Invalid seed phrase")
|
||||
} else {
|
||||
if (!RootStore.validSeedPhrase(mnemonicString)) {
|
||||
_internal.errorString = qsTr("Invalid seed phrase") + '. ' +
|
||||
qsTr("This seed phrase doesn't match our supported dictionary. Check for misspelled words.")
|
||||
}
|
||||
}
|
||||
return _internal.errorString === ""
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _internal
|
||||
property int seedPhraseInputWidth: (parent.width/2)
|
||||
property int seedPhraseInputHeight: 48
|
||||
property var mnemonicInput: []
|
||||
property string errorString: ""
|
||||
readonly property var seedPhraseWordsOptions: [12, 18, 24]
|
||||
|
||||
function getSeedPhraseString() {
|
||||
var seedPhrase = ""
|
||||
for(var i = 0; i < grid.model; i++) {
|
||||
if(!!grid.itemAtIndex(i)) {
|
||||
seedPhrase += grid.itemAtIndex(i).text + " "
|
||||
}
|
||||
}
|
||||
return seedPhrase
|
||||
}
|
||||
}
|
||||
|
||||
cellWidth: _internal.seedPhraseInputWidth
|
||||
cellHeight: _internal.seedPhraseInputHeight
|
||||
interactive: false
|
||||
z: 100000
|
||||
|
||||
onModelChanged: {
|
||||
mnemonicString = "";
|
||||
let menmonicInputTemp = _internal.mnemonicInput.filter(function(value) {
|
||||
return value.pos <= grid.count
|
||||
})
|
||||
_internal.mnemonicInput = []
|
||||
for (let i = 0; i < menmonicInputTemp.length; i++) {
|
||||
// .pos starts with 1
|
||||
grid.itemAtIndex(menmonicInputTemp[i].pos - 1).setWord(menmonicInputTemp[i].seed)
|
||||
grid.addWord(menmonicInputTemp[i].pos,
|
||||
menmonicInputTemp[i].seed,
|
||||
true)
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
}
|
||||
|
||||
|
||||
onIsValidChanged: {
|
||||
if(isValid) {
|
||||
mnemonicString = _internal.getSeedPhraseString()
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if(visible) {
|
||||
grid.itemAtIndex(0).textEdit.input.edit.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
function pasteWords () {
|
||||
const clipboardText = globalUtils.getFromClipboard()
|
||||
// Split words separated by commas and or blank spaces (spaces, enters, tabs)
|
||||
let words = clipboardText.split(/[, \s]+/)
|
||||
|
||||
let timeout = 0
|
||||
let indexOfWordsOption = _internal.seedPhraseWordsOptions.indexOf(words.length)
|
||||
if(indexOfWordsOption == -1) {
|
||||
return false
|
||||
}
|
||||
footerItem.switchToIndex(indexOfWordsOption)
|
||||
timeout = 100
|
||||
|
||||
timer.setTimeout(function(){
|
||||
_internal.mnemonicInput = []
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
const item = grid.itemAtIndex(i)
|
||||
if (item && item.leftComponentText)
|
||||
item.setWord(words[i])
|
||||
}
|
||||
}, timeout);
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function addWord(pos, word, ignoreGoingNext) {
|
||||
_internal.mnemonicInput.push({"pos": pos, "seed": word.replace(/\s/g, '')});
|
||||
for (var j = 0; j < _internal.mnemonicInput.length; j++) {
|
||||
if (_internal.mnemonicInput[j].pos === pos && _internal.mnemonicInput[j].seed !== word) {
|
||||
_internal.mnemonicInput[j].seed = word;
|
||||
}
|
||||
}
|
||||
//remove duplicates
|
||||
var valueArr = _internal.mnemonicInput.map(function(item){ return item.pos });
|
||||
var isDuplicate = valueArr.some(function(item, idx){
|
||||
if (valueArr.indexOf(item) !== idx) {
|
||||
_internal.mnemonicInput.splice(idx, 1);
|
||||
}
|
||||
return valueArr.indexOf(item) !== idx
|
||||
});
|
||||
if (!ignoreGoingNext) {
|
||||
for (var i = !grid.atXBeginning ? 12 : 0; i < grid.count; i++) {
|
||||
if (parseInt(grid.itemAtIndex(i).leftComponentText) === (parseInt(pos)+1)) {
|
||||
grid.currentIndex = grid.itemAtIndex(i).itemIndex;
|
||||
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus();
|
||||
if (grid.currentIndex === 11) {
|
||||
grid.positionViewAtEnd();
|
||||
if (grid.count === 20) {
|
||||
grid.contentX = 1500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
grid.isValid = (_internal.mnemonicInput.length === grid.model);
|
||||
}
|
||||
|
||||
delegate: StatusSeedPhraseInput {
|
||||
id: statusSeedInput
|
||||
width: grid.cellWidth - (Style.current.halfPadding/2)
|
||||
height: (grid.cellHeight - Style.current.halfPadding)
|
||||
textEdit.errorMessageCmp.visible: false
|
||||
textEdit.input.placeholder.objectName: "seedPhraseInputPlaceholder" + index
|
||||
leftComponentText: index + 1
|
||||
inputList: BIP39_en { }
|
||||
property int itemIndex: index
|
||||
z: (grid.currentIndex === index) ? 150000000 : 0
|
||||
onDoneInsertingWord: {
|
||||
grid.addWord(leftComponentText, word)
|
||||
}
|
||||
onEditClicked: {
|
||||
grid.currentIndex = index;
|
||||
grid.itemAtIndex(index).textEdit.input.edit.forceActiveFocus();
|
||||
}
|
||||
onKeyPressed: {
|
||||
if (event.key === Qt.Key_Backtab) {
|
||||
for (var i = !grid.atXBeginning ? 12 : 0; i < grid.count; i++) {
|
||||
if (parseInt(grid.itemAtIndex(i).leftComponentText) === ((parseInt(leftComponentText)-1) >= 0 ? (parseInt(leftComponentText)-1) : 0)) {
|
||||
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus(Qt.TabFocusReason);
|
||||
textEdit.input.tabNavItem = grid.itemAtIndex(i).textEdit.input.edit;
|
||||
event.accepted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
for (var i = !grid.atXBeginning ? 12 : 0; i < grid.count; i++) {
|
||||
if (parseInt(grid.itemAtIndex(i).leftComponentText) === ((parseInt(leftComponentText)+1) <= grid.count ? (parseInt(leftComponentText)+1) : grid.count)) {
|
||||
grid.itemAtIndex(i).textEdit.input.edit.forceActiveFocus(Qt.TabFocusReason);
|
||||
textEdit.input.tabNavItem = grid.itemAtIndex(i).textEdit.input.edit;
|
||||
event.accepted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.matches(StandardKey.Paste)) {
|
||||
if (grid.pasteWords()) {
|
||||
// Paste was done by splitting the words
|
||||
event.accepted = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
event.accepted = true
|
||||
grid.enterPressed()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Delete || event.key === Qt.Key_Backspace) {
|
||||
var wordIndex = _internal.mnemonicInput.findIndex(x => x.pos === leftComponentText);
|
||||
if (wordIndex > -1) {
|
||||
_internal.mnemonicInput.splice(wordIndex , 1);
|
||||
grid.isValid = _internal.mnemonicInput.length === grid.model
|
||||
}
|
||||
}
|
||||
|
||||
grid.currentIndex = index;
|
||||
}
|
||||
}
|
||||
footer: Item {
|
||||
id: footerC
|
||||
function switchToIndex(index) {
|
||||
changeSeedNbWordsTabBar.currentIndex = index
|
||||
}
|
||||
width: grid.width - (Style.current.halfPadding/2)
|
||||
height: changeSeedNbWordsTabBar.height + errorMessage.height + Style.current.padding*2
|
||||
StatusBaseText {
|
||||
id: errorMessage
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.padding
|
||||
|
||||
height: visible ? implicitHeight : 0
|
||||
visible: !!text
|
||||
text: _internal.errorString
|
||||
|
||||
font.pixelSize: 12
|
||||
color: Theme.palette.dangerColor1
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
StatusSwitchTabBar {
|
||||
id: changeSeedNbWordsTabBar
|
||||
width: parent.width
|
||||
anchors.top: errorMessage.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
Repeater {
|
||||
model: _internal.seedPhraseWordsOptions
|
||||
StatusSwitchTabButton {
|
||||
text: qsTr("%n word(s)", "", modelData)
|
||||
}
|
||||
}
|
||||
onCurrentIndexChanged: {
|
||||
grid.model = _internal.seedPhraseWordsOptions[changeSeedNbWordsTabBar.currentIndex]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
import QtQuick 2.12
|
||||
import QtQml.Models 2.14
|
||||
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
import utils 1.0
|
||||
import "../stores"
|
||||
|
||||
StatusSelect {
|
||||
id: selectAccountType
|
||||
|
||||
property int addAccountType
|
||||
property string derivedFromAddress: ""
|
||||
property string selectedKeyUid: userProfile.keyUid
|
||||
property bool selectedKeyUidMigratedToKeycard: userProfile.isKeycardUser
|
||||
|
||||
function resetMe() {
|
||||
_internal.getGeneratedAccountsModel()
|
||||
selectAccountType.addAccountType = Constants.AddAccountType.GenerateNew
|
||||
selectAccountType.selectedKeyUid = userProfile.keyUid
|
||||
selectAccountType.selectedKeyUidMigratedToKeycard = userProfile.isKeycardUser
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RootStore.generatedAccountsViewModel
|
||||
function onModelReset() {
|
||||
_internal.delegateModel.model = RootStore.generatedAccountsViewModel
|
||||
_internal.getGeneratedAccountsModel()
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _internal
|
||||
property string importSeedPhraseID: "importNewSeedPhrase"
|
||||
property string importPrivateKeyID: "generateFromPrivateKey"
|
||||
property string addWatchOnlyAccountID: "watchOnlyAccount"
|
||||
|
||||
property var delegateModel: DelegateModel {
|
||||
model: RootStore.generatedAccountsViewModel
|
||||
onModelUpdated: {
|
||||
_internal.getGeneratedAccountsModel()
|
||||
}
|
||||
}
|
||||
property ListModel generatedAccountsModel: ListModel{}
|
||||
|
||||
function getGeneratedAccountsModel() {
|
||||
if(generatedAccountsModel) {
|
||||
generatedAccountsModel.clear()
|
||||
for (var row = 0; row < _internal.delegateModel.model.count; row++) {
|
||||
if (_internal.delegateModel.items.count > 0) {
|
||||
var item = _internal.delegateModel.items.get(row).model;
|
||||
generatedAccountsModel.append({"name": item.name, "iconName": item.iconName, "generatedModel": item.generatedModel, "derivedfrom": item.derivedfrom, "isHeader": false,
|
||||
"keyUid": item.keyUid, "migratedToKeycard": item.migratedToKeycard})
|
||||
if (row === 0 && _internal.delegateModel.model.count > 1) {
|
||||
generatedAccountsModel.append({"name": qsTr("Imported"), "iconName": "", "derivedfrom": "", "isHeader": true, "keyUid": "", "migratedToKeycard": false})
|
||||
}
|
||||
}
|
||||
}
|
||||
generatedAccountsModel.append({"name": qsTr("Add new"), "iconName": "", "derivedfrom": "", "isHeader": true, "keyUid": "", "migratedToKeycard": false})
|
||||
generatedAccountsModel.append({"name": qsTr("Import new Seed Phrase"), "iconName": "seed-phrase", "derivedfrom": "", "isHeader": false, "keyUid": "", "migratedToKeycard": false, "objectName": _internal.importSeedPhraseID})
|
||||
generatedAccountsModel.append({"name": qsTr("Generate from Private key"), "iconName": "password", "derivedfrom": "", "isHeader": false, "keyUid": "", "migratedToKeycard": false, "objectName": _internal.importPrivateKeyID})
|
||||
generatedAccountsModel.append({"name": qsTr("Add a watch-only address"), "iconName": "show", "derivedfrom": "", "isHeader": false, "keyUid": "", "migratedToKeycard": false, "objectName": _internal.addWatchOnlyAccountID})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label: qsTr("Origin")
|
||||
model: _internal.generatedAccountsModel
|
||||
selectedItemComponent: StatusListItem {
|
||||
id: selectedItem
|
||||
asset.bgColor: "transparent"
|
||||
border.width: 1
|
||||
border.color: Theme.palette.baseColor2
|
||||
tagsDelegate: StatusListItemTag {
|
||||
bgColor: model.color
|
||||
bgRadius: 6
|
||||
height: Style.current.bigPadding
|
||||
closeButtonVisible: false
|
||||
asset.emoji: model.emoji
|
||||
asset.emojiSize: Emoji.size.verySmall
|
||||
asset.isLetterIdenticon: true
|
||||
title: model.name
|
||||
titleText.font.pixelSize: 12
|
||||
titleText.color: Theme.palette.indirectColor1
|
||||
}
|
||||
}
|
||||
menuDelegate: StatusListItem {
|
||||
id: defaultListItem
|
||||
objectName: !!model.objectName ? model.objectName : ""
|
||||
title: model.name
|
||||
asset.name: model.iconName
|
||||
tagsModel : model.generatedModel
|
||||
enabled: !model.isHeader
|
||||
asset.bgColor: "transparent"
|
||||
asset.color: model.generatedModel ? Theme.palette.primaryColor1 : Theme.palette.directColor5
|
||||
tagsDelegate: StatusListItemTag {
|
||||
bgColor: model.color
|
||||
bgRadius: 6
|
||||
height: 24
|
||||
closeButtonVisible: false
|
||||
asset.emoji: model.emoji
|
||||
asset.emojiSize: Emoji.size.verySmall
|
||||
asset.isLetterIdenticon: true
|
||||
title: model.name
|
||||
titleText.font.pixelSize: 12
|
||||
titleText.color: Theme.palette.indirectColor1
|
||||
}
|
||||
onClicked: {
|
||||
selectAccountType.addAccountType = (model.objectName === _internal.importSeedPhraseID) ? Constants.AddAccountType.ImportSeedPhrase :
|
||||
(model.objectName === _internal.importPrivateKeyID) ? Constants.AddAccountType.ImportPrivateKey :
|
||||
(model.objectName === _internal.addWatchOnlyAccountID) ? Constants.AddAccountType.WatchOnly :
|
||||
Constants.AddAccountType.GenerateNew
|
||||
selectedItem.title = model.name
|
||||
selectedItem.asset.name = model.iconName
|
||||
selectedItem.tagsModel = model.generatedModel
|
||||
selectedItem.enabled = !model.isHeader
|
||||
|
||||
selectAccountType.derivedFromAddress = model.derivedfrom
|
||||
selectAccountType.selectedKeyUid = model.keyUid
|
||||
selectAccountType.selectedKeyUidMigratedToKeycard = model.migratedToKeycard
|
||||
|
||||
selectMenu.close()
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if(index === 0) {
|
||||
selectedItem.title = model.name
|
||||
selectedItem.asset.name = model.iconName
|
||||
selectedItem.tagsModel = model.generatedModel
|
||||
selectedItem.enabled = !model.isHeader
|
||||
selectAccountType.derivedFromAddress = model.derivedfrom
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,352 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
import QtQuick.Layouts 1.14
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
import shared.controls 1.0
|
||||
|
||||
import "../stores"
|
||||
import "../views"
|
||||
import "../panels"
|
||||
|
||||
StatusModal {
|
||||
id: root
|
||||
|
||||
readonly property int marginBetweenInputs: 38
|
||||
property var emojiPopup: null
|
||||
|
||||
header.title: qsTr("Generate an account")
|
||||
closePolicy: nextButton.loading? Popup.NoAutoClose : Popup.CloseOnEscape
|
||||
hasCloseButton: !nextButton.loading
|
||||
|
||||
signal afterAddAccount()
|
||||
|
||||
Connections {
|
||||
target: emojiPopup
|
||||
enabled: root.opened
|
||||
|
||||
function onEmojiSelected (emojiText, atCursor) {
|
||||
accountNameInput.input.asset.emoji = emojiText
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: walletSectionAccounts
|
||||
function onUserAuthenticationSuccess(password: string) {
|
||||
validationError.text = ""
|
||||
d.password = password
|
||||
RootStore.loggedInUserAuthenticated = true
|
||||
if (d.selectedAccountType === Constants.AddAccountType.ImportPrivateKey) {
|
||||
d.generateNewAccount()
|
||||
}
|
||||
else {
|
||||
if (!d.selectedKeyUidMigratedToKeycard) {
|
||||
d.getDerivedAddressList()
|
||||
}
|
||||
}
|
||||
}
|
||||
function onUserAuthentiactionFail() {
|
||||
d.password = ""
|
||||
RootStore.loggedInUserAuthenticated = false
|
||||
validationError.text = qsTr("An authentication failed")
|
||||
nextButton.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property int numOfItems: 100
|
||||
readonly property int pageNumber: 1
|
||||
|
||||
property string password: ""
|
||||
property int selectedAccountType: Constants.AddAccountType.GenerateNew
|
||||
property string selectedAccountDerivedFromAddress: ""
|
||||
property string selectedKeyUid: RootStore.defaultSelectedKeyUid
|
||||
property bool selectedKeyUidMigratedToKeycard: RootStore.defaultSelectedKeyUidMigratedToKeycard
|
||||
property string selectedPath: ""
|
||||
property string selectedAddress: ""
|
||||
property bool selectedAddressAvailable: false
|
||||
property bool useFullyCustomPath: false
|
||||
|
||||
readonly property bool authenticationNeeded: d.selectedAccountType !== Constants.AddAccountType.WatchOnly &&
|
||||
d.password === ""
|
||||
property string addAccountIcon: ""
|
||||
|
||||
property bool isLoading: RootStore.derivedAddressesLoading
|
||||
onIsLoadingChanged: {
|
||||
if(!isLoading && nextButton.loading) {
|
||||
d.generateNewAccount()
|
||||
}
|
||||
}
|
||||
|
||||
function getDerivedAddressList() {
|
||||
if (d.useFullyCustomPath) {
|
||||
if(!!d.selectedPath && !!d.selectedAccountDerivedFromAddress
|
||||
&& (d.password.length > 0)) {
|
||||
RootStore.getDerivedAddressList(d.password, d.selectedAccountDerivedFromAddress,
|
||||
d.selectedPath, numOfItems, pageNumber,
|
||||
!(d.selectedKeyUidMigratedToKeycard || userProfile.isKeycardUser))
|
||||
}
|
||||
} else if(d.selectedAccountType === Constants.AddAccountType.ImportSeedPhrase
|
||||
&& !!advancedSelection.expandableItem.path
|
||||
&& !!advancedSelection.expandableItem.mnemonicText) {
|
||||
RootStore.getDerivedAddressListForMnemonic(advancedSelection.expandableItem.mnemonicText,
|
||||
advancedSelection.expandableItem.path, numOfItems, pageNumber)
|
||||
} else if(!!d.selectedPath && !!d.selectedAccountDerivedFromAddress
|
||||
&& (d.password.length > 0)) {
|
||||
RootStore.getDerivedAddress(d.password, d.selectedAccountDerivedFromAddress, d.selectedPath,
|
||||
!(d.selectedKeyUidMigratedToKeycard || userProfile.isKeycardUser))
|
||||
}
|
||||
}
|
||||
|
||||
function generateNewAccount() {
|
||||
// TODO the loading doesn't work because the function freezes the view. Might need to use threads
|
||||
if (!advancedSelection.validate()) {
|
||||
Global.playErrorSound()
|
||||
return nextButton.loading = false
|
||||
}
|
||||
|
||||
let errMessage = ""
|
||||
|
||||
switch(d.selectedAccountType) {
|
||||
case Constants.AddAccountType.GenerateNew:
|
||||
if (d.selectedKeyUidMigratedToKeycard) {
|
||||
errMessage = RootStore.addNewWalletAccountGeneratedFromKeycard(Constants.generatedWalletType,
|
||||
accountNameInput.text,
|
||||
colorSelectionGrid.selectedColor,
|
||||
accountNameInput.input.asset.emoji)
|
||||
}
|
||||
else {
|
||||
let finalFullPath = advancedSelection.expandableItem.completePath
|
||||
if (!d.useFullyCustomPath) {
|
||||
finalFullPath = d.selectedPath
|
||||
}
|
||||
errMessage = RootStore.generateNewAccount(d.password, accountNameInput.text, colorSelectionGrid.selectedColor,
|
||||
accountNameInput.input.asset.emoji, finalFullPath,
|
||||
advancedSelection.expandableItem.derivedFromAddress)
|
||||
}
|
||||
break
|
||||
case Constants.AddAccountType.ImportSeedPhrase:
|
||||
errMessage = RootStore.addAccountsFromSeed(advancedSelection.expandableItem.mnemonicText, d.password,
|
||||
accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.asset.emoji,
|
||||
advancedSelection.expandableItem.completePath)
|
||||
break
|
||||
case Constants.AddAccountType.ImportPrivateKey:
|
||||
errMessage = RootStore.addAccountsFromPrivateKey(advancedSelection.expandableItem.privateKey, d.password,
|
||||
accountNameInput.text, colorSelectionGrid.selectedColor, accountNameInput.input.asset.emoji)
|
||||
break
|
||||
case Constants.AddAccountType.WatchOnly:
|
||||
errMessage = RootStore.addWatchOnlyAccount(advancedSelection.expandableItem.watchAddress, accountNameInput.text,
|
||||
colorSelectionGrid.selectedColor, accountNameInput.input.asset.emoji)
|
||||
break
|
||||
}
|
||||
|
||||
nextButton.loading = false
|
||||
|
||||
if (errMessage) {
|
||||
console.warn(`Unhandled error case. Status-go message: ${errMessage}`)
|
||||
} else {
|
||||
root.afterAddAccount()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
||||
function nextButtonClicked() {
|
||||
nextButton.loading = true
|
||||
if (d.authenticationNeeded) {
|
||||
d.password = ""
|
||||
if (d.selectedKeyUidMigratedToKeycard &&
|
||||
d.selectedAccountType === Constants.AddAccountType.GenerateNew) {
|
||||
RootStore.authenticateUserAndDeriveAddressOnKeycardForPath(d.selectedKeyUid, d.selectedPath, true)
|
||||
}
|
||||
else {
|
||||
RootStore.authenticateUser()
|
||||
}
|
||||
}
|
||||
else {
|
||||
d.generateNewAccount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
RootStore.loggedInUserAuthenticated = false
|
||||
d.addAccountIcon = "password"
|
||||
if (RootStore.loggedInUserUsesBiometricLogin()) {
|
||||
d.addAccountIcon = "touch-id"
|
||||
}
|
||||
else if (RootStore.loggedInUserIsKeycardUser()) {
|
||||
d.addAccountIcon = "keycard"
|
||||
}
|
||||
|
||||
accountNameInput.input.asset.emoji = StatusQUtils.Emoji.getRandomEmoji(StatusQUtils.Emoji.size.verySmall)
|
||||
colorSelectionGrid.selectedColorIndex = Math.floor(Math.random() * colorSelectionGrid.model.length)
|
||||
accountNameInput.input.edit.forceActiveFocus()
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
RootStore.loggedInUserAuthenticated = false
|
||||
d.password = ""
|
||||
validationError.text = ""
|
||||
accountNameInput.reset()
|
||||
advancedSelection.expanded = false
|
||||
advancedSelection.reset()
|
||||
}
|
||||
|
||||
contentItem: StatusScrollView {
|
||||
id: scroll
|
||||
implicitWidth: root.width
|
||||
implicitHeight: 400
|
||||
topPadding: Style.current.halfPadding
|
||||
bottomPadding: Style.current.halfPadding
|
||||
leftPadding: Style.current.padding
|
||||
rightPadding: Style.current.padding
|
||||
objectName: "AddAccountModalContent"
|
||||
|
||||
Column {
|
||||
property alias accountNameInput: accountNameInput
|
||||
width: scroll.availableWidth
|
||||
spacing: Style.current.halfPadding
|
||||
topPadding: 20
|
||||
|
||||
StatusBaseText {
|
||||
id: validationError
|
||||
visible: text !== ""
|
||||
width: parent.width
|
||||
height: 16
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 12
|
||||
color: Style.current.danger
|
||||
wrapMode: TextEdit.Wrap
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: accountNameInput
|
||||
placeholderText: qsTr("Enter an account name...")
|
||||
label: qsTr("Account name")
|
||||
input.isIconSelectable: true
|
||||
input.asset.color: colorSelectionGrid.selectedColor ? colorSelectionGrid.selectedColor : Theme.palette.directColor1
|
||||
input.leftPadding: Style.current.padding
|
||||
onIconClicked: {
|
||||
root.emojiPopup.open()
|
||||
root.emojiPopup.emojiSize = StatusQUtils.Emoji.size.verySmall
|
||||
root.emojiPopup.x = root.x + accountNameInput.x + Style.current.padding
|
||||
root.emojiPopup.y = root.y + contentItem.y + accountNameInput.y + accountNameInput.height + Style.current.halfPadding
|
||||
}
|
||||
validators: [
|
||||
StatusMinLengthValidator {
|
||||
errorMessage: qsTr("You need to enter an account name")
|
||||
minLength: 1
|
||||
}
|
||||
]
|
||||
onKeyPressed: {
|
||||
if(event.key === Qt.Key_Tab) {
|
||||
if (nextButton.enabled) {
|
||||
nextButton.forceActiveFocus(Qt.MouseFocusReason)
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusColorSelectorGrid {
|
||||
id: colorSelectionGrid
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
enabled: accountNameInput.valid
|
||||
titleText: qsTr("color").toUpperCase()
|
||||
}
|
||||
|
||||
StatusExpandableItem {
|
||||
id: advancedSelection
|
||||
|
||||
property bool isValid: true
|
||||
|
||||
function validate() {
|
||||
return !!expandableItem && expandableItem.validate()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
return !!expandableItem && expandableItem.reset()
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
|
||||
primaryText: qsTr("Advanced")
|
||||
type: StatusExpandableItem.Type.Tertiary
|
||||
expandable: true
|
||||
expandableComponent: AdvancedAddAccountView {
|
||||
width: parent.width
|
||||
onCalculateDerivedPath: {
|
||||
d.selectedAccountDerivedFromAddress = derivedFromAddress
|
||||
d.selectedPath = path
|
||||
if (d.selectedKeyUidMigratedToKeycard) {
|
||||
d.password = ""
|
||||
validationError.text = ""
|
||||
RootStore.loggedInUserAuthenticated = false
|
||||
}
|
||||
else{
|
||||
d.getDerivedAddressList()
|
||||
}
|
||||
}
|
||||
onEnterPressed: {
|
||||
if (nextButton.enabled) {
|
||||
nextButton.clicked(null)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
d.selectedAccountType = Qt.binding(() => addAccountType)
|
||||
d.selectedAccountDerivedFromAddress = Qt.binding(() => derivedFromAddress)
|
||||
d.selectedKeyUid = Qt.binding(() => selectedKeyUid)
|
||||
d.selectedKeyUidMigratedToKeycard = Qt.binding(() => selectedKeyUidMigratedToKeycard)
|
||||
d.selectedPath = Qt.binding(() => path)
|
||||
d.selectedAddress = Qt.binding(() => selectedAddress)
|
||||
d.selectedAddressAvailable = Qt.binding(() => selectedAddressAvailable)
|
||||
d.useFullyCustomPath = Qt.binding(() => useFullyCustomPath)
|
||||
advancedSelection.isValid = Qt.binding(() => isValid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rightButtons: [
|
||||
StatusButton {
|
||||
id: nextButton
|
||||
|
||||
text: {
|
||||
if (loading) {
|
||||
return qsTr("Loading...")
|
||||
}
|
||||
return qsTr("Add account")
|
||||
}
|
||||
|
||||
enabled: {
|
||||
if (!accountNameInput.valid ||
|
||||
loading ||
|
||||
((root.authenticationNeeded && !d.useFullyCustomPath) && !d.selectedAddressAvailable)) {
|
||||
return false
|
||||
}
|
||||
return advancedSelection.isValid
|
||||
}
|
||||
|
||||
icon.name: d.authenticationNeeded? d.addAccountIcon : ""
|
||||
highlighted: focus
|
||||
|
||||
onClicked : d.nextButtonClicked()
|
||||
}
|
||||
]
|
||||
}
|
|
@ -18,7 +18,6 @@ QtObject {
|
|||
property string backButtonName: ""
|
||||
property var currentAccount: Constants.isCppApp ? walletSectionAccounts.currentAccount: walletSectionCurrent
|
||||
property var accounts: walletSectionAccounts.model
|
||||
property var generatedAccounts: walletSectionAccounts.generated
|
||||
property var appSettings: localAppSettings
|
||||
property var accountSensitiveSettings: localAccountSensitiveSettings
|
||||
property bool hideSignPhraseModal: accountSensitiveSettings.hideSignPhraseModal
|
||||
|
@ -55,10 +54,6 @@ QtObject {
|
|||
return d.chainColors[chainShortName]
|
||||
}
|
||||
|
||||
// Used for new wallet account generation
|
||||
property var generatedAccountsViewModel: walletSectionAccounts.generatedAccounts
|
||||
property var derivedAddressesList: walletSectionAccounts.derivedAddresses
|
||||
|
||||
property var layer1Networks: networksModule.layer1
|
||||
property var layer2Networks: networksModule.layer2
|
||||
property var testNetworks: networksModule.test
|
||||
|
@ -114,9 +109,6 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
property bool derivedAddressesLoading: walletSectionAccounts.derivedAddressesLoading
|
||||
property string derivedAddressesError: walletSectionAccounts.derivedAddressesError
|
||||
|
||||
function setHideSignPhraseModal(value) {
|
||||
localAccountSensitiveSettings.hideSignPhraseModal = value;
|
||||
}
|
||||
|
@ -143,26 +135,6 @@ QtObject {
|
|||
walletSection.switchAccountByAddress(address)
|
||||
}
|
||||
|
||||
function generateNewAccount(password, accountName, color, emoji, path, derivedFrom) {
|
||||
return walletSectionAccounts.generateNewAccount(password, accountName, color, emoji, path, derivedFrom)
|
||||
}
|
||||
|
||||
function addNewWalletAccountGeneratedFromKeycard(accountType, accountName, color, emoji) {
|
||||
return walletSectionAccounts.addNewWalletAccountGeneratedFromKeycard(accountType, accountName, color, emoji)
|
||||
}
|
||||
|
||||
function addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji) {
|
||||
return walletSectionAccounts.addAccountsFromPrivateKey(privateKey, password, accountName, color, emoji)
|
||||
}
|
||||
|
||||
function addAccountsFromSeed(seedPhrase, password, accountName, color, emoji, path) {
|
||||
return walletSectionAccounts.addAccountsFromSeed(seedPhrase, password, accountName, color, emoji, path)
|
||||
}
|
||||
|
||||
function addWatchOnlyAccount(address, accountName,color, emoji) {
|
||||
return walletSectionAccounts.addWatchOnlyAccount(address, accountName, color, emoji)
|
||||
}
|
||||
|
||||
function deleteAccount(address) {
|
||||
return walletSectionAccounts.deleteAccount(address)
|
||||
}
|
||||
|
@ -215,72 +187,4 @@ QtObject {
|
|||
function copyToClipboard(text) {
|
||||
globalUtils.copyToClipboard(text)
|
||||
}
|
||||
|
||||
function getDerivedAddress(password, derivedFrom, path, hashPassword) {
|
||||
walletSectionAccounts.getDerivedAddress(password, derivedFrom, path, hashPassword)
|
||||
}
|
||||
|
||||
function getDerivedAddressList(password, derivedFrom, path, pageSize, pageNumber, hashPassword) {
|
||||
walletSectionAccounts.getDerivedAddressList(password, derivedFrom, path, pageSize, pageNumber, hashPassword)
|
||||
}
|
||||
|
||||
function getDerivedAddressData(index) {
|
||||
return walletSectionAccounts.getDerivedAddressAtIndex(index)
|
||||
}
|
||||
|
||||
function getDerivedAddressPathData(index) {
|
||||
return walletSectionAccounts.getDerivedAddressPathAtIndex(index)
|
||||
}
|
||||
|
||||
function getDerivedAddressHasActivityData(index) {
|
||||
return walletSectionAccounts.getDerivedAddressHasActivityAtIndex(index)
|
||||
}
|
||||
|
||||
function getDerivedAddressAlreadyCreatedData(index) {
|
||||
return walletSectionAccounts.getDerivedAddressAlreadyCreatedAtIndex(index)
|
||||
}
|
||||
|
||||
function getDerivedAddressListForMnemonic(mnemonic, path, pageSize , pageNumber) {
|
||||
walletSectionAccounts.getDerivedAddressListForMnemonic(mnemonic, path, pageSize , pageNumber)
|
||||
}
|
||||
|
||||
function getDerivedAddressForPrivateKey(privateKey) {
|
||||
walletSectionAccounts.getDerivedAddressForPrivateKey(privateKey)
|
||||
}
|
||||
|
||||
function resetDerivedAddressModel() {
|
||||
walletSectionAccounts.resetDerivedAddressModel()
|
||||
}
|
||||
|
||||
function validSeedPhrase(mnemonic) {
|
||||
return walletSectionAccounts.validSeedPhrase(mnemonic)
|
||||
}
|
||||
|
||||
function getNextSelectableDerivedAddressIndex() {
|
||||
return walletSectionAccounts.getNextSelectableDerivedAddressIndex()
|
||||
}
|
||||
|
||||
function authenticateUser() {
|
||||
walletSectionAccounts.authenticateUser()
|
||||
}
|
||||
|
||||
function loggedInUserUsesBiometricLogin() {
|
||||
return userProfile.usingBiometricLogin
|
||||
}
|
||||
|
||||
function loggedInUserIsKeycardUser() {
|
||||
return userProfile.isKeycardUser
|
||||
}
|
||||
|
||||
function createSharedKeycardModule() {
|
||||
walletSectionAccounts.createSharedKeycardModule()
|
||||
}
|
||||
|
||||
function destroySharedKeycarModule() {
|
||||
walletSectionAccounts.destroySharedKeycarModule()
|
||||
}
|
||||
|
||||
function authenticateUserAndDeriveAddressOnKeycardForPath(keyUid, derivationPath, searchForFirstAvailableAddress) {
|
||||
walletSectionAccounts.authenticateUserAndDeriveAddressOnKeycardForPath(keyUid, derivationPath, searchForFirstAvailableAddress)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
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.Controls 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
|
||||
import utils 1.0
|
||||
import "../stores"
|
||||
import "../panels"
|
||||
|
||||
ColumnLayout {
|
||||
id: advancedSection
|
||||
|
||||
readonly property alias useFullyCustomPath: fullyCustomPathCheckBox.checked
|
||||
property int addAccountType: Constants.AddAccountType.GenerateNew
|
||||
property string selectedKeyUid: RootStore.defaultSelectedKeyUid
|
||||
property bool selectedKeyUidMigratedToKeycard: RootStore.defaultSelectedKeyUidMigratedToKeycard
|
||||
property string selectedAddress: ""
|
||||
property bool selectedAddressAvailable: false
|
||||
property string enterPasswordIcon: ""
|
||||
property string derivedFromAddress: ""
|
||||
property string mnemonicText: ""
|
||||
property alias privateKey: importPrivateKeyPanel.text
|
||||
property string path: ""
|
||||
property string pathSubFix: ""
|
||||
property string completePath: path + "/" + pathSubFix
|
||||
property alias watchAddress: addressInput.text
|
||||
property bool isValid: addAccountType === Constants.AddAccountType.ImportSeedPhrase ? importSeedPhrasePanel.isValid :
|
||||
addAccountType === Constants.AddAccountType.ImportPrivateKey ? (importPrivateKeyPanel.text !== "" && importPrivateKeyPanel.valid) :
|
||||
addAccountType === Constants.AddAccountType.WatchOnly ? (addressInput.text !== "" && addressInput.valid) : true
|
||||
|
||||
signal calculateDerivedPath()
|
||||
signal enterPressed()
|
||||
|
||||
objectName: "advancedAddAccountViewRoot"
|
||||
|
||||
function reset() {
|
||||
//reset selectGeneratedAccount
|
||||
selectGeneratedAccount.resetMe()
|
||||
|
||||
// reset privateKey
|
||||
importPrivateKeyPanel.resetMe()
|
||||
|
||||
// reset importSeedPhrasePanel
|
||||
importSeedPhrasePanel.reset()
|
||||
|
||||
// reset derivation path
|
||||
derivationPathsPanel.reset()
|
||||
|
||||
// reset derviedAccountsList
|
||||
derivedAddressesPanel.reset()
|
||||
|
||||
// reset watch only address input
|
||||
addressInput.text = ""
|
||||
addressInput.reset()
|
||||
}
|
||||
|
||||
function validate() {
|
||||
if(addAccountType === Constants.AddAccountType.ImportSeedPhrase) {
|
||||
// validate mnemonic
|
||||
return importSeedPhrasePanel.validate()
|
||||
}
|
||||
else if(addAccountType === Constants.AddAccountType.ImportPrivateKey) {
|
||||
// validate privateKey
|
||||
return importPrivateKeyPanel.validateMe()
|
||||
}
|
||||
else if(addAccountType === Constants.AddAccountType.WatchOnly) {
|
||||
return addressInput.valid
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
onPathChanged: {
|
||||
if(addAccountType === Constants.AddAccountType.ImportSeedPhrase) {
|
||||
if(importSeedPhrasePanel.isValid) {
|
||||
advancedSection.calculateDerivedPath()
|
||||
}
|
||||
}
|
||||
else {
|
||||
advancedSection.calculateDerivedPath()
|
||||
}
|
||||
}
|
||||
|
||||
onDerivedFromAddressChanged: {
|
||||
// reset derviedAccountsList
|
||||
derivedAddressesPanel.reset()
|
||||
|
||||
if(addAccountType === Constants.AddAccountType.ImportSeedPhrase) {
|
||||
if(importSeedPhrasePanel.isValid) {
|
||||
advancedSection.calculateDerivedPath()
|
||||
}
|
||||
}
|
||||
else {
|
||||
advancedSection.calculateDerivedPath()
|
||||
}
|
||||
}
|
||||
|
||||
spacing: Style.current.padding
|
||||
|
||||
SelectGeneratedAccount {
|
||||
id: selectGeneratedAccount
|
||||
objectName: "selectGeneratedAccount"
|
||||
Component.onCompleted: {
|
||||
advancedSection.addAccountType = Qt.binding(function() {return addAccountType})
|
||||
advancedSection.derivedFromAddress = Qt.binding(function() {return derivedFromAddress})
|
||||
advancedSection.selectedKeyUid = Qt.binding(function() {return selectedKeyUid})
|
||||
advancedSection.selectedKeyUidMigratedToKeycard = Qt.binding(function() {return selectedKeyUidMigratedToKeycard})
|
||||
}
|
||||
}
|
||||
|
||||
ImportPrivateKeyPanel {
|
||||
id: importPrivateKeyPanel
|
||||
visible: advancedSection.addAccountType === Constants.AddAccountType.ImportPrivateKey && advancedSection.visible
|
||||
}
|
||||
|
||||
ImportSeedPhrasePanel {
|
||||
id: importSeedPhrasePanel
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.preferredHeight: visible ? importSeedPhrasePanel.preferredHeight: 0
|
||||
Layout.leftMargin: (Style.current.halfPadding/4)
|
||||
visible: advancedSection.addAccountType === Constants.AddAccountType.ImportSeedPhrase && advancedSection.visible
|
||||
onMnemonicStringChanged: {
|
||||
advancedSection.mnemonicText = mnemonicString
|
||||
if(isValid) {
|
||||
calculateDerivedPath()
|
||||
}
|
||||
}
|
||||
onEnterPressed: advancedSection.enterPressed()
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: addressInput
|
||||
input.placeholder.objectName: "advancedAddAccountViewAddressInputPlaceholder"
|
||||
input.objectName: "advancedAddAccountViewAddressInput"
|
||||
visible: advancedSection.addAccountType === Constants.AddAccountType.WatchOnly && advancedSection.visible
|
||||
placeholderText: qsTr("Enter address...")
|
||||
label: qsTr("Account address")
|
||||
validators: [
|
||||
StatusAddressValidator {
|
||||
errorMessage: qsTr("This needs to be a valid address (starting with 0x)")
|
||||
},
|
||||
StatusMinLengthValidator {
|
||||
errorMessage: qsTr("You need to enter an address")
|
||||
minLength: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.preferredWidth: parent.width
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: advancedSection.width
|
||||
Layout.rightMargin: 2
|
||||
spacing: Style.current.bigPadding
|
||||
visible: advancedSection.addAccountType !== Constants.AddAccountType.ImportPrivateKey &&
|
||||
advancedSection.addAccountType !== Constants.AddAccountType.WatchOnly
|
||||
|
||||
readonly property int itemWidth: (advancedSection.width - Style.current.bigPadding) * 0.5
|
||||
|
||||
DerivationPathsPanel {
|
||||
id: derivationPathsPanel
|
||||
useFullyCustomPath: fullyCustomPathCheckBox.checked
|
||||
Layout.preferredWidth: parent.itemWidth
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Component.onCompleted: advancedSection.path = Qt.binding(function() { return derivationPathsPanel.path})
|
||||
}
|
||||
DerivedAddressesPanel {
|
||||
id: derivedAddressesPanel
|
||||
Layout.preferredWidth: parent.itemWidth
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
selectedAccountType: advancedSection.addAccountType
|
||||
selectedKeyUid: advancedSection.selectedKeyUid
|
||||
selectedKeyUidMigratedToKeycard: advancedSection.selectedKeyUidMigratedToKeycard
|
||||
selectedPath: advancedSection.path
|
||||
|
||||
Component.onCompleted: {
|
||||
advancedSection.selectedAddress = Qt.binding(function() { return derivedAddressesPanel.selectedAddress})
|
||||
advancedSection.selectedAddressAvailable = Qt.binding(function() { return derivedAddressesPanel.selectedAddressAvailable})
|
||||
advancedSection.pathSubFix = Qt.binding(function() { return derivedAddressesPanel.pathSubFix})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusCheckBox {
|
||||
id: fullyCustomPathCheckBox
|
||||
objectName: "fullyCustomPathCheckBox"
|
||||
visible: advancedSection.addAccountType === Constants.AddAccountType.GenerateNew
|
||||
Layout.preferredWidth: advancedSection.width
|
||||
text: qsTr("I acknowledge that by adding an account out of the default Status derivation path I will not be able to migrate a keypair to a Keycard")
|
||||
onToggled: {
|
||||
advancedSection.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,41 +32,8 @@ Rectangle {
|
|||
|
||||
property var emojiPopup: null
|
||||
|
||||
function onAfterAddAccount () {
|
||||
root.changeSelectedAccount(RootStore.accounts.rowCount() - 1)
|
||||
}
|
||||
|
||||
color: Style.current.secondaryMenuBackground
|
||||
|
||||
Loader {
|
||||
id: addAccountModal
|
||||
active: false
|
||||
asynchronous: true
|
||||
|
||||
function open() {
|
||||
if (!active) {
|
||||
RootStore.createSharedKeycardModule()
|
||||
active = true
|
||||
}
|
||||
item.open()
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (item) {
|
||||
RootStore.destroySharedKeycarModule()
|
||||
item.close()
|
||||
}
|
||||
active = false
|
||||
}
|
||||
|
||||
sourceComponent: AddAccountModal {
|
||||
anchors.centerIn: parent
|
||||
onAfterAddAccount: root.onAfterAddAccount()
|
||||
emojiPopup: root.emojiPopup
|
||||
onClosed: addAccountModal.close()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: Style.current.padding
|
||||
|
@ -96,7 +63,6 @@ Rectangle {
|
|||
height: parent.height * 2
|
||||
color: hovered || highlighted ? Theme.palette.primaryColor3
|
||||
: "transparent"
|
||||
onClicked: addAccountModal.open()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -813,12 +813,6 @@ QtObject {
|
|||
NoError
|
||||
}
|
||||
|
||||
enum AddAccountType {
|
||||
GenerateNew,
|
||||
ImportSeedPhrase,
|
||||
ImportPrivateKey,
|
||||
WatchOnly
|
||||
}
|
||||
|
||||
readonly property QtObject walletSection: QtObject {
|
||||
readonly property string cancelledMessage: "cancelled"
|
||||
|
|
Loading…
Reference in New Issue