chore(@desktop/wallet): old add account modal and corresponding methods from `walletSectionAccounts` module removed

This commit is contained in:
Sale Djenic 2023-03-22 16:15:38 +01:00 committed by saledjenic
parent 388fab4a4a
commit 7b16a93cc0
15 changed files with 17 additions and 2021 deletions

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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: ""
}
]
}
}

View File

@ -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
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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]
}
}
}
}

View File

@ -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
}
}
}
}

View File

@ -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()
}
]
}

View File

@ -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)
}
}

View File

@ -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()
}
}
}
}

View File

@ -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()
}
}

View File

@ -813,12 +813,6 @@ QtObject {
NoError
}
enum AddAccountType {
GenerateNew,
ImportSeedPhrase,
ImportPrivateKey,
WatchOnly
}
readonly property QtObject walletSection: QtObject {
readonly property string cancelledMessage: "cancelled"