Switch to new login/create/restore status-go endpoints (#14139)
* fix: new login endpoints - iteration 1 * feat: use CreateAccountAndLogin endpoint * fix keycard import * better comment * wip: screens reordering * cleanup * force minimum of 30 seconds fetching backup * keycard flows fixes * typo fix * remove ganache argument * fix: wallet issues * update status-go
This commit is contained in:
parent
ec9bf9fc7b
commit
59cb770ddb
|
@ -1546,10 +1546,11 @@ proc runStartUsingKeycardForProfilePopup[T](self: Module[T]) =
|
|||
################################################################################
|
||||
|
||||
method checkAndPerformProfileMigrationIfNeeded*[T](self: Module[T]) =
|
||||
let keyUid = singletonInstance.userProfile.getKeyUid()
|
||||
let migrationNeeded = self.settingsService.getProfileMigrationNeeded()
|
||||
let profileKeypair = self.walletAccountService.getKeypairByKeyUid(singletonInstance.userProfile.getKeyUid())
|
||||
let profileKeypair = self.walletAccountService.getKeypairByKeyUid(keyUid)
|
||||
if profileKeypair.isNil:
|
||||
info "quit the app because of unresolved profile keypair"
|
||||
info "quit the app because of unresolved profile keypair", keyUid
|
||||
quit() # quit the app
|
||||
if not migrationNeeded:
|
||||
if not self.keycardSharedModule.isNil:
|
||||
|
|
|
@ -370,7 +370,7 @@ proc getSeedPhraseLength*(self: Controller): int =
|
|||
proc validSeedPhrase*(self: Controller, seedPhrase: string): bool =
|
||||
if not serviceApplicable(self.accountsService):
|
||||
return
|
||||
let err = self.accountsService.validateMnemonic(seedPhrase)
|
||||
let (_, err) = self.accountsService.validateMnemonic(seedPhrase)
|
||||
return err.len == 0
|
||||
|
||||
proc getKeyUidForSeedPhrase*(self: Controller, seedPhrase: string): string =
|
||||
|
|
|
@ -13,19 +13,17 @@ import app_service/service/profile/service as profile_service
|
|||
import app_service/service/keycard/service as keycard_service
|
||||
import app_service/service/devices/service as devices_service
|
||||
import app_service/common/[account_constants, utils]
|
||||
|
||||
import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module
|
||||
|
||||
import app_service/service/accounts/dto/create_account_request
|
||||
|
||||
logScope:
|
||||
topics = "startup-controller"
|
||||
|
||||
type ProfileImageDetails = object
|
||||
url*: string
|
||||
croppedImage*: string
|
||||
x1*: int
|
||||
y1*: int
|
||||
x2*: int
|
||||
y2*: int
|
||||
croppedImage*: string # TODO: Remove after https://github.com/status-im/status-go/issues/4977
|
||||
cropRectangle*: ImageCropRectangle
|
||||
|
||||
type
|
||||
Controller* = ref object of RootObj
|
||||
|
@ -57,6 +55,7 @@ type
|
|||
tmpRecoverKeycardUsingSeedPhraseWhileLoggingIn: bool
|
||||
tmpConnectionString: string
|
||||
localPairingStatus: LocalPairingStatus
|
||||
loggedInPofilePublicKey: string
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
|
@ -116,12 +115,6 @@ proc connectToFetchingFromWakuEvents*(self: Controller) =
|
|||
self.delegate.onFetchingFromWakuMessageReceived(receivedData.clock, k, v.totalNumber, v.dataNumber)
|
||||
self.connectionIds.add(handlerId)
|
||||
|
||||
proc connectToTimeoutEventAndStratTimer*(self: Controller, timeoutInMilliseconds: int) =
|
||||
var handlerId = self.events.onWithUUID(SIGNAL_GENERAL_TIMEOUT) do(e: Args):
|
||||
self.delegate.startAppAfterDelay()
|
||||
self.connectionIds.add(handlerId)
|
||||
self.generalService.runTimer(timeoutInMilliseconds)
|
||||
|
||||
proc disconnect*(self: Controller) =
|
||||
self.disconnectKeychain()
|
||||
for id in self.connectionIds:
|
||||
|
@ -134,7 +127,7 @@ proc delete*(self: Controller) =
|
|||
proc init*(self: Controller) =
|
||||
var handlerId = self.events.onWithUUID(SignalType.NodeLogin.event) do(e:Args):
|
||||
let signal = NodeSignal(e)
|
||||
self.delegate.onNodeLogin(signal.error)
|
||||
self.delegate.onNodeLogin(signal.error, signal.account, signal.settings)
|
||||
self.connectionIds.add(handlerId)
|
||||
|
||||
handlerId = self.events.onWithUUID(SignalType.NodeStopped.event) do(e:Args):
|
||||
|
@ -196,12 +189,14 @@ proc init*(self: Controller) =
|
|||
proc shouldStartWithOnboardingScreen*(self: Controller): bool =
|
||||
return self.accountsService.openedAccounts().len == 0
|
||||
|
||||
# This is used when fetching backup failed and we create a new displayName and profileImage.
|
||||
# At this point the account is already created in the database. All that's left is to set the displayName and profileImage.
|
||||
proc storeProfileDataAndProceedWithAppLoading*(self: Controller) =
|
||||
self.delegate.removeAllKeycardUidPairsForCheckingForAChangeAfterLogin() # reason for this is in the table in AppController.nim file
|
||||
discard self.profileService.setDisplayName(self.tmpDisplayName)
|
||||
let images = self.storeIdentityImage()
|
||||
self.accountsService.updateLoggedInAccount(self.tmpDisplayName, images)
|
||||
self.delegate.finishAppLoading()
|
||||
self.delegate.notifyLoggedInAccountChanged()
|
||||
|
||||
proc checkFetchingStatusAndProceed*(self: Controller) =
|
||||
self.delegate.checkFetchingStatusAndProceed()
|
||||
|
@ -221,11 +216,15 @@ proc clearImage*(self: Controller) =
|
|||
proc generateImage*(self: Controller, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string =
|
||||
let formatedImg = singletonInstance.utils.formatImagePath(imageUrl)
|
||||
let images = self.generalService.generateImages(formatedImg, aX, aY, bX, bY)
|
||||
if(images.len == 0):
|
||||
if images.len == 0:
|
||||
return
|
||||
for img in images:
|
||||
if(img.imgType == "large"):
|
||||
self.tmpProfileImageDetails = ProfileImageDetails(url: imageUrl, croppedImage: img.uri, x1: aX, y1: aY, x2: bX, y2: bY)
|
||||
if img.imgType == "large":
|
||||
self.tmpProfileImageDetails = ProfileImageDetails(
|
||||
url: formatedImg,
|
||||
croppedImage: img.uri,
|
||||
cropRectangle: ImageCropRectangle(ax: aX, ay: aY, bx: bX, by: bY)
|
||||
)
|
||||
return img.uri
|
||||
|
||||
proc fetchWakuMessages*(self: Controller) =
|
||||
|
@ -349,22 +348,46 @@ proc tryToObtainDataFromKeychain*(self: Controller) =
|
|||
let selectedAccount = self.getSelectedLoginAccount()
|
||||
self.keychainService.tryToObtainData(selectedAccount.keyUid)
|
||||
|
||||
# TODO: Remove when implemented https://github.com/status-im/status-go/issues/4977
|
||||
proc storeIdentityImage*(self: Controller): seq[Image] =
|
||||
if self.tmpProfileImageDetails.url.len == 0:
|
||||
return
|
||||
let account = self.accountsService.getLoggedInAccount()
|
||||
let image = singletonInstance.utils.formatImagePath(self.tmpProfileImageDetails.url)
|
||||
result = self.profileService.storeIdentityImage(account.keyUid, image, self.tmpProfileImageDetails.x1,
|
||||
self.tmpProfileImageDetails.y1, self.tmpProfileImageDetails.x2, self.tmpProfileImageDetails.y2)
|
||||
result = self.profileService.storeIdentityImage(
|
||||
account.keyUid,
|
||||
image,
|
||||
self.tmpProfileImageDetails.cropRectangle.aX,
|
||||
self.tmpProfileImageDetails.cropRectangle.aY,
|
||||
self.tmpProfileImageDetails.cropRectangle.bX,
|
||||
self.tmpProfileImageDetails.cropRectangle.bY,
|
||||
)
|
||||
self.tmpProfileImageDetails = ProfileImageDetails()
|
||||
|
||||
# validMnemonic only checks if mnemonic is valid
|
||||
# This is used from UI.
|
||||
proc validMnemonic*(self: Controller, mnemonic: string): bool =
|
||||
let err = self.accountsService.validateMnemonic(mnemonic)
|
||||
let (keyUID, err) = self.accountsService.validateMnemonic(mnemonic)
|
||||
if err.len == 0:
|
||||
self.setSeedPhrase(mnemonic)
|
||||
return true
|
||||
return false
|
||||
|
||||
# validateMnemonicForImport checks if mnemonic is valid and not yet saved in local database
|
||||
proc validateMnemonicForImport*(self: Controller, mnemonic: string): bool =
|
||||
let (keyUID, err) = self.accountsService.validateMnemonic(mnemonic)
|
||||
if err.len != 0:
|
||||
self.delegate.emitStartupError(err, StartupErrorType.ImportAccError)
|
||||
return false
|
||||
|
||||
if self.accountsService.openedAccountsContainsKeyUid(keyUID):
|
||||
self.delegate.emitStartupError(ACCOUNT_ALREADY_EXISTS_ERROR, StartupErrorType.ImportAccError)
|
||||
return false
|
||||
|
||||
self.setSeedPhrase(mnemonic)
|
||||
return true
|
||||
|
||||
# TODO: Remove after https://github.com/status-im/status-go/issues/4977
|
||||
proc importMnemonic*(self: Controller): bool =
|
||||
let error = self.accountsService.importMnemonic(self.tmpSeedPhrase)
|
||||
if(error.len == 0):
|
||||
|
@ -380,25 +403,37 @@ proc setupKeychain(self: Controller, store: bool) =
|
|||
else:
|
||||
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER)
|
||||
|
||||
proc setupAccount(self: Controller, accountId: string, removeMnemonic: bool, storeToKeychain: bool, recoverAccount: bool = false) =
|
||||
self.delegate.moveToLoadingAppState()
|
||||
let error = self.accountsService.setupAccount(accountId, self.tmpPassword, self.tmpDisplayName, removeMnemonic, recoverAccount)
|
||||
proc processCreateAccountResult*(self: Controller, error: string, storeToKeychain: bool) =
|
||||
if error != "":
|
||||
self.delegate.emitStartupError(error, StartupErrorType.SetupAccError)
|
||||
else:
|
||||
self.setupKeychain(storeToKeychain)
|
||||
|
||||
proc storeGeneratedAccountAndLogin*(self: Controller, storeToKeychain: bool) =
|
||||
let accounts = self.getGeneratedAccounts()
|
||||
if accounts.len == 0:
|
||||
error "list of generated accounts is empty"
|
||||
return
|
||||
let accountId = accounts[0].id
|
||||
self.setupAccount(accountId, removeMnemonic=false, storeToKeychain)
|
||||
proc createAccountAndLogin*(self: Controller, storeToKeychain: bool) =
|
||||
self.delegate.moveToLoadingAppState()
|
||||
let error = self.accountsService.createAccountAndLogin(
|
||||
self.tmpPassword,
|
||||
self.tmpDisplayName,
|
||||
self.tmpProfileImageDetails.url,
|
||||
self.tmpProfileImageDetails.cropRectangle
|
||||
)
|
||||
self.processCreateAccountResult(error, storeToKeychain)
|
||||
|
||||
proc storeImportedAccountAndLogin*(self: Controller, storeToKeychain: bool, recoverAccount: bool = false) =
|
||||
let accountId = self.getImportedAccount().id
|
||||
self.setupAccount(accountId, removeMnemonic=true, storeToKeychain, recoverAccount)
|
||||
proc importAccountAndLogin*(self: Controller, storeToKeychain: bool, recoverAccount: bool = false) =
|
||||
if recoverAccount:
|
||||
self.delegate.prepareAndInitFetchingData()
|
||||
self.connectToFetchingFromWakuEvents()
|
||||
else:
|
||||
self.delegate.moveToLoadingAppState()
|
||||
let error = self.accountsService.importAccountAndLogin(
|
||||
self.tmpSeedPhrase,
|
||||
self.tmpPassword,
|
||||
recoverAccount,
|
||||
self.tmpDisplayName,
|
||||
self.tmpProfileImageDetails.url,
|
||||
self.tmpProfileImageDetails.cropRectangle,
|
||||
)
|
||||
self.processCreateAccountResult(error, storeToKeychain)
|
||||
|
||||
proc storeKeycardAccountAndLogin*(self: Controller, storeToKeychain: bool, newKeycard: bool) =
|
||||
if self.importMnemonic():
|
||||
|
@ -419,6 +454,9 @@ proc setupKeycardAccount*(self: Controller, storeToKeychain: bool, recoverAccoun
|
|||
self.accountsService.openedAccountsContainsKeyUid(self.tmpKeycardEvent.keyUid):
|
||||
self.delegate.emitStartupError(ACCOUNT_ALREADY_EXISTS_ERROR, StartupErrorType.ImportAccError)
|
||||
return
|
||||
if recoverAccount:
|
||||
self.delegate.prepareAndInitFetchingData()
|
||||
self.connectToFetchingFromWakuEvents()
|
||||
if self.tmpSeedPhrase.len > 0:
|
||||
# if `tmpSeedPhrase` is not empty means user has recovered keycard via seed phrase
|
||||
let accFromSeedPhrase = self.accountsService.createAccountFromMnemonic(self.tmpSeedPhrase, includeEncryption = true,
|
||||
|
@ -441,7 +479,7 @@ proc setupKeycardAccount*(self: Controller, storeToKeychain: bool, recoverAccoun
|
|||
self.tmpKeycardEvent.encryptionKey.privateKey = accFromSeedPhrase.derivedAccounts.encryption.privateKey
|
||||
self.tmpKeycardEvent.encryptionKey.publicKey = accFromSeedPhrase.derivedAccounts.encryption.publicKey
|
||||
self.tmpKeycardEvent.encryptionKey.address = accFromSeedPhrase.derivedAccounts.encryption.address
|
||||
self.delegate.moveToLoadingAppState()
|
||||
|
||||
self.syncKeycardBasedOnAppWalletStateAfterLogin()
|
||||
self.accountsService.setupAccountKeycard(self.tmpKeycardEvent, self.tmpDisplayName, useImportedAcc = false, recoverAccount)
|
||||
self.setupKeychain(storeToKeychain)
|
||||
|
@ -611,3 +649,30 @@ proc validateLocalPairingConnectionString*(self: Controller, connectionString: s
|
|||
|
||||
proc inputConnectionStringForBootstrapping*(self: Controller, connectionString: string): string =
|
||||
return self.devicesService.inputConnectionStringForBootstrapping(connectionString)
|
||||
|
||||
proc setLoggedInAccount*(self: Controller, account: AccountDto) =
|
||||
self.accountsService.setLoggedInAccount(account)
|
||||
|
||||
proc setLoggedInProfile*(self: Controller, publicKey: string) =
|
||||
self.loggedInPofilePublicKey = publicKey
|
||||
|
||||
proc getLoggedInAccountPublicKey*(self: Controller): string =
|
||||
return self.loggedInPofilePublicKey
|
||||
|
||||
proc getLoggedInAccountDisplayName*(self: Controller): string =
|
||||
return self.accountsService.getLoggedInAccount().name
|
||||
|
||||
proc getLoggedInAccountImage*(self: Controller): string =
|
||||
let images = self.accountsService.getLoggedInAccount().images
|
||||
for img in images:
|
||||
if img.imgType == "large":
|
||||
return img.uri
|
||||
return ""
|
||||
|
||||
# NOTE: This could be a constant now, but in future we should check if the user
|
||||
# has already enabled notifications and return corresponding result from this function.
|
||||
proc notificationsNeedsEnable*(self: Controller): bool =
|
||||
return main_constants.IS_MACOS
|
||||
|
||||
proc proceedToApp*(self: Controller) =
|
||||
self.delegate.finishAppLoading()
|
||||
|
|
|
@ -10,16 +10,24 @@ proc newBiometricsState*(flowType: FlowType, backState: State): BiometricsState
|
|||
proc delete*(self: BiometricsState) =
|
||||
self.State.delete
|
||||
|
||||
method executePrimaryCommand*(self: BiometricsState, controller: Controller) =
|
||||
let storeToKeychain = true # true, cause we have support for keychain for mac os
|
||||
method getNextPrimaryState*(self: BiometricsState, controller: Controller): State =
|
||||
if self.flowType == FlowType.FirstRunOldUserImportSeedPhrase or
|
||||
self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||
return createState(StateType.ProfileFetching, self.flowType, self)
|
||||
return nil
|
||||
|
||||
method getNextSecondaryState*(self: BiometricsState, controller: Controller): State =
|
||||
return self.getNextPrimaryState(controller)
|
||||
|
||||
proc command(self: BiometricsState, controller: Controller, storeToKeychain: bool) =
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
||||
controller.storeGeneratedAccountAndLogin(storeToKeychain)
|
||||
controller.createAccountAndLogin(storeToKeychain)
|
||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||
controller.storeImportedAccountAndLogin(storeToKeychain)
|
||||
controller.importAccountAndLogin(storeToKeychain)
|
||||
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
||||
## This should not be the correct call for this flow, this is an issue,
|
||||
## but since current implementation is like that and this is not a bug fixing issue, left as it is.
|
||||
controller.storeImportedAccountAndLogin(storeToKeychain, recoverAccount = true)
|
||||
controller.importAccountAndLogin(storeToKeychain, recoverAccount = true)
|
||||
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
|
||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||
|
@ -32,27 +40,11 @@ method executePrimaryCommand*(self: BiometricsState, controller: Controller) =
|
|||
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
|
||||
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)
|
||||
|
||||
method executePrimaryCommand*(self: BiometricsState, controller: Controller) =
|
||||
self.command(controller, true)
|
||||
|
||||
method executeSecondaryCommand*(self: BiometricsState, controller: Controller) =
|
||||
let storeToKeychain = false # false, cause we don't have keychain support for other than mac os
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
||||
controller.storeGeneratedAccountAndLogin(storeToKeychain)
|
||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||
controller.storeImportedAccountAndLogin(storeToKeychain)
|
||||
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
||||
## This should not be the correct call for this flow, this is an issue,
|
||||
## but since current implementation is like that and this is not a bug fixing issue, left as it is.
|
||||
controller.storeImportedAccountAndLogin(storeToKeychain, recoverAccount = true)
|
||||
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
|
||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
|
||||
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||
controller.setupKeycardAccount(storeToKeychain, recoverAccount = true)
|
||||
elif self.flowType == FlowType.LostKeycardReplacement:
|
||||
self.storeToKeychain = storeToKeychain
|
||||
controller.startLoginFlowAutomatically(controller.getPin())
|
||||
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
|
||||
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)
|
||||
self.command(controller, false)
|
||||
|
||||
method resolveKeycardNextState*(self: BiometricsState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||
controller: Controller): State =
|
||||
|
|
|
@ -9,4 +9,4 @@ proc delete*(self: NotificationState) =
|
|||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: NotificationState, controller: Controller): State =
|
||||
return createState(StateType.Welcome, FlowType.General, nil)
|
||||
controller.proceedToApp()
|
||||
|
|
|
@ -9,23 +9,10 @@ proc delete*(self: UserProfileChatKeyState) =
|
|||
self.State.delete
|
||||
|
||||
method executePrimaryCommand*(self: UserProfileChatKeyState, controller: Controller) =
|
||||
if main_constants.IS_MACOS:
|
||||
return
|
||||
let storeToKeychain = false # false, cause we don't have keychain support for other than mac os
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
|
||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
|
||||
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||
controller.setupKeycardAccount(storeToKeychain)
|
||||
if not controller.notificationsNeedsEnable():
|
||||
controller.proceedToApp()
|
||||
|
||||
method getNextPrimaryState*(self: UserProfileChatKeyState, controller: Controller): State =
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeys or
|
||||
self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||
return createState(StateType.UserProfileCreatePassword, self.flowType, self)
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys or
|
||||
self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard or
|
||||
self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||
if not main_constants.IS_MACOS:
|
||||
if controller.notificationsNeedsEnable():
|
||||
return createState(StateType.AllowNotifications, self.flowType, nil)
|
||||
return nil
|
||||
return createState(StateType.Biometrics, self.flowType, self)
|
||||
|
|
|
@ -9,23 +9,21 @@ proc delete*(self: UserProfileConfirmPasswordState) =
|
|||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: UserProfileConfirmPasswordState, controller: Controller): State =
|
||||
if not main_constants.IS_MACOS:
|
||||
if not main_constants.SUPPORTS_FINGERPRINT:
|
||||
return nil
|
||||
return createState(StateType.Biometrics, self.flowType, self)
|
||||
|
||||
method executePrimaryCommand*(self: UserProfileConfirmPasswordState, controller: Controller) =
|
||||
if main_constants.IS_MACOS:
|
||||
if main_constants.SUPPORTS_FINGERPRINT:
|
||||
return
|
||||
let storeToKeychain = false # false, cause we don't have keychain support for other than mac os
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
||||
controller.storeGeneratedAccountAndLogin(storeToKeychain)
|
||||
controller.createAccountAndLogin(storeToKeychain)
|
||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||
controller.storeImportedAccountAndLogin(storeToKeychain)
|
||||
controller.importAccountAndLogin(storeToKeychain)
|
||||
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
|
||||
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
||||
controller.storeImportedAccountAndLogin(storeToKeychain, recoverAccount = true)
|
||||
controller.importAccountAndLogin(storeToKeychain, recoverAccount = true)
|
||||
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
|
||||
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ proc delete*(self: UserProfileCreateState) =
|
|||
self.State.delete
|
||||
|
||||
method executePrimaryCommand*(self: UserProfileCreateState, controller: Controller) =
|
||||
# We're here in case of a backup fetch failure
|
||||
if self.flowType == FlowType.FirstRunOldUserImportSeedPhrase or
|
||||
self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||
controller.storeProfileDataAndProceedWithAppLoading()
|
||||
|
@ -16,9 +17,20 @@ method executePrimaryCommand*(self: UserProfileCreateState, controller: Controll
|
|||
method getNextPrimaryState*(self: UserProfileCreateState, controller: Controller): State =
|
||||
if self.flowType == FlowType.FirstRunOldUserImportSeedPhrase or
|
||||
self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||
return
|
||||
return createState(StateType.UserProfileChatKey, self.flowType, self)
|
||||
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeys or
|
||||
self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||
return createState(StateType.UserProfileCreatePassword, self.flowType, self)
|
||||
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys or
|
||||
self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||
if main_constants.SUPPORTS_FINGERPRINT:
|
||||
return createState(StateType.Biometrics, self.flowType, self)
|
||||
return createState(StateType.UserProfileChatKey, self.flowType, self)
|
||||
|
||||
return nil
|
||||
|
||||
method executeBackCommand*(self: UserProfileCreateState, controller: Controller) =
|
||||
controller.setDisplayName("")
|
||||
controller.clearImage()
|
|
@ -44,10 +44,13 @@ method getNextPrimaryState*(self: UserProfileEnterSeedPhraseState, controller: C
|
|||
method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller: Controller) =
|
||||
if self.flowType == FlowType.FirstRunNewUserImportSeedPhrase or
|
||||
self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
||||
self.successfulImport = controller.importMnemonic()
|
||||
else:
|
||||
self.successfulImport = controller.validateMnemonicForImport(controller.getSeedPhrase())
|
||||
return
|
||||
|
||||
self.successfulImport = controller.validMnemonic(controller.getSeedPhrase())
|
||||
if self.successfulImport:
|
||||
if not self.successfulImport:
|
||||
return
|
||||
|
||||
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
|
||||
let keyUid = controller.getKeyUidForSeedPhrase(controller.getSeedPhrase())
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import ../../../app_service/service/accounts/service as accounts_service
|
|||
import models/login_account_item as login_acc_item
|
||||
from ../../../app_service/service/keycard/service import KeycardEvent, KeyDetails
|
||||
from ../../../app_service/service/devices/dto/local_pairing_status import LocalPairingStatus
|
||||
from ../../../app_service/service/settings/dto/settings import SettingsDto
|
||||
|
||||
const UNIQUE_STARTUP_MODULE_IDENTIFIER* = "SartupModule"
|
||||
|
||||
|
@ -115,7 +116,7 @@ method importAccountSuccess*(self: AccessInterface) {.base.} =
|
|||
method setSelectedLoginAccount*(self: AccessInterface, item: login_acc_item.Item) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onNodeLogin*(self: AccessInterface, error: string) {.base.} =
|
||||
method onNodeLogin*(self: AccessInterface, error: string, account: AccountDto, settings: SettingsDto) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onProfileConverted*(self: AccessInterface, success: bool) {.base.} =
|
||||
|
@ -171,15 +172,15 @@ method onFetchingFromWakuMessageReceived*(self: AccessInterface, backedUpMsgCloc
|
|||
totalMessages: int, loadedMessages: int) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method prepareAndInitFetchingData*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method finishAppLoading*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method checkFetchingStatusAndProceed*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method startAppAfterDelay*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getConnectionString*(self: AccessInterface): string {.base} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
|
@ -215,6 +216,21 @@ method insertMockedKeycardAction*(self: AccessInterface, cardIndex: int) {.base.
|
|||
method removeMockedKeycardAction*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method notificationsNeedsEnable*(self: AccessInterface): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getLoggedInAccountPublicKey*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getLoggedInAccountDisplayName*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getLoggedInAccountImage*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method notifyLoggedInAccountChanged*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
# This way (using concepts) is used only for the modules managed by AppController
|
||||
type
|
||||
DelegateInterface* = concept c
|
||||
|
|
|
@ -21,6 +21,7 @@ import app_service/service/general/service as general_service
|
|||
import app_service/service/profile/service as profile_service
|
||||
import app_service/service/keycard/service as keycard_service
|
||||
import app_service/service/devices/service as devices_service
|
||||
from app_service/service/settings/dto/settings import SettingsDto
|
||||
|
||||
import app/modules/shared_modules/keycard_popup/module as keycard_shared_module
|
||||
|
||||
|
@ -124,10 +125,7 @@ method load*[T](self: Module[T]) =
|
|||
accounts.add(gen_acc_item.initItem(acc.id, acc.alias, acc.address, acc.derivedAccounts.whisper.publicKey, acc.keyUid))
|
||||
self.view.setGeneratedAccountList(accounts)
|
||||
|
||||
if(self.controller.shouldStartWithOnboardingScreen()):
|
||||
if main_constants.IS_MACOS:
|
||||
self.view.setCurrentStartupState(newNotificationState(state.FlowType.General, nil))
|
||||
else:
|
||||
if self.controller.shouldStartWithOnboardingScreen():
|
||||
self.view.setCurrentStartupState(newWelcomeState(state.FlowType.General, nil))
|
||||
else:
|
||||
let openedAccounts = self.controller.getOpenedAccounts()
|
||||
|
@ -349,7 +347,8 @@ method checkFetchingStatusAndProceed*[T](self: Module[T]) =
|
|||
if currStateObj.isNil:
|
||||
error "cannot resolve current state in order to resolve next state"
|
||||
return
|
||||
self.view.setCurrentStartupState(newProfileFetchingAnnouncementState(currStateObj.flowType(), nil))
|
||||
let nextState = newProfileFetchingAnnouncementState(currStateObj.flowType(), nil)
|
||||
self.view.setCurrentStartupState(nextState)
|
||||
|
||||
method onFetchingFromWakuMessageReceived*[T](self: Module[T], backedUpMsgClock: uint64, section: string,
|
||||
totalMessages: int, receivedMessageAtPosition: int) =
|
||||
|
@ -374,26 +373,10 @@ method onFetchingFromWakuMessageReceived*[T](self: Module[T], backedUpMsgClock:
|
|||
if self.view.fetchingDataModel().allMessagesLoaded():
|
||||
self.view.setCurrentStartupState(newProfileFetchingSuccessState(currStateObj.flowType(), nil))
|
||||
|
||||
proc prepareAndInitFetchingData[T](self: Module[T]) =
|
||||
method prepareAndInitFetchingData*[T](self: Module[T]) =
|
||||
# fetching data from waku starts when messenger starts
|
||||
self.view.createAndInitFetchingDataModel(listOfEntitiesWeExpectToBeSynced)
|
||||
|
||||
proc delayStartingApp[T](self: Module[T]) =
|
||||
## In the following 2 cases:
|
||||
## - FlowType.FirstRunOldUserImportSeedPhrase
|
||||
## - FlowType.FirstRunOldUserKeycardImport
|
||||
## we want to delay app start just to be sure that messages from waku will be received
|
||||
self.controller.connectToTimeoutEventAndStratTimer(timeoutInMilliseconds = 30000) # delay for 30 seconds
|
||||
|
||||
method startAppAfterDelay*[T](self: Module[T]) =
|
||||
if not self.view.fetchingDataModel().allMessagesLoaded():
|
||||
let currStateObj = self.view.currentStartupStateObj()
|
||||
if currStateObj.isNil:
|
||||
error "cannot determine current startup state", procName="startAppAfterDelay"
|
||||
quit() # quit the app
|
||||
self.view.setCurrentStartupState(newProfileFetchingState(currStateObj.flowType(), nil))
|
||||
self.moveToStartupState()
|
||||
|
||||
proc logoutAndDisplayError[T](self: Module[T], error: string, errType: StartupErrorType) =
|
||||
self.delegate.logout()
|
||||
if self.controller.isSelectedLoginAccountKeycardAccount() and
|
||||
|
@ -418,35 +401,13 @@ method onProfileConverted*[T](self: Module[T], success: bool) =
|
|||
self.moveToStartupState()
|
||||
self.view.setCurrentStartupState(newLoginKeycardConvertedToRegularAccountState(currStateObj.flowType(), nil))
|
||||
|
||||
method onNodeLogin*[T](self: Module[T], error: string) =
|
||||
method onNodeLogin*[T](self: Module[T], error: string, account: AccountDto, settings: SettingsDto) =
|
||||
let currStateObj = self.view.currentStartupStateObj()
|
||||
if currStateObj.isNil:
|
||||
error "cannot determine current startup state", procName="onNodeLogin"
|
||||
quit() # quit the app
|
||||
|
||||
if error.len == 0:
|
||||
if currStateObj.flowType() == state.FlowType.FirstRunOldUserImportSeedPhrase or
|
||||
currStateObj.flowType() == state.FlowType.FirstRunOldUserKeycardImport:
|
||||
self.prepareAndInitFetchingData()
|
||||
self.controller.connectToFetchingFromWakuEvents()
|
||||
self.delayStartingApp()
|
||||
let err = self.delegate.userLoggedIn()
|
||||
if err.len > 0:
|
||||
self.logoutAndDisplayError(err, StartupErrorType.UnknownType)
|
||||
return
|
||||
elif currStateObj.flowType() == state.FlowType.LostKeycardConvertToRegularAccount:
|
||||
self.controller.convertKeycardProfileKeypairToRegular()
|
||||
return
|
||||
else:
|
||||
let err = self.delegate.userLoggedIn()
|
||||
if err.len > 0:
|
||||
self.logoutAndDisplayError(err, StartupErrorType.UnknownType)
|
||||
return
|
||||
if currStateObj.flowType() != FlowType.AppLogin:
|
||||
let images = self.controller.storeIdentityImage()
|
||||
self.accountsService.updateLoggedInAccount(self.getDisplayName, images)
|
||||
self.finishAppLoading()
|
||||
else:
|
||||
if error.len != 0:
|
||||
self.moveToStartupState()
|
||||
if currStateObj.flowType() == state.FlowType.AppLogin:
|
||||
if self.controller.isSelectedLoginAccountKeycardAccount():
|
||||
|
@ -456,6 +417,51 @@ method onNodeLogin*[T](self: Module[T], error: string) =
|
|||
else:
|
||||
self.emitStartupError(error, StartupErrorType.SetupAccError)
|
||||
error "login error", methodName="onNodeLogin", errDesription =error
|
||||
return
|
||||
|
||||
self.controller.setLoggedInAccount(account)
|
||||
self.accountsService.updateLoggedInAccount(account.name, account.images)
|
||||
self.controller.setLoggedInProfile(settings.publicKey)
|
||||
self.view.notifyLoggedInAccountChanged()
|
||||
|
||||
if currStateObj.flowType() == state.FlowType.FirstRunOldUserImportSeedPhrase or
|
||||
currStateObj.flowType() == state.FlowType.FirstRunOldUserKeycardImport:
|
||||
let err = self.delegate.userLoggedIn()
|
||||
if err.len > 0:
|
||||
self.logoutAndDisplayError(err, StartupErrorType.UnknownType)
|
||||
return
|
||||
return
|
||||
|
||||
if currStateObj.flowType() == state.FlowType.LostKeycardConvertToRegularAccount:
|
||||
self.controller.convertKeycardProfileKeypairToRegular()
|
||||
return
|
||||
|
||||
let err = self.delegate.userLoggedIn()
|
||||
if err.len > 0:
|
||||
self.logoutAndDisplayError(err, StartupErrorType.UnknownType)
|
||||
return
|
||||
|
||||
if currStateObj.flowType() == FlowType.AppLogin:
|
||||
self.finishAppLoading()
|
||||
return
|
||||
|
||||
# TODO: Remove this block when implemented https://github.com/status-im/status-go/issues/4977
|
||||
if currStateObj.flowType() == FlowType.FirstRunNewUserNewKeycardKeys or
|
||||
currStateObj.flowType() == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||
let images = self.controller.storeIdentityImage()
|
||||
self.accountsService.updateLoggedInAccount(self.getDisplayName, images)
|
||||
self.view.notifyLoggedInAccountChanged()
|
||||
|
||||
var nextState: state.State
|
||||
if currStateObj.flowType() == FlowType.LostKeycardReplacement:
|
||||
if not self.controller.notificationsNeedsEnable():
|
||||
self.finishAppLoading()
|
||||
return
|
||||
nextState = newNotificationState(currStateObj.flowType(), nil)
|
||||
else:
|
||||
nextState = newUserProfileChatKeyState(currStateObj.flowType(), nil)
|
||||
self.view.setCurrentStartupState(nextState)
|
||||
self.moveToStartupState()
|
||||
|
||||
method onKeycardResponse*[T](self: Module[T], keycardFlowType: string, keycardEvent: KeycardEvent) =
|
||||
if self.isSharedKeycardModuleFlowRunning():
|
||||
|
@ -585,4 +591,19 @@ method insertMockedKeycardAction*[T](self: Module[T], cardIndex: int) =
|
|||
method removeMockedKeycardAction*[T](self: Module[T]) =
|
||||
self.keycardService.removeMockedKeycardAction()
|
||||
|
||||
method notificationsNeedsEnable*[T](self: Module[T]): bool =
|
||||
return self.controller.notificationsNeedsEnable()
|
||||
|
||||
method getLoggedInAccountPublicKey*[T](self: Module[T]): string =
|
||||
return self.controller.getLoggedInAccountPublicKey()
|
||||
|
||||
method getLoggedInAccountDisplayName*[T](self: Module[T]): string =
|
||||
return self.controller.getLoggedInAccountDisplayName()
|
||||
|
||||
method getLoggedInAccountImage*[T](self: Module[T]): string =
|
||||
return self.controller.getLoggedInAccountImage()
|
||||
|
||||
method notifyLoggedInAccountChanged*[T](self: Module[T]) =
|
||||
self.view.notifyLoggedInAccountChanged()
|
||||
|
||||
{.pop.}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml
|
||||
import NimQml, chronicles
|
||||
import io_interface
|
||||
import selected_login_account
|
||||
import internal/[state, state_wrapper]
|
||||
|
@ -127,6 +127,7 @@ QtObject:
|
|||
proc setAppState*(self: View, state: AppState) =
|
||||
if(self.appState == state):
|
||||
return
|
||||
debug "app state changed ", state
|
||||
self.appState = state
|
||||
self.appStateChanged(self.appState.int)
|
||||
QtProperty[int] appState:
|
||||
|
@ -206,10 +207,8 @@ QtObject:
|
|||
proc validMnemonic*(self: View, mnemonic: string): bool {.slot.} =
|
||||
return self.delegate.validMnemonic(mnemonic)
|
||||
|
||||
proc accountImportSuccess*(self: View) {.signal.}
|
||||
proc importAccountSuccess*(self: View) =
|
||||
self.importedAccountChanged()
|
||||
self.accountImportSuccess()
|
||||
|
||||
proc selectedLoginAccountChanged*(self: View) {.signal.}
|
||||
proc getSelectedLoginAccount(self: View): QVariant {.slot.} =
|
||||
|
@ -390,3 +389,30 @@ QtObject:
|
|||
|
||||
proc removeMockedKeycardAction*(self: View) {.slot.} =
|
||||
self.delegate.removeMockedKeycardAction()
|
||||
|
||||
proc getNotificationsNeedsEnable*(self: View): bool {.slot.} =
|
||||
return self.delegate.notificationsNeedsEnable()
|
||||
QtProperty[bool] notificationsNeedsEnable:
|
||||
read = getNotificationsNeedsEnable
|
||||
|
||||
proc getLoggedInAccountPublicKey*(self: View): string {.slot.} =
|
||||
return self.delegate.getLoggedInAccountPublicKey()
|
||||
proc loggedInAccountChanged*(self: View) {.signal.}
|
||||
QtProperty[string] loggedInAccountPublicKey:
|
||||
read = getLoggedInAccountPublicKey
|
||||
notify = loggedInAccountChanged
|
||||
|
||||
proc getLoggedInAccountDisplayName*(self: View): string {.slot.} =
|
||||
return self.delegate.getLoggedInAccountDisplayName()
|
||||
QtProperty[string] loggedInAccountDisplayName:
|
||||
read = getLoggedInAccountDisplayName
|
||||
notify = loggedInAccountChanged
|
||||
|
||||
proc getLoggedInAccountImage*(self: View): string {.slot.} =
|
||||
return self.delegate.getLoggedInAccountImage()
|
||||
QtProperty[string] loggedInAccountImage:
|
||||
read = getLoggedInAccountImage
|
||||
notify = loggedInAccountChanged
|
||||
|
||||
proc notifyLoggedInAccountChanged*(self: View) =
|
||||
self.loggedInAccountChanged()
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
import json, std/options
|
||||
import ./wallet_secretes_config
|
||||
import ./image_crop_rectangle
|
||||
|
||||
export wallet_secretes_config
|
||||
export image_crop_rectangle
|
||||
|
||||
type
|
||||
CreateAccountRequest* = object
|
||||
backupDisabledDataDir*: string
|
||||
kdfIterations*: int
|
||||
deviceName*: string
|
||||
displayName*: string
|
||||
password*: string
|
||||
imagePath*: string
|
||||
imageCropRectangle*: ImageCropRectangle
|
||||
customizationColor*: string
|
||||
emoji*: string
|
||||
|
||||
wakuV2Nameserver*: Option[string]
|
||||
wakuV2LightClient*: bool
|
||||
|
||||
logLevel*: Option[string]
|
||||
logFilePath*: string
|
||||
logEnabled*: bool
|
||||
|
||||
previewPrivacy*: bool
|
||||
|
||||
verifyTransactionURL*: Option[string]
|
||||
verifyENSURL*: Option[string]
|
||||
verifyENSContractAddress*: Option[string]
|
||||
verifyTransactionChainID*: Option[int64]
|
||||
upstreamConfig*: string
|
||||
networkID*: Option[uint64]
|
||||
|
||||
walletSecretsConfig*: WalletSecretsConfig
|
||||
|
||||
torrentConfigEnabled*: Option[bool]
|
||||
torrentConfigPort*: Option[int]
|
||||
|
||||
proc toJson*(self: CreateAccountRequest): JsonNode =
|
||||
result = %*{
|
||||
"backupDisabledDataDir": self.backupDisabledDataDir,
|
||||
"kdfIterations": self.kdfIterations,
|
||||
"deviceName": self.deviceName,
|
||||
"displayName": self.displayName,
|
||||
"password": self.password,
|
||||
"imagePath": self.imagePath,
|
||||
"imageCropRectangle": self.imageCropRectangle,
|
||||
"customizationColor": self.customizationColor,
|
||||
"emoji": self.emoji,
|
||||
"wakuV2LightClient": self.wakuV2LightClient,
|
||||
"logFilePath": self.logFilePath,
|
||||
"logEnabled": self.logEnabled,
|
||||
"previewPrivacy": self.previewPrivacy,
|
||||
"upstreamConfig": self.upstreamConfig,
|
||||
}
|
||||
|
||||
if self.logLevel.isSome():
|
||||
result["logLevel"] = %self.logLevel.get("")
|
||||
|
||||
if self.wakuV2Nameserver.isSome():
|
||||
result["wakuV2Nameserver"] = %self.wakuV2Nameserver.get()
|
||||
|
||||
if self.verifyTransactionURL.isSome():
|
||||
result["verifyTransactionURL"] = %self.verifyTransactionURL.get()
|
||||
|
||||
if self.verifyENSURL.isSome():
|
||||
result["verifyENSURL"] = %self.verifyENSURL.get()
|
||||
|
||||
if self.verifyENSContractAddress.isSome():
|
||||
result["verifyENSContractAddress"] = %self.verifyENSContractAddress.get()
|
||||
|
||||
if self.verifyTransactionChainID.isSome():
|
||||
result["verifyTransactionChainID"] = %self.verifyTransactionChainID.get()
|
||||
|
||||
if self.networkID.isSome():
|
||||
result["networkID"] = %self.networkID.get()
|
||||
|
||||
if self.torrentConfigEnabled.isSome():
|
||||
result["torrentConfigEnabled"] = %self.torrentConfigEnabled.get()
|
||||
|
||||
if self.torrentConfigPort.isSome():
|
||||
result["torrentConfigPort"] = %self.torrentConfigPort.get()
|
||||
|
||||
for key, value in self.walletSecretsConfig.toJson().pairs():
|
||||
result[key] = value
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
type
|
||||
ImageCropRectangle* = object
|
||||
ax*: int
|
||||
ay*: int
|
||||
bx*: int
|
||||
by*: int
|
||||
|
||||
# Default `toJson` works fine.
|
|
@ -0,0 +1,22 @@
|
|||
import json
|
||||
import ./wallet_secretes_config
|
||||
|
||||
type
|
||||
LoginAccountRequest* = object
|
||||
passwordHash*: string
|
||||
keyUID*: string
|
||||
kdfIterations*: int
|
||||
runtimeLogLevel*: string
|
||||
wakuV2Nameserver*: string
|
||||
walletSecretsConfig*: WalletSecretsConfig
|
||||
|
||||
proc toJson*(self: LoginAccountRequest): JsonNode =
|
||||
result = %* {
|
||||
"password": self.passwordHash,
|
||||
"keyUid": self.keyUID,
|
||||
"kdfIterations": self.kdfIterations,
|
||||
"runtimeLogLevel": self.runtimeLogLevel,
|
||||
"wakuV2Nameserver": self.wakuV2Nameserver
|
||||
}
|
||||
for key, value in self.walletSecretsConfig.toJson().pairs():
|
||||
result[key] = value
|
|
@ -0,0 +1,18 @@
|
|||
import json
|
||||
import create_account_request
|
||||
|
||||
type
|
||||
RestoreAccountRequest* = object
|
||||
mnemonic*: string
|
||||
fetchBackup*: bool
|
||||
createAccountRequest*: CreateAccountRequest
|
||||
|
||||
proc toJson*(self: RestoreAccountRequest): JsonNode =
|
||||
result = %*{
|
||||
"mnemonic": self.mnemonic,
|
||||
"fetchBackup": self.fetchBackup
|
||||
}
|
||||
|
||||
for key, value in self.createAccountRequest.toJson().pairs():
|
||||
result[key] = value
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import json
|
||||
|
||||
type
|
||||
WalletSecretsConfig* = object
|
||||
poktToken*: string
|
||||
infuraToken*: string
|
||||
infuraSecret*: string
|
||||
openseaApiKey*: string
|
||||
raribleMainnetApiKey*: string
|
||||
raribleTestnetApiKey*: string
|
||||
alchemyEthereumMainnetToken*: string
|
||||
alchemyEthereumGoerliToken*: string
|
||||
alchemyEthereumSepoliaToken*: string
|
||||
alchemyArbitrumMainnetToken*: string
|
||||
alchemyArbitrumGoerliToken*: string
|
||||
alchemyArbitrumSepoliaToken*: string
|
||||
alchemyOptimismMainnetToken*: string
|
||||
alchemyOptimismGoerliToken*: string
|
||||
alchemyOptimismSepoliaToken*: string
|
||||
|
||||
proc toJson*(self: WalletSecretsConfig): JsonNode =
|
||||
return %* {
|
||||
"poktToken": self.poktToken,
|
||||
"infuraToken": self.infuraToken,
|
||||
"infuraSecret": self.infuraSecret,
|
||||
"openseaApiKey": self.openseaApiKey,
|
||||
"raribleMainnetApiKey": self.raribleMainnetApiKey,
|
||||
"raribleTestnetApiKey": self.raribleTestnetApiKey,
|
||||
"alchemyEthereumMainnetToken": self.alchemyEthereumMainnetToken,
|
||||
"alchemyEthereumGoerliToken": self.alchemyEthereumGoerliToken,
|
||||
"alchemyEthereumSepoliaToken": self.alchemyEthereumSepoliaToken,
|
||||
"alchemyArbitrumMainnetToken": self.alchemyArbitrumMainnetToken,
|
||||
"alchemyArbitrumGoerliToken": self.alchemyArbitrumGoerliToken,
|
||||
"alchemyArbitrumSepoliaToken": self.alchemyArbitrumSepoliaToken,
|
||||
"alchemyOptimismMainnetToken": self.alchemyOptimismMainnetToken,
|
||||
"alchemyOptimismGoerliToken": self.alchemyOptimismGoerliToken,
|
||||
"alchemyOptimismSepoliaToken": self.alchemyOptimismSepoliaToken
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
import NimQml, Tables, os, json, stew/shims/strformat, sequtils, strutils, uuids, times
|
||||
import NimQml, Tables, os, json, stew/shims/strformat, sequtils, strutils, uuids, times, std/options
|
||||
import json_serialization, chronicles
|
||||
|
||||
import ../../../app/global/global_singleton
|
||||
import ./dto/accounts as dto_accounts
|
||||
import ./dto/generated_accounts as dto_generated_accounts
|
||||
import ./dto/login_request
|
||||
import ./dto/create_account_request
|
||||
import ./dto/restore_account_request
|
||||
|
||||
from ../keycard/service import KeycardEvent, KeyDetails
|
||||
import ../../../backend/general as status_general
|
||||
import ../../../backend/core as status_core
|
||||
|
@ -29,7 +33,7 @@ const DEFAULT_WALLET_ACCOUNT_NAME = "Account 1"
|
|||
const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET, PATH_ENCRYPTION]
|
||||
const ACCOUNT_ALREADY_EXISTS_ERROR* = "account already exists"
|
||||
const KDF_ITERATIONS* {.intdefine.} = 256_000
|
||||
const DEFAULT_COLORID_FOR_DEFAULT_WALLET_ACCOUNT = "primary" # to match `CustomizationColor` on the go side
|
||||
const DEFAULT_CUSTOMIZATION_COLOR = "primary" # to match `CustomizationColor` on the go side
|
||||
|
||||
# allow runtime override via environment variable. core contributors can set a
|
||||
# specific peer to set for testing messaging and mailserver functionality with squish.
|
||||
|
@ -66,8 +70,6 @@ QtObject:
|
|||
defaultWalletEmoji: string
|
||||
tmpAccount: AccountDto
|
||||
tmpHashedPassword: string
|
||||
tmpThumbnailImage: string
|
||||
tmpLargeImage: string
|
||||
|
||||
proc delete*(self: Service) =
|
||||
self.QObject.delete
|
||||
|
@ -81,9 +83,17 @@ QtObject:
|
|||
result.keyStoreDir = main_constants.ROOTKEYSTOREDIR
|
||||
result.defaultWalletEmoji = ""
|
||||
|
||||
proc setLocalAccountSettingsFile(self: Service) =
|
||||
if self.loggedInAccount.isValid():
|
||||
singletonInstance.localAccountSettings.setFileName(self.loggedInAccount.name)
|
||||
|
||||
proc getLoggedInAccount*(self: Service): AccountDto =
|
||||
return self.loggedInAccount
|
||||
|
||||
proc setLoggedInAccount*(self: Service, account: AccountDto) =
|
||||
self.loggedInAccount = account
|
||||
self.setLocalAccountSettingsFile()
|
||||
|
||||
proc updateLoggedInAccount*(self: Service, displayName: string, images: seq[Image]) =
|
||||
self.loggedInAccount.name = displayName
|
||||
self.loggedInAccount.images = images
|
||||
|
@ -125,12 +135,12 @@ QtObject:
|
|||
self.loggedInAccount = AccountDto()
|
||||
self.importedAccount = GeneratedAccountDto()
|
||||
|
||||
proc validateMnemonic*(self: Service, mnemonic: string): string =
|
||||
proc validateMnemonic*(self: Service, mnemonic: string): (string, string) =
|
||||
try:
|
||||
let response = status_general.validateMnemonic(mnemonic)
|
||||
if response.result.contains("error"):
|
||||
return response.result["error"].getStr
|
||||
return ""
|
||||
return ("", response.result["error"].getStr)
|
||||
return (response.result["keyUID"].getStr, "")
|
||||
except Exception as e:
|
||||
error "error: ", procName="validateMnemonic", errName = e.name, errDesription = e.msg
|
||||
|
||||
|
@ -153,47 +163,7 @@ QtObject:
|
|||
error "error: ", procName="openedAccounts", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc openedAccountsContainsKeyUid*(self: Service, keyUid: string): bool =
|
||||
let openedAccounts = self.openedAccounts()
|
||||
for acc in openedAccounts:
|
||||
if acc.keyUid == keyUid:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc storeDerivedAccounts(self: Service, accountId, hashedPassword: string,
|
||||
paths: seq[string]): DerivedAccounts =
|
||||
let response = status_account.storeDerivedAccounts(accountId, hashedPassword, paths)
|
||||
|
||||
if response.result.contains("error"):
|
||||
raise newException(Exception, response.result["error"].getStr)
|
||||
|
||||
result = toDerivedAccounts(response.result)
|
||||
|
||||
proc storeAccount(self: Service, accountId, hashedPassword: string): GeneratedAccountDto =
|
||||
let response = status_account.storeAccounts(accountId, hashedPassword)
|
||||
|
||||
if response.result.contains("error"):
|
||||
raise newException(Exception, response.result["error"].getStr)
|
||||
|
||||
result = toGeneratedAccountDto(response.result)
|
||||
|
||||
proc saveAccountAndLogin(self: Service, hashedPassword: string, account,
|
||||
subaccounts, settings, config: JsonNode): AccountDto =
|
||||
try:
|
||||
let response = status_account.saveAccountAndLogin(hashedPassword, account, subaccounts, settings, config)
|
||||
|
||||
var error = "response doesn't contain \"error\""
|
||||
if(response.result.contains("error")):
|
||||
error = response.result["error"].getStr
|
||||
if error == "":
|
||||
debug "Account saved succesfully"
|
||||
result = toAccountDto(account)
|
||||
return
|
||||
|
||||
let err = "Error saving account and logging in: " & error
|
||||
error "error: ", procName="saveAccountAndLogin", errDesription = err
|
||||
|
||||
except Exception as e:
|
||||
error "error: ", procName="saveAccountAndLogin", errName = e.name, errDesription = e.msg
|
||||
return (keyUID in self.openedAccounts().mapIt(it.keyUid))
|
||||
|
||||
proc saveKeycardAccountAndLogin(self: Service, chatKey, password: string, account, subaccounts, settings,
|
||||
config: JsonNode): AccountDto =
|
||||
|
@ -214,31 +184,13 @@ QtObject:
|
|||
except Exception as e:
|
||||
error "error: ", procName="saveKeycardAccountAndLogin", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc prepareAccountJsonObject(self: Service, account: GeneratedAccountDto, displayName: string): JsonNode =
|
||||
result = %* {
|
||||
"name": if displayName == "": account.alias else: displayName,
|
||||
"address": account.address,
|
||||
"key-uid": account.keyUid,
|
||||
"keycard-pairing": nil,
|
||||
"kdfIterations": KDF_ITERATIONS,
|
||||
}
|
||||
|
||||
proc getAccountDataForAccountId(self: Service, accountId: string, displayName: string): JsonNode =
|
||||
for acc in self.generatedAccounts:
|
||||
if(acc.id == accountId):
|
||||
return self.prepareAccountJsonObject(acc, displayName)
|
||||
|
||||
if(self.importedAccount.isValid()):
|
||||
if(self.importedAccount.id == accountId):
|
||||
return self.prepareAccountJsonObject(self.importedAccount, displayName)
|
||||
|
||||
proc prepareSubaccountJsonObject(self: Service, account: GeneratedAccountDto, displayName: string):
|
||||
JsonNode =
|
||||
result = %* [
|
||||
{
|
||||
"public-key": account.derivedAccounts.defaultWallet.publicKey,
|
||||
"address": account.derivedAccounts.defaultWallet.address,
|
||||
"colorId": DEFAULT_COLORID_FOR_DEFAULT_WALLET_ACCOUNT,
|
||||
"colorId": DEFAULT_CUSTOMIZATION_COLOR,
|
||||
"wallet": true,
|
||||
"path": PATH_DEFAULT_WALLET,
|
||||
"name": DEFAULT_WALLET_ACCOUNT_NAME,
|
||||
|
@ -312,6 +264,7 @@ QtObject:
|
|||
if(self.importedAccount.id == accountId):
|
||||
return self.prepareAccountSettingsJsonObject(self.importedAccount, installationId, displayName, withoutMnemonic)
|
||||
|
||||
# TODO: Remove after https://github.com/status-im/status-go/issues/4977
|
||||
proc getDefaultNodeConfig*(self: Service, installationId: string, recoverAccount: bool): JsonNode =
|
||||
let fleet = Fleet.ShardsTest
|
||||
let dnsDiscoveryURL = "enrtree://AMOJVZX4V6EXP7NTJPMAYJYST2QP6AJXYW76IU6VGJS7UVSNDYZG4@boot.test.shards.nodes.status.im"
|
||||
|
@ -352,6 +305,7 @@ QtObject:
|
|||
result["KeycardPairingDataFile"] = newJString(main_constants.KEYCARDPAIRINGDATAFILE)
|
||||
result["ProcessBackedupMessages"] = newJBool(recoverAccount)
|
||||
|
||||
# TODO: Remove after https://github.com/status-im/status-go/issues/4977
|
||||
proc getLoginNodeConfig(self: Service): JsonNode =
|
||||
# To create appropriate NodeConfig for Login we set only params that maybe be set via env variables or cli flags
|
||||
result = %*{}
|
||||
|
@ -394,10 +348,6 @@ QtObject:
|
|||
if STATUS_PORT != 0:
|
||||
result["ListenAddr"] = newJString("0.0.0.0:" & $main_constants.STATUS_PORT)
|
||||
|
||||
proc setLocalAccountSettingsFile(self: Service) =
|
||||
if self.getLoggedInAccount.isValid():
|
||||
singletonInstance.localAccountSettings.setFileName(self.getLoggedInAccount.name)
|
||||
|
||||
proc addKeycardDetails(self: Service, kcInstance: string, settingsJson: var JsonNode, accountData: var JsonNode) =
|
||||
let keycardPairingJsonString = readFile(main_constants.KEYCARDPAIRINGDATAFILE)
|
||||
let keycardPairingJsonObj = keycardPairingJsonString.parseJSON
|
||||
|
@ -412,38 +362,87 @@ QtObject:
|
|||
if not accountData.isNil:
|
||||
accountData["keycard-pairing"] = kcDataObj{"key"}
|
||||
|
||||
proc setupAccount*(self: Service, accountId, password, displayName: string, removeMnemonic: bool, recoverAccount: bool = false): string =
|
||||
proc buildWalletSecrets(self: Service): WalletSecretsConfig =
|
||||
return WalletSecretsConfig(
|
||||
poktToken: POKT_TOKEN_RESOLVED,
|
||||
infuraToken: INFURA_TOKEN_RESOLVED,
|
||||
infuraSecret: INFURA_TOKEN_SECRET_RESOLVED,
|
||||
openseaApiKey: OPENSEA_API_KEY_RESOLVED,
|
||||
raribleMainnetApiKey: RARIBLE_MAINNET_API_KEY_RESOLVED,
|
||||
raribleTestnetApiKey: RARIBLE_TESTNET_API_KEY_RESOLVED,
|
||||
alchemyEthereumMainnetToken: ALCHEMY_ETHEREUM_MAINNET_TOKEN_RESOLVED,
|
||||
alchemyEthereumGoerliToken: ALCHEMY_ETHEREUM_GOERLI_TOKEN_RESOLVED,
|
||||
alchemyEthereumSepoliaToken: ALCHEMY_ETHEREUM_SEPOLIA_TOKEN_RESOLVED,
|
||||
alchemyArbitrumMainnetToken: ALCHEMY_ARBITRUM_MAINNET_TOKEN_RESOLVED,
|
||||
alchemyArbitrumGoerliToken: ALCHEMY_ARBITRUM_GOERLI_TOKEN_RESOLVED,
|
||||
alchemyArbitrumSepoliaToken: ALCHEMY_ARBITRUM_SEPOLIA_TOKEN_RESOLVED,
|
||||
alchemyOptimismMainnetToken: ALCHEMY_OPTIMISM_MAINNET_TOKEN_RESOLVED,
|
||||
alchemyOptimismGoerliToken: ALCHEMY_OPTIMISM_GOERLI_TOKEN_RESOLVED,
|
||||
alchemyOptimismSepoliaToken: ALCHEMY_OPTIMISM_SEPOLIA_TOKEN_RESOLVED,
|
||||
)
|
||||
|
||||
proc buildCreateAccountRequest(self: Service, password: string, displayName: string, imagePath: string, imageCropRectangle: ImageCropRectangle): CreateAccountRequest =
|
||||
return CreateAccountRequest(
|
||||
backupDisabledDataDir: main_constants.STATUSGODIR,
|
||||
kdfIterations: KDF_ITERATIONS,
|
||||
password: hashPassword(password),
|
||||
displayName: displayName,
|
||||
imagePath: imagePath,
|
||||
imageCropRectangle: imageCropRectangle,
|
||||
customizationColor: DEFAULT_CUSTOMIZATION_COLOR,
|
||||
emoji: self.defaultWalletEmoji,
|
||||
logLevel: some(toStatusGoSupportedLogLevel(main_constants.LOG_LEVEL)),
|
||||
wakuV2LightClient: false,
|
||||
previewPrivacy: true,
|
||||
torrentConfigEnabled: some(true),
|
||||
torrentConfigPort: some(TORRENT_CONFIG_PORT),
|
||||
walletSecretsConfig: self.buildWalletSecrets(),
|
||||
)
|
||||
|
||||
proc createAccountAndLogin*(self: Service, password: string, displayName: string, imagePath: string, imageCropRectangle: ImageCropRectangle): string =
|
||||
try:
|
||||
let installationId = $genUUID()
|
||||
var accountDataJson = self.getAccountDataForAccountId(accountId, displayName)
|
||||
self.setKeyStoreDir(accountDataJson{"key-uid"}.getStr) # must be called before `getDefaultNodeConfig`
|
||||
let subaccountDataJson = self.getSubaccountDataForAccountId(accountId, displayName)
|
||||
var settingsJson = self.getAccountSettings(accountId, installationId, displayName, removeMnemonic)
|
||||
let nodeConfigJson = self.getDefaultNodeConfig(installationId, recoverAccount)
|
||||
let request = self.buildCreateAccountRequest(password, displayName, imagePath, imageCropRectangle)
|
||||
let response = status_account.createAccountAndLogin(request)
|
||||
|
||||
if(accountDataJson.isNil or subaccountDataJson.isNil or settingsJson.isNil or
|
||||
nodeConfigJson.isNil):
|
||||
let description = "at least one json object is not prepared well"
|
||||
error "error: ", procName="setupAccount", errDesription = description
|
||||
return description
|
||||
if not response.result.contains("error"):
|
||||
error "invalid status-go response", response
|
||||
return "invalid response: no error field found"
|
||||
|
||||
let hashedPassword = hashPassword(password)
|
||||
discard self.storeAccount(accountId, hashedPassword)
|
||||
discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS)
|
||||
self.loggedInAccount = self.saveAccountAndLogin(hashedPassword,
|
||||
accountDataJson,
|
||||
subaccountDataJson,
|
||||
settingsJson,
|
||||
nodeConfigJson)
|
||||
|
||||
self.setLocalAccountSettingsFile()
|
||||
|
||||
if self.getLoggedInAccount.isValid():
|
||||
let error = response.result["error"].getStr
|
||||
if error == "":
|
||||
debug "Account saved succesfully"
|
||||
return ""
|
||||
else:
|
||||
return "logged in account is not valid"
|
||||
|
||||
error "createAccountAndLogin status-go error: ", error
|
||||
return "createAccountAndLogin failed: " & error
|
||||
|
||||
except Exception as e:
|
||||
error "error: ", procName="setupAccount", errName = e.name, errDesription = e.msg
|
||||
error "failed to create account or login", procName="createAccountAndLogin", errName = e.name, errDesription = e.msg
|
||||
return e.msg
|
||||
|
||||
proc importAccountAndLogin*(self: Service, mnemonic: string, password: string, recoverAccount: bool, displayName: string, imagePath: string, imageCropRectangle: ImageCropRectangle): string =
|
||||
try:
|
||||
let request = RestoreAccountRequest(
|
||||
mnemonic: mnemonic,
|
||||
fetchBackup: recoverAccount,
|
||||
createAccountRequest: self.buildCreateAccountRequest(password, displayName, imagePath, imageCropRectangle),
|
||||
)
|
||||
let response = status_account.restoreAccountAndLogin(request)
|
||||
|
||||
if not response.result.contains("error"):
|
||||
error "invalid status-go response", response
|
||||
return "invalid response: no error field found"
|
||||
|
||||
let error = response.result["error"].getStr
|
||||
if error == "":
|
||||
debug "Account saved succesfully"
|
||||
return ""
|
||||
|
||||
error "importAccountAndLogin status-go error: ", error
|
||||
return "importAccountAndLogin failed: " & error
|
||||
|
||||
except Exception as e:
|
||||
error "failed to import account or login", procName="importAccountAndLogin", errName = e.name, errDesription = e.msg
|
||||
return e.msg
|
||||
|
||||
proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent, displayName: string, useImportedAcc: bool,
|
||||
|
@ -491,7 +490,7 @@ QtObject:
|
|||
{
|
||||
"public-key": walletPublicKey,
|
||||
"address": walletAddress,
|
||||
"colorId": DEFAULT_COLORID_FOR_DEFAULT_WALLET_ACCOUNT,
|
||||
"colorId": DEFAULT_CUSTOMIZATION_COLOR,
|
||||
"wallet": true,
|
||||
"path": PATH_DEFAULT_WALLET,
|
||||
"name": DEFAULT_WALLET_ACCOUNT_NAME,
|
||||
|
@ -658,35 +657,28 @@ QtObject:
|
|||
except Exception as e:
|
||||
error "error: ", procName="verifyDatabasePassword", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc doLogin(self: Service, account: AccountDto, hashedPassword, thumbnailImage, largeImage: string) =
|
||||
let nodeConfigJson = self.getLoginNodeConfig()
|
||||
let response = status_account.login(
|
||||
account.name,
|
||||
account.keyUid,
|
||||
account.kdfIterations,
|
||||
hashedPassword,
|
||||
thumbnailImage,
|
||||
largeImage,
|
||||
$nodeConfigJson
|
||||
proc doLogin(self: Service, account: AccountDto, passwordHash: string) =
|
||||
var request = LoginAccountRequest(
|
||||
keyUid: account.keyUid,
|
||||
kdfIterations: account.kdfIterations,
|
||||
passwordHash: passwordHash,
|
||||
walletSecretsConfig: self.buildWalletSecrets(),
|
||||
)
|
||||
|
||||
if main_constants.runtimeLogLevelSet():
|
||||
request.runtimeLogLevel = toStatusGoSupportedLogLevel(main_constants.LOG_LEVEL)
|
||||
|
||||
let response = status_account.loginAccount(request)
|
||||
|
||||
if response.result{"error"}.getStr != "":
|
||||
self.events.emit(SIGNAL_LOGIN_ERROR, LoginErrorArgs(error: response.result{"error"}.getStr))
|
||||
return
|
||||
|
||||
debug "Account logged in"
|
||||
self.loggedInAccount = account
|
||||
debug "account logged in"
|
||||
self.setLocalAccountSettingsFile()
|
||||
|
||||
proc login*(self: Service, account: AccountDto, hashedPassword: string) =
|
||||
try:
|
||||
var thumbnailImage: string
|
||||
var largeImage: string
|
||||
for img in account.images:
|
||||
if(img.imgType == "thumbnail"):
|
||||
thumbnailImage = img.uri
|
||||
elif(img.imgType == "large"):
|
||||
largeImage = img.uri
|
||||
|
||||
let keyStoreDir = joinPath(main_constants.ROOTKEYSTOREDIR, account.keyUid) & main_constants.sep
|
||||
if not dirExists(keyStoreDir):
|
||||
os.createDir(keyStoreDir)
|
||||
|
@ -698,11 +690,11 @@ QtObject:
|
|||
|
||||
let isOldHashPassword = self.verifyDatabasePassword(account.keyUid, hashedPasswordToUpperCase(hashedPassword))
|
||||
if isOldHashPassword:
|
||||
debug "database reencryption scheduled"
|
||||
|
||||
# Save tmp properties so that we can login after the timer
|
||||
self.tmpAccount = account
|
||||
self.tmpHashedPassword = hashedPassword
|
||||
self.tmpThumbnailImage = thumbnailImage
|
||||
self.tmpLargeImage = largeImage
|
||||
|
||||
# Start a 1 second timer for the loading screen to appear
|
||||
let arg = TimerTaskArg(
|
||||
|
@ -714,24 +706,25 @@ QtObject:
|
|||
self.threadpool.start(arg)
|
||||
return
|
||||
|
||||
self.doLogin(account, hashedPassword, thumbnailImage, largeImage)
|
||||
self.doLogin(account, hashedPassword)
|
||||
|
||||
except Exception as e:
|
||||
error "error: ", procName="login", errName = e.name, errDesription = e.msg
|
||||
error "login failed", errName = e.name, errDesription = e.msg
|
||||
self.events.emit(SIGNAL_LOGIN_ERROR, LoginErrorArgs(error: e.msg))
|
||||
|
||||
proc onWaitForReencryptionTimeout(self: Service, response: string) {.slot.} =
|
||||
debug "starting database reencryption"
|
||||
|
||||
# Reencryption (can freeze and take up to 30 minutes)
|
||||
let oldHashedPassword = hashedPasswordToUpperCase(self.tmpHashedPassword)
|
||||
discard status_privacy.changeDatabasePassword(self.tmpAccount.keyUid, oldHashedPassword, self.tmpHashedPassword)
|
||||
|
||||
# Normal login after reencryption
|
||||
self.doLogin(self.tmpAccount, self.tmpHashedPassword, self.tmpThumbnailImage, self.tmpLargeImage)
|
||||
self.doLogin(self.tmpAccount, self.tmpHashedPassword)
|
||||
|
||||
# Clear out the temp properties
|
||||
self.tmpAccount = AccountDto()
|
||||
self.tmpHashedPassword = ""
|
||||
self.tmpThumbnailImage = ""
|
||||
self.tmpLargeImage = ""
|
||||
|
||||
proc loginAccountKeycard*(self: Service, accToBeLoggedIn: AccountDto, keycardData: KeycardEvent): string =
|
||||
try:
|
||||
|
@ -758,7 +751,7 @@ QtObject:
|
|||
self.setLocalAccountSettingsFile()
|
||||
return
|
||||
except Exception as e:
|
||||
error "error: ", procName="loginAccountKeycard", errName = e.name, errDesription = e.msg
|
||||
error "keycard login failed", procName="loginAccountKeycard", errName = e.name, errDesription = e.msg
|
||||
return e.msg
|
||||
|
||||
proc convertRegularProfileKeypairToKeycard*(self: Service, keycardUid, currentPassword: string, newPassword: string) =
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import json, json_serialization, chronicles, strutils
|
||||
import ./core, ../app_service/common/utils
|
||||
import ../app_service/service/wallet_account/dto/account_dto
|
||||
import ../app_service/service/accounts/dto/login_request
|
||||
import ../app_service/service/accounts/dto/create_account_request
|
||||
import ../app_service/service/accounts/dto/restore_account_request
|
||||
import ./response_type
|
||||
|
||||
import status_go
|
||||
|
@ -310,36 +313,6 @@ proc openedAccounts*(path: string): RpcResponse[JsonNode] =
|
|||
error "error doing rpc request", methodName = "openedAccounts", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc storeDerivedAccounts*(id, hashedPassword: string, paths: seq[string]):
|
||||
RpcResponse[JsonNode] =
|
||||
let payload = %* {
|
||||
"accountID": id,
|
||||
"paths": paths,
|
||||
"password": hashedPassword
|
||||
}
|
||||
|
||||
try:
|
||||
let response = status_go.multiAccountStoreDerivedAccounts($payload)
|
||||
result.result = Json.decode(response, JsonNode)
|
||||
|
||||
except RpcException as e:
|
||||
error "error doing rpc request", methodName = "storeDerivedAccounts", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc storeAccounts*(id, hashedPassword: string): RpcResponse[JsonNode] =
|
||||
let payload = %* {
|
||||
"accountID": id,
|
||||
"password": hashedPassword
|
||||
}
|
||||
|
||||
try:
|
||||
let response = status_go.multiAccountStoreAccount($payload)
|
||||
result.result = Json.decode(response, JsonNode)
|
||||
|
||||
except RpcException as e:
|
||||
error "error doing rpc request", methodName = "storeAccounts", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc addPeer*(peer: string): RpcResponse[JsonNode] =
|
||||
try:
|
||||
let response = status_go.addPeer(peer)
|
||||
|
@ -349,15 +322,24 @@ proc addPeer*(peer: string): RpcResponse[JsonNode] =
|
|||
error "error doing rpc request", methodName = "addPeer", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc saveAccountAndLogin*(hashedPassword: string, account, subaccounts, settings,
|
||||
config: JsonNode): RpcResponse[JsonNode] =
|
||||
proc createAccountAndLogin*(request: CreateAccountRequest): RpcResponse[JsonNode] =
|
||||
try:
|
||||
let response = status_go.saveAccountAndLogin($account, hashedPassword,
|
||||
$settings, $config, $subaccounts)
|
||||
let payload = request.toJson()
|
||||
let response = status_go.createAccountAndLogin($payload)
|
||||
result.result = Json.decode(response, JsonNode)
|
||||
|
||||
except RpcException as e:
|
||||
error "error doing rpc request", methodName = "saveAccountAndLogin", exception=e.msg
|
||||
error "error doing rpc request", methodName = "createAccountAndLogin", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc restoreAccountAndLogin*(request: RestoreAccountRequest): RpcResponse[JsonNode] =
|
||||
try:
|
||||
let payload = request.toJson()
|
||||
let response = status_go.restoreAccountAndLogin($payload)
|
||||
result.result = Json.decode(response, JsonNode)
|
||||
|
||||
except RpcException as e:
|
||||
error "error doing rpc request", methodName = "restoreAccountAndLogin", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc saveAccountAndLoginWithKeycard*(chatKey, password: string, account, subaccounts, settings, config: JsonNode):
|
||||
|
@ -388,25 +370,14 @@ proc convertKeycardProfileKeypairToRegular*(mnemonic: string, currPassword: stri
|
|||
error "error doing rpc request", methodName = "convertKeycardProfileKeypairToRegular", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc login*(name, keyUid: string, kdfIterations: int, hashedPassword, thumbnail, large: string, nodeCfgObj: string):
|
||||
RpcResponse[JsonNode]
|
||||
=
|
||||
proc loginAccount*(request: LoginAccountRequest): RpcResponse[JsonNode] =
|
||||
try:
|
||||
var payload = %* {
|
||||
"name": name,
|
||||
"key-uid": keyUid,
|
||||
"identityImage": newJNull(),
|
||||
"kdfIterations": kdfIterations,
|
||||
}
|
||||
|
||||
if(thumbnail.len>0 and large.len > 0):
|
||||
payload["identityImage"] = %* {"thumbnail": thumbnail, "large": large}
|
||||
|
||||
let response = status_go.loginWithConfig($payload, hashedPassword, nodeCfgObj)
|
||||
let payload = request.toJson()
|
||||
let response = status_go.loginAccount($payload)
|
||||
result.result = Json.decode(response, JsonNode)
|
||||
|
||||
except RpcException as e:
|
||||
error "error doing rpc request", methodName = "login", exception=e.msg
|
||||
error "loginAccount failed", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc loginWithKeycard*(chatKey, password: string, account, confNode: JsonNode): RpcResponse[JsonNode] =
|
||||
|
|
|
@ -51,9 +51,11 @@ OnboardingBasePage {
|
|||
return keysMainViewComponent
|
||||
|
||||
case Constants.startupState.userProfileCreate:
|
||||
case Constants.startupState.userProfileChatKey:
|
||||
return insertDetailsViewComponent
|
||||
|
||||
case Constants.startupState.userProfileChatKey:
|
||||
return profileChatKeyViewComponent
|
||||
|
||||
case Constants.startupState.userProfileCreatePassword:
|
||||
return createPasswordViewComponent
|
||||
|
||||
|
@ -241,6 +243,13 @@ following the \"Add existing Status user\" flow, using your seed phrase.")
|
|||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: profileChatKeyViewComponent
|
||||
ProfileChatKeyView {
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: createPasswordViewComponent
|
||||
CreatePasswordView {
|
||||
|
|
|
@ -20,6 +20,7 @@ QtObject {
|
|||
readonly property string localPairingInstallationId: startupModuleInst ? startupModuleInst.localPairingInstallationId : ""
|
||||
readonly property string localPairingInstallationName: startupModuleInst ? startupModuleInst.localPairingInstallationName : ""
|
||||
readonly property string localPairingInstallationDeviceType: startupModuleInst ? startupModuleInst.localPairingInstallationDeviceType : ""
|
||||
readonly property bool notificationsNeedsEnable: startupModuleInst ? startupModuleInst.notificationsNeedsEnable : false
|
||||
|
||||
function backAction() {
|
||||
root.currentStartupState.backAction()
|
||||
|
|
|
@ -68,7 +68,7 @@ Item {
|
|||
leftPadding: Style.current.padding
|
||||
rightPadding: Style.current.padding
|
||||
font.weight: Font.Medium
|
||||
text: qsTr("Ok, got it")
|
||||
text: qsTr("Start using Status")
|
||||
onClicked: {
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
|
|
|
@ -22,56 +22,23 @@ import "../shared"
|
|||
Item {
|
||||
id: root
|
||||
objectName: "onboardingInsertDetailsView"
|
||||
|
||||
property StartupStore startupStore
|
||||
|
||||
property string pubKey
|
||||
property string address
|
||||
property string displayName
|
||||
signal createPassword()
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!!root.startupStore.startupModuleInst.importedAccountPubKey) {
|
||||
root.address = root.startupStore.startupModuleInst.importedAccountAddress ;
|
||||
root.pubKey = root.startupStore.startupModuleInst.importedAccountPubKey;
|
||||
}
|
||||
nameInput.text = root.startupStore.getDisplayName();
|
||||
userImage.asset.name = root.startupStore.getCroppedProfileImage();
|
||||
}
|
||||
|
||||
onStateChanged: {
|
||||
if (state === Constants.startupState.userProfileCreate) {
|
||||
nameInput.input.edit.forceActiveFocus()
|
||||
return
|
||||
}
|
||||
nextBtn.forceActiveFocus()
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !root.startupStore.startupModuleInst.importedAccountPubKey
|
||||
sourceComponent: StatusListView {
|
||||
model: root.startupStore.startupModuleInst.generatedAccountsModel
|
||||
delegate: Item {
|
||||
Component.onCompleted: {
|
||||
if (index === 0) {
|
||||
root.address = model.address;
|
||||
root.pubKey = model.pubKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
function doAction() {
|
||||
if(!nextBtn.enabled) {
|
||||
if (!nextBtn.enabled) {
|
||||
return
|
||||
}
|
||||
if (root.state === Constants.startupState.userProfileCreate) {
|
||||
root.startupStore.setDisplayName(nameInput.text)
|
||||
root.displayName = nameInput.text;
|
||||
}
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
|
@ -109,19 +76,16 @@ Item {
|
|||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
Layout.topMargin: Style.current.bigPadding
|
||||
StatusSmartIdenticon {
|
||||
anchors.left: parent.left
|
||||
id: userImage
|
||||
objectName: "welcomeScreenUserProfileImage"
|
||||
anchors.left: parent.left
|
||||
asset.width: 86
|
||||
asset.height: 86
|
||||
asset.letterSize: 32
|
||||
asset.color: Utils.colorForPubkey(root.pubKey)
|
||||
asset.color: Utils.colorForColorId(0) // We haven't generated the keys yet, show default color
|
||||
asset.charactersLen: 2
|
||||
asset.isImage: !!asset.name
|
||||
asset.imgIsIdenticon: false
|
||||
ringSettings {
|
||||
ringSpecModel: Utils.getColorHashAsJson(root.pubKey)
|
||||
}
|
||||
}
|
||||
StatusRoundButton {
|
||||
id: updatePicButton
|
||||
|
@ -165,54 +129,6 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: chatKeyTxt
|
||||
objectName: "insertDetailsViewChatKeyTxt"
|
||||
Layout.preferredHeight: 22
|
||||
color: Style.current.secondaryText
|
||||
text: qsTr("Chatkey:") + " " + Utils.getCompressedPk(root.pubKey)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
||||
Layout.topMargin: 13
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Item {
|
||||
id: chainsChatKeyImg
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
||||
Layout.topMargin: Style.current.padding
|
||||
Layout.preferredWidth: 215
|
||||
Layout.preferredHeight: 77
|
||||
|
||||
Image {
|
||||
id: imgChains
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: Style.svg("onboarding/chains")
|
||||
cache: false
|
||||
}
|
||||
EmojiHash {
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
}
|
||||
publicKey: root.pubKey
|
||||
objectName: "publicKeyEmojiHash"
|
||||
}
|
||||
StatusSmartIdenticon {
|
||||
id: userImageCopy
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
rightMargin: 25
|
||||
}
|
||||
asset.width: 44
|
||||
asset.height: 44
|
||||
asset.color: "transparent"
|
||||
ringSettings { ringSpecModel: Utils.getColorHashAsJson(root.pubKey) }
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
@ -252,92 +168,4 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: Constants.startupState.userProfileCreate
|
||||
when: root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileCreate
|
||||
PropertyChanges {
|
||||
target: usernameText
|
||||
text: qsTr("Your profile")
|
||||
}
|
||||
PropertyChanges {
|
||||
target: txtDesc
|
||||
text: qsTr("Longer and unusual names are better as they are less likely to be used by someone else.")
|
||||
}
|
||||
PropertyChanges {
|
||||
target: chatKeyTxt
|
||||
visible: false
|
||||
}
|
||||
PropertyChanges {
|
||||
target: chainsChatKeyImg
|
||||
opacity: 0.0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: userImageCopy
|
||||
opacity: 0.0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: updatePicButton
|
||||
opacity: 1.0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: nameInputItem
|
||||
enabled: true
|
||||
visible: true
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: Constants.startupState.userProfileChatKey
|
||||
when: root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileChatKey
|
||||
PropertyChanges {
|
||||
target: usernameText
|
||||
text: qsTr("Your emojihash and identicon ring")
|
||||
}
|
||||
PropertyChanges {
|
||||
target: txtDesc
|
||||
text: qsTr("This set of emojis and coloured ring around your avatar are unique and represent your chat key, so your friends can easily distinguish you from potential impersonators.")
|
||||
}
|
||||
PropertyChanges {
|
||||
target: chatKeyTxt
|
||||
visible: true
|
||||
}
|
||||
PropertyChanges {
|
||||
target: chainsChatKeyImg
|
||||
opacity: 1.0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: userImageCopy
|
||||
opacity: 1.0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: updatePicButton
|
||||
opacity: 0.0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: nameInputItem
|
||||
enabled: false
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: "*"
|
||||
to: "*"
|
||||
SequentialAnimation {
|
||||
PropertyAction {
|
||||
target: root
|
||||
property: "opacity"
|
||||
value: 0.0
|
||||
}
|
||||
PropertyAction {
|
||||
target: root
|
||||
property: "opacity"
|
||||
value: 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.14
|
||||
import QtQuick.Dialogs 1.3
|
||||
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
|
||||
import shared.panels 1.0
|
||||
import shared 1.0
|
||||
import shared.popups 1.0
|
||||
import shared.controls 1.0
|
||||
import utils 1.0
|
||||
|
||||
import "../popups"
|
||||
import "../stores"
|
||||
import "../shared"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
objectName: "onboardingProfileChatKeyView"
|
||||
|
||||
property StartupStore startupStore
|
||||
|
||||
Component.onCompleted: {
|
||||
nextBtn.forceActiveFocus()
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property string publicKey: root.startupStore.startupModuleInst.loggedInAccountPublicKey
|
||||
readonly property string displayName: root.startupStore.startupModuleInst.loggedInAccountDisplayName
|
||||
readonly property string image: root.startupStore.startupModuleInst.loggedInAccountImage
|
||||
|
||||
function doAction() {
|
||||
if (!nextBtn.enabled) {
|
||||
return
|
||||
}
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
height: 461
|
||||
anchors.centerIn: parent
|
||||
|
||||
StyledText {
|
||||
id: usernameText
|
||||
objectName: "onboardingHeaderText"
|
||||
text: qsTr("Your emojihash and identicon ring")
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 22
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: txtDesc
|
||||
Layout.preferredWidth: root.state === Constants.startupState.userProfileCreate? 338 : 643
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
Layout.topMargin: Style.current.smallPadding
|
||||
color: Style.current.secondaryText
|
||||
text: qsTr("This set of emojis and coloured ring around your avatar are unique and represent your chat key, so your friends can easily distinguish you from potential impersonators.")
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: 15
|
||||
lineHeight: 1.2
|
||||
font.letterSpacing: -0.2
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 86
|
||||
Layout.preferredHeight: 86
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
Layout.topMargin: Style.current.bigPadding
|
||||
StatusSmartIdenticon {
|
||||
id: userImage
|
||||
objectName: "welcomeScreenUserProfileImage"
|
||||
anchors.left: parent.left
|
||||
asset.width: 86
|
||||
asset.height: 86
|
||||
asset.letterSize: 32
|
||||
asset.color: Utils.colorForPubkey(d.publicKey)
|
||||
asset.charactersLen: 2
|
||||
asset.isImage: !!asset.name
|
||||
asset.imgIsIdenticon: false
|
||||
asset.name: d.image
|
||||
name: d.displayName
|
||||
ringSettings {
|
||||
ringSpecModel: Utils.getColorHashAsJson(d.publicKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: chatKeyTxt
|
||||
objectName: "profileChatKeyViewChatKeyTxt"
|
||||
Layout.preferredHeight: 22
|
||||
color: Style.current.secondaryText
|
||||
text: qsTr("Chatkey:") + " " + Utils.getCompressedPk(d.publicKey)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
||||
Layout.topMargin: 13
|
||||
font.pixelSize: 15
|
||||
visible: true
|
||||
}
|
||||
|
||||
Item {
|
||||
id: chainsChatKeyImg
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
||||
Layout.topMargin: Style.current.padding
|
||||
Layout.preferredWidth: 215
|
||||
Layout.preferredHeight: 77
|
||||
|
||||
Image {
|
||||
id: imgChains
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: Style.svg("onboarding/chains")
|
||||
cache: false
|
||||
}
|
||||
EmojiHash {
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
}
|
||||
publicKey: d.publicKey
|
||||
objectName: "publicKeyEmojiHash"
|
||||
}
|
||||
StatusSmartIdenticon {
|
||||
id: userImageCopy
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
rightMargin: 25
|
||||
}
|
||||
asset.width: 44
|
||||
asset.height: 44
|
||||
asset.color: "transparent"
|
||||
ringSettings { ringSpecModel: Utils.getColorHashAsJson(d.publicKey) }
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
id: nextBtn
|
||||
objectName: "onboardingDetailsViewNextButton"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.weight: Font.Medium
|
||||
text: root.startupStore.notificationsNeedsEnable ? qsTr("Next") : qsTr("Start using Status")
|
||||
onClicked: {
|
||||
d.doAction()
|
||||
}
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
event.accepted = true
|
||||
d.doAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,11 @@ Item {
|
|||
|
||||
property int counter: Constants.onboarding.profileFetching.timeout
|
||||
|
||||
// NOTE: This block can be removed when fetching backup is optimized
|
||||
// https://github.com/status-im/status-go/issues/5022
|
||||
readonly property bool minimumWaitingTimePassed: (Constants.onboarding.profileFetching.timeout - counter) > 30000
|
||||
readonly property bool fetching: root.startupStore.currentStartupState.stateType === Constants.startupState.profileFetching || !d.minimumWaitingTimePassed
|
||||
|
||||
readonly property string fetchingDataText: qsTr("Fetching data...")
|
||||
readonly property string continueText: qsTr("Continue")
|
||||
}
|
||||
|
@ -147,12 +152,12 @@ Item {
|
|||
id: button
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
focus: true
|
||||
enabled: root.state !== Constants.startupState.profileFetching
|
||||
enabled: !d.fetching
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 1000
|
||||
running: root.state === Constants.startupState.profileFetching
|
||||
running: d.fetching
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
d.counter = d.counter - 1000 // decrease 1000 ms
|
||||
|
@ -176,7 +181,7 @@ Item {
|
|||
states: [
|
||||
State {
|
||||
name: Constants.startupState.profileFetching
|
||||
when: root.startupStore.currentStartupState.stateType === Constants.startupState.profileFetching
|
||||
when: d.fetching
|
||||
PropertyChanges {
|
||||
target: title
|
||||
text: d.fetchingDataText
|
||||
|
|
|
@ -174,7 +174,7 @@ Item {
|
|||
StatusButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
enabled: d.allEntriesValid
|
||||
text: qsTr("Finish")
|
||||
text: qsTr("Next")
|
||||
onClicked: {
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 294ee4bf49df10af5c877cfdc4d36a6688c0f0ca
|
||||
Subproject commit d38b1147f8b4d0c4abdb1dbf353aff36454e02ed
|
|
@ -1 +1 @@
|
|||
Subproject commit 78db9054fc4fac565f5fb6cdd711d22e5870d0a1
|
||||
Subproject commit 68bc9d39fd9e42e75448d1536e2825a0fbc3d19a
|
Loading…
Reference in New Issue