fix(@desktop/onboarding): Onboarding/Login flow improvements
- startup, login and onboarding modules merged into the single one - `State` class introduced which is the base class for all states, every state determines what is the next state in each of 3 possible actions, and what is the previous state, if it has previous state - `StateWrapper` class is introduced as a convenient way to expose `State`'s props and deal with them on the qml side - startup module maintains states as a linked list and there are few convenient methods to move through the list `onBackActionClicked`, `onNextPrimaryActionClicked` `onNextSecondaryActionClicked`, `onNextTertiaryActionClicked` - redundant code removed Fixes: #6473
This commit is contained in:
parent
1178b4f8c4
commit
f9244e2c9f
|
@ -188,7 +188,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
|
|||
statusFoundation.events,
|
||||
result.keychainService,
|
||||
result.accountsService,
|
||||
result.generalService
|
||||
result.generalService,
|
||||
result.profileService
|
||||
)
|
||||
result.mainModule = main_module.newModule[AppController](
|
||||
result,
|
||||
|
|
|
@ -4,9 +4,9 @@ import ./utils/qrcodegen
|
|||
|
||||
# Services as instances shouldn't be used in this class, just some general/global procs
|
||||
import ../../app_service/common/conversion
|
||||
import ../../app_service/service/accounts/service as procs_from_accounts
|
||||
import ../../app_service/service/visual_identity/service as procs_from_visual_identity_service
|
||||
|
||||
include ../../app_service/service/accounts/utils
|
||||
|
||||
QtObject:
|
||||
type Utils* = ref object of QObject
|
||||
|
@ -28,7 +28,7 @@ QtObject:
|
|||
result.removePrefix('/')
|
||||
|
||||
proc isAlias*(self: Utils, value: string): bool {.slot.} =
|
||||
result = procs_from_accounts.isAlias(value)
|
||||
result = isAlias(value)
|
||||
|
||||
proc urlFromUserInput*(self: Utils, input: string): string {.slot.} =
|
||||
result = url_fromUserInput(input)
|
||||
|
@ -71,7 +71,7 @@ QtObject:
|
|||
return $stint.fromHex(StUint[256], value)
|
||||
|
||||
proc generateAlias*(self: Utils, pk: string): string {.slot.} =
|
||||
return procs_from_accounts.generateAliasFromPk(pk)
|
||||
return generateAliasFromPk(pk)
|
||||
|
||||
proc getFileSize*(self: Utils, filename: string): string {.slot.} =
|
||||
var f: File = nil
|
||||
|
@ -146,7 +146,7 @@ QtObject:
|
|||
int(procs_from_visual_identity_service.colorIdOf(publicKey))
|
||||
|
||||
proc getCompressedPk*(self: Utils, publicKey: string): string {.slot.} =
|
||||
procs_from_accounts.compressPk(publicKey)
|
||||
compressPk(publicKey)
|
||||
|
||||
proc isCompressedPubKey*(self: Utils, publicKey: string): bool {.slot.} =
|
||||
conversion.isCompressedPubKey(publicKey)
|
||||
|
|
|
@ -85,15 +85,11 @@ proc init*(self: Controller) =
|
|||
self.events.on(SIGNAL_MAILSERVER_NOT_WORKING) do(e: Args):
|
||||
self.delegate.emitMailserverNotWorking()
|
||||
|
||||
if(defined(macosx)):
|
||||
let account = self.accountsService.getLoggedInAccount()
|
||||
singletonInstance.localAccountSettings.setFileName(account.name)
|
||||
|
||||
self.events.on("keychainServiceSuccess") do(e:Args):
|
||||
self.events.on(SIGNAL_KEYCHAIN_SERVICE_SUCCESS) do(e:Args):
|
||||
let args = KeyChainServiceArg(e)
|
||||
self.delegate.emitStoringPasswordSuccess()
|
||||
|
||||
self.events.on("keychainServiceError") do(e:Args):
|
||||
self.events.on(SIGNAL_KEYCHAIN_SERVICE_ERROR) do(e:Args):
|
||||
let args = KeyChainServiceArg(e)
|
||||
singletonInstance.localAccountSettings.removeKey(LS_KEY_STORE_TO_KEYCHAIN)
|
||||
self.delegate.emitStoringPasswordError(args.errDescription)
|
||||
|
|
|
@ -2,28 +2,52 @@ import chronicles
|
|||
|
||||
import io_interface
|
||||
|
||||
import ../../global/global_singleton
|
||||
import ../../core/signals/types
|
||||
import ../../core/eventemitter
|
||||
import ../../../app_service/service/general/service as general_service
|
||||
import ../../../app_service/service/accounts/service as accounts_service
|
||||
|
||||
import ../../../app_service/service/keychain/service as keychain_service
|
||||
import ../../../app_service/service/profile/service as profile_service
|
||||
|
||||
logScope:
|
||||
topics = "startup-controller"
|
||||
|
||||
type ProfileImageDetails = object
|
||||
url*: string
|
||||
x1*: int
|
||||
y1*: int
|
||||
x2*: int
|
||||
y2*: int
|
||||
|
||||
type
|
||||
Controller* = ref object of RootObj
|
||||
delegate: io_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
generalService: general_service.Service
|
||||
accountsService: accounts_service.Service
|
||||
keychainService: keychain_service.Service
|
||||
profileService: profile_service.Service
|
||||
tmpProfileImageDetails: ProfileImageDetails
|
||||
tmpDisplayName: string
|
||||
tmpPassword: string
|
||||
tmpMnemonic: string
|
||||
tmpSelectedLoginAccountKeyUid: string
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
accountsService: accounts_service.Service):
|
||||
generalService: general_service.Service,
|
||||
accountsService: accounts_service.Service,
|
||||
keychainService: keychain_service.Service,
|
||||
profileService: profile_service.Service):
|
||||
Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.generalService = generalService
|
||||
result.accountsService = accountsService
|
||||
result.keychainService = keychainService
|
||||
result.profileService = profileService
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
discard
|
||||
|
@ -31,10 +55,7 @@ proc delete*(self: Controller) =
|
|||
proc init*(self: Controller) =
|
||||
self.events.on(SignalType.NodeLogin.event) do(e:Args):
|
||||
let signal = NodeSignal(e)
|
||||
if signal.event.error == "":
|
||||
self.delegate.userLoggedIn()
|
||||
else:
|
||||
error "error: ", methodName="init", errDesription = "login error " & signal.event.error
|
||||
self.delegate.onNodeLogin(signal.event.error)
|
||||
|
||||
self.events.on(SignalType.NodeStopped.event) do(e:Args):
|
||||
self.events.emit("nodeStopped", Args())
|
||||
|
@ -44,5 +65,136 @@ proc init*(self: Controller) =
|
|||
self.events.on(SignalType.NodeReady.event) do(e:Args):
|
||||
self.events.emit("nodeReady", Args())
|
||||
|
||||
self.events.on(SIGNAL_KEYCHAIN_SERVICE_SUCCESS) do(e:Args):
|
||||
let args = KeyChainServiceArg(e)
|
||||
self.delegate.emitObtainingPasswordSuccess(args.data)
|
||||
|
||||
self.events.on(SIGNAL_KEYCHAIN_SERVICE_ERROR) do(e:Args):
|
||||
let args = KeyChainServiceArg(e)
|
||||
# We are notifying user only about keychain errors.
|
||||
if (args.errType == ERROR_TYPE_AUTHENTICATION):
|
||||
return
|
||||
singletonInstance.localAccountSettings.removeKey(LS_KEY_STORE_TO_KEYCHAIN)
|
||||
self.delegate.emitObtainingPasswordError(args.errDescription)
|
||||
|
||||
proc shouldStartWithOnboardingScreen*(self: Controller): bool =
|
||||
return self.accountsService.openedAccounts().len == 0
|
||||
|
||||
proc getGeneratedAccounts*(self: Controller): seq[GeneratedAccountDto] =
|
||||
return self.accountsService.generatedAccounts()
|
||||
|
||||
proc getImportedAccount*(self: Controller): GeneratedAccountDto =
|
||||
return self.accountsService.getImportedAccount()
|
||||
|
||||
proc generateImages*(self: Controller, image: string, aX: int, aY: int, bX: int, bY: int): seq[general_service.Image] =
|
||||
return self.generalService.generateImages(image, aX, aY, bX, bY)
|
||||
|
||||
proc getPasswordStrengthScore*(self: Controller, password, userName: string): int =
|
||||
return self.generalService.getPasswordStrengthScore(password, userName)
|
||||
|
||||
proc generateImage*(self: Controller, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string =
|
||||
let formatedImg = singletonInstance.utils.formatImagePath(imageUrl)
|
||||
let images = self.generateImages(formatedImg, aX, aY, bX, bY)
|
||||
if(images.len == 0):
|
||||
return
|
||||
for img in images:
|
||||
if(img.imgType == "large"):
|
||||
self.tmpProfileImageDetails = ProfileImageDetails(url: imageUrl, x1: aX, y1: aY, x2: bX, y2: bY)
|
||||
return img.uri
|
||||
|
||||
proc setDisplayName*(self: Controller, value: string) =
|
||||
self.tmpDisplayName = value
|
||||
|
||||
proc getDisplayName*(self: Controller): string =
|
||||
return self.tmpDisplayName
|
||||
|
||||
proc setPassword*(self: Controller, value: string) =
|
||||
self.tmpPassword = value
|
||||
|
||||
proc getPassword*(self: Controller): string =
|
||||
return self.tmpPassword
|
||||
|
||||
proc storePasswordToKeychain(self: Controller) =
|
||||
let account = self.accountsService.getLoggedInAccount()
|
||||
let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
|
||||
if (value != LS_VALUE_STORE or account.name.len == 0):
|
||||
return
|
||||
self.keychainService.storePassword(account.name, self.tmpPassword)
|
||||
|
||||
proc storeIdentityImage*(self: Controller) =
|
||||
if self.tmpProfileImageDetails.url.len == 0:
|
||||
return
|
||||
let account = self.accountsService.getLoggedInAccount()
|
||||
let image = singletonInstance.utils.formatImagePath(self.tmpProfileImageDetails.url)
|
||||
self.profileService.storeIdentityImage(account.keyUid, image, self.tmpProfileImageDetails.x1,
|
||||
self.tmpProfileImageDetails.y1, self.tmpProfileImageDetails.x2, self.tmpProfileImageDetails.y2)
|
||||
self.tmpProfileImageDetails = ProfileImageDetails()
|
||||
|
||||
proc setupAccount(self: Controller, accountId: string, storeToKeychain: bool) =
|
||||
let error = self.accountsService.setupAccount(accountId, self.tmpPassword, self.tmpDisplayName)
|
||||
if error != "":
|
||||
self.delegate.setupAccountError(error)
|
||||
else:
|
||||
if storeToKeychain:
|
||||
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_STORE)
|
||||
self.storePasswordToKeychain()
|
||||
else:
|
||||
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER)
|
||||
self.setPassword("")
|
||||
self.setDisplayName("")
|
||||
|
||||
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, storeToKeychain)
|
||||
|
||||
proc storeImportedAccountAndLogin*(self: Controller, storeToKeychain: bool) =
|
||||
let accountId = self.getImportedAccount().id
|
||||
self.setupAccount(accountId, storeToKeychain)
|
||||
|
||||
proc validMnemonic*(self: Controller, mnemonic: string): bool =
|
||||
let err = self.accountsService.validateMnemonic(mnemonic)
|
||||
if err.len == 0:
|
||||
self.tmpMnemonic = mnemonic
|
||||
return true
|
||||
return false
|
||||
|
||||
proc importMnemonic*(self: Controller): bool =
|
||||
let error = self.accountsService.importMnemonic(self.tmpMnemonic)
|
||||
if(error.len == 0):
|
||||
self.delegate.importAccountSuccess()
|
||||
return true
|
||||
else:
|
||||
self.delegate.importAccountError(error)
|
||||
return false
|
||||
|
||||
proc getOpenedAccounts*(self: Controller): seq[AccountDto] =
|
||||
return self.accountsService.openedAccounts()
|
||||
|
||||
proc getSelectedLoginAccount(self: Controller): AccountDto =
|
||||
let openedAccounts = self.getOpenedAccounts()
|
||||
for acc in openedAccounts:
|
||||
if(acc.keyUid == self.tmpSelectedLoginAccountKeyUid):
|
||||
return acc
|
||||
|
||||
proc setSelectedLoginAccountKeyUid*(self: Controller, keyUid: string) =
|
||||
self.tmpSelectedLoginAccountKeyUid = keyUid
|
||||
# Dealing with Keychain is the MacOS only feature
|
||||
if(not defined(macosx)):
|
||||
return
|
||||
let selectedAccount = self.getSelectedLoginAccount()
|
||||
singletonInstance.localAccountSettings.setFileName(selectedAccount.name)
|
||||
let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
|
||||
if (value != LS_VALUE_STORE):
|
||||
return
|
||||
self.keychainService.tryToObtainPassword(selectedAccount.name)
|
||||
|
||||
proc login*(self: Controller) =
|
||||
let selectedAccount = self.getSelectedLoginAccount()
|
||||
let error = self.accountsService.login(selectedAccount, self.tmpPassword)
|
||||
self.setPassword("")
|
||||
if(error.len > 0):
|
||||
self.delegate.emitAccountLoginError(error)
|
|
@ -0,0 +1,38 @@
|
|||
import state
|
||||
import ../controller
|
||||
|
||||
type
|
||||
BiometricsState* = ref object of State
|
||||
|
||||
proc newBiometricsState*(flowType: FlowType, backState: State): BiometricsState =
|
||||
result = BiometricsState()
|
||||
result.setup(flowType, StateType.Biometrics, backState)
|
||||
|
||||
proc delete*(self: BiometricsState) =
|
||||
self.State.delete
|
||||
|
||||
method moveToNextPrimaryState*(self: BiometricsState): bool =
|
||||
return false
|
||||
|
||||
method moveToNextSecondaryState*(self: BiometricsState): bool =
|
||||
return false
|
||||
|
||||
method executePrimaryCommand*(self: BiometricsState, controller: Controller) =
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
||||
controller.storeGeneratedAccountAndLogin(storeToKeychain = true) # true, cause we have support for keychain for mac os
|
||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||
controller.storeImportedAccountAndLogin(storeToKeychain = true) # true, cause we have support for keychain for mac os
|
||||
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 = true) # true, cause we have support for keychain for mac os
|
||||
|
||||
method executeSecondaryCommand*(self: BiometricsState, controller: Controller) =
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
||||
controller.storeGeneratedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||
controller.storeImportedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
||||
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 = false) # false, cause we don't have keychain support for other than mac os
|
|
@ -0,0 +1,25 @@
|
|||
import state
|
||||
import ../controller
|
||||
import welcome_state_new_user, welcome_state_old_user
|
||||
|
||||
type
|
||||
LoginState* = ref object of State
|
||||
|
||||
proc newLoginState*(flowType: FlowType, backState: State): LoginState =
|
||||
result = LoginState()
|
||||
result.setup(flowType, StateType.Login, backState)
|
||||
|
||||
proc delete*(self: LoginState) =
|
||||
self.State.delete
|
||||
|
||||
method moveToNextPrimaryState*(self: LoginState): bool =
|
||||
return false
|
||||
|
||||
method executePrimaryCommand*(self: LoginState, controller: Controller) =
|
||||
controller.login()
|
||||
|
||||
method getNextSecondaryState*(self: LoginState): State =
|
||||
return newWelcomeStateNewUser(FlowType.General, self)
|
||||
|
||||
method getNextTertiaryState*(self: LoginState): State =
|
||||
return newWelcomeStateOldUser(FlowType.General, self)
|
|
@ -0,0 +1,14 @@
|
|||
import state, welcome_state
|
||||
|
||||
type
|
||||
NotificationState* = ref object of State
|
||||
|
||||
proc newNotificationState*(flowType: FlowType, backState: State): NotificationState =
|
||||
result = NotificationState()
|
||||
result.setup(flowType, StateType.AllowNotifications, backState)
|
||||
|
||||
proc delete*(self: NotificationState) =
|
||||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: NotificationState): State =
|
||||
return newWelcomeState(FlowType.General, nil)
|
|
@ -0,0 +1,111 @@
|
|||
import ../controller
|
||||
|
||||
type FlowType* {.pure.} = enum
|
||||
General = "General"
|
||||
FirstRunNewUserNewKeys = "FirstRunNewUserNewKeys"
|
||||
FirstRunNewUserNewKeycardKeys = "FirstRunNewUserNewKeycardKeys"
|
||||
FirstRunNewUserImportSeedPhrase = "FirstRunNewUserImportSeedPhrase"
|
||||
FirstRunOldUserSyncCode = "FirstRunOldUserSyncCode"
|
||||
FirstRunOldUserKeycardImport = "FirstRunOldUserKeycardImport"
|
||||
FirstRunOldUserImportSeedPhrase = "FirstRunOldUserImportSeedPhrase"
|
||||
AppLogin = "AppLogin"
|
||||
|
||||
type StateType* {.pure.} = enum
|
||||
NoState = "NoState"
|
||||
AllowNotifications = "AllowNotifications"
|
||||
Welcome = "Welcome"
|
||||
WelcomeNewStatusUser = "WelcomeNewStatusUser"
|
||||
WelcomeOldStatusUser = "WelcomeOldStatusUser"
|
||||
UserProfileCreate = "UserProfileCreate"
|
||||
UserProfileChatKey = "UserProfileChatKey"
|
||||
UserProfileCreatePassword = "UserProfileCreatePassword"
|
||||
UserProfileConfirmPassword = "UserProfileConfirmPassword"
|
||||
UserProfileImportSeedPhrase = "UserProfileImportSeedPhrase"
|
||||
UserProfileEnterSeedPhrase = "UserProfileEnterSeedPhrase"
|
||||
Biometrics = "Biometrics"
|
||||
Login = "Login"
|
||||
|
||||
## This is the base class for all state we may have in onboarding/login flow.
|
||||
## We should not instance of this class (in c++ this will be an abstract class).
|
||||
## For now each `State` inherited instance supports up to 3 different actions (e.g. 3 buttons on the UI).
|
||||
type
|
||||
State* {.pure inheritable.} = ref object of RootObj
|
||||
flowType: FlowType
|
||||
stateType: StateType
|
||||
backState: State
|
||||
|
||||
proc setup*(self: State, flowType: FlowType, stateType: StateType, backState: State) =
|
||||
self.flowType = flowType
|
||||
self.stateType = stateType
|
||||
self.backState = backState
|
||||
|
||||
## `flowType` - detemines the flow this instance belongs to
|
||||
## `stateType` - detemines the state this instance describes
|
||||
## `backState` - the sate (instance) we're moving to if user clicks "back" button,
|
||||
## in case we should not display "back" button for this state, set it to `nil`
|
||||
proc newState*(self: State, flowType: FlowType, stateType: StateType, backState: State): State =
|
||||
result = State()
|
||||
result.setup(flowType, stateType, backState)
|
||||
|
||||
proc delete*(self: State) =
|
||||
discard
|
||||
|
||||
## Returns flow type
|
||||
method flowType*(self: State): FlowType {.inline base.} =
|
||||
self.flowType
|
||||
|
||||
## Returns state type
|
||||
method stateType*(self: State): StateType {.inline base.} =
|
||||
self.stateType
|
||||
|
||||
## Returns back state instance
|
||||
method getBackState*(self: State): State {.inline base.} =
|
||||
self.backState
|
||||
|
||||
## Returns true if we should display "back" button, otherwise false
|
||||
method displayBackButton*(self: State): bool {.inline base.} =
|
||||
return not self.backState.isNil
|
||||
|
||||
## Returns next state instance in case the "primary" action is triggered
|
||||
method getNextPrimaryState*(self: State): State {.inline base.} =
|
||||
return nil
|
||||
|
||||
## Returns next state instance in case the "secondary" action is triggered
|
||||
method getNextSecondaryState*(self: State): State {.inline base.} =
|
||||
return nil
|
||||
|
||||
## Returns next state instance in case the "tertiary" action is triggered
|
||||
method getNextTertiaryState*(self: State): State {.inline base.} =
|
||||
return nil
|
||||
|
||||
## This method is executed in case "back" button is clicked
|
||||
method executeBackCommand*(self: State, controller: Controller) {.inline base.} =
|
||||
discard
|
||||
|
||||
## This method is executed in case "primary" action is triggered
|
||||
method executePrimaryCommand*(self: State, controller: Controller) {.inline base.} =
|
||||
discard
|
||||
|
||||
## This method is executed in case "secondary" action is triggered
|
||||
method executeSecondaryCommand*(self: State, controller: Controller) {.inline base.} =
|
||||
discard
|
||||
|
||||
## This method is executed in case "tertiary" action is triggered
|
||||
method executeTertiaryCommand*(self: State, controller: Controller) {.inline base.} =
|
||||
discard
|
||||
|
||||
## Returns true if we should move from this state immediatelly when the "primary" action is triggered,
|
||||
## in case we need to wait for some other action, or some aync event, this should return false
|
||||
method moveToNextPrimaryState*(self: State): bool {.inline base.} =
|
||||
return true
|
||||
|
||||
## Returns true if we should move from this state immediatelly when the "secondary" action is triggered,
|
||||
## in case we need to wait for some other action, or some aync event, this should return false
|
||||
method moveToNextSecondaryState*(self: State): bool {.inline base.} =
|
||||
return true
|
||||
|
||||
## Returns true if we should move from this state immediatelly when the "tertiary" action is triggered,
|
||||
## in case we need to wait for some other action, or some aync event, this should return false
|
||||
method moveToNextTertiaryState*(self: State): bool {.inline base.} =
|
||||
return true
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import NimQml
|
||||
import state
|
||||
|
||||
QtObject:
|
||||
type StateWrapper* = ref object of QObject
|
||||
stateObj: State
|
||||
|
||||
proc delete*(self: StateWrapper) =
|
||||
self.QObject.delete
|
||||
|
||||
proc newStateWrapper*(): StateWrapper =
|
||||
new(result, delete)
|
||||
result.QObject.setup()
|
||||
|
||||
proc stateWrapperChanged*(self:StateWrapper) {.signal.}
|
||||
|
||||
proc setStateObj*(self: StateWrapper, stateObj: State) =
|
||||
self.stateObj = stateObj
|
||||
self.stateWrapperChanged()
|
||||
|
||||
proc getStateObj*(self: StateWrapper): State =
|
||||
return self.stateObj
|
||||
|
||||
proc getFlowType(self: StateWrapper): string {.slot.} =
|
||||
if(self.stateObj.isNil):
|
||||
return $FlowType.General
|
||||
return $self.stateObj.flowType()
|
||||
QtProperty[string] flowType:
|
||||
read = getFlowType
|
||||
notify = stateWrapperChanged
|
||||
|
||||
proc getStateType(self: StateWrapper): string {.slot.} =
|
||||
if(self.stateObj.isNil):
|
||||
return $StateType.NoState
|
||||
return $self.stateObj.stateType()
|
||||
QtProperty[string] stateType:
|
||||
read = getStateType
|
||||
notify = stateWrapperChanged
|
||||
|
||||
proc getDisplayBackButton(self: StateWrapper): bool {.slot.} =
|
||||
if(self.stateObj.isNil):
|
||||
return false
|
||||
return self.stateObj.displayBackButton()
|
||||
QtProperty[bool] displayBackButton:
|
||||
read = getDisplayBackButton
|
||||
notify = stateWrapperChanged
|
||||
|
||||
proc backActionClicked*(self: StateWrapper) {.signal.}
|
||||
proc backAction*(self: StateWrapper) {.slot.} =
|
||||
self.backActionClicked()
|
||||
|
||||
proc primaryActionClicked*(self: StateWrapper) {.signal.}
|
||||
proc doPrimaryAction*(self: StateWrapper) {.slot.} =
|
||||
self.primaryActionClicked()
|
||||
|
||||
proc secondaryActionClicked*(self: StateWrapper) {.signal.}
|
||||
proc doSecondaryAction*(self: StateWrapper) {.slot.} =
|
||||
self.secondaryActionClicked()
|
||||
|
||||
proc tertiaryActionClicked*(self: StateWrapper) {.signal.}
|
||||
proc doTertiaryAction*(self: StateWrapper) {.slot.} =
|
||||
self.tertiaryActionClicked()
|
|
@ -0,0 +1,15 @@
|
|||
import state
|
||||
import user_profile_create_password_state
|
||||
|
||||
type
|
||||
UserProfileChatKeyState* = ref object of State
|
||||
|
||||
proc newUserProfileChatKeyState*(flowType: FlowType, backState: State): UserProfileChatKeyState =
|
||||
result = UserProfileChatKeyState()
|
||||
result.setup(flowType, StateType.UserProfileChatKey, backState)
|
||||
|
||||
proc delete*(self: UserProfileChatKeyState) =
|
||||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: UserProfileChatKeyState): State =
|
||||
return newUserProfileCreatePasswordState(self.State.flowType, self)
|
|
@ -0,0 +1,34 @@
|
|||
import state
|
||||
import biometrics_state
|
||||
import ../controller
|
||||
|
||||
type
|
||||
UserProfileConfirmPasswordState* = ref object of State
|
||||
|
||||
proc newUserProfileConfirmPasswordState*(flowType: FlowType, backState: State): UserProfileConfirmPasswordState =
|
||||
result = UserProfileConfirmPasswordState()
|
||||
result.setup(flowType, StateType.UserProfileConfirmPassword, backState)
|
||||
|
||||
proc delete*(self: UserProfileConfirmPasswordState) =
|
||||
self.State.delete
|
||||
|
||||
method moveToNextPrimaryState*(self: UserProfileConfirmPasswordState): bool =
|
||||
return defined(macosx)
|
||||
|
||||
method getNextPrimaryState*(self: UserProfileConfirmPasswordState): State =
|
||||
if not self.moveToNextPrimaryState():
|
||||
return nil
|
||||
return newBiometricsState(self.State.flowType, nil)
|
||||
|
||||
method executePrimaryCommand*(self: UserProfileConfirmPasswordState, controller: Controller) =
|
||||
if self.moveToNextPrimaryState():
|
||||
return
|
||||
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
||||
controller.storeGeneratedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||
controller.storeImportedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
||||
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 = false) # false, cause we don't have keychain support for other than mac os
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import state
|
||||
import ../controller
|
||||
import user_profile_confirm_password_state
|
||||
|
||||
type
|
||||
UserProfileCreatePasswordState* = ref object of State
|
||||
|
||||
proc newUserProfileCreatePasswordState*(flowType: FlowType, backState: State): UserProfileCreatePasswordState =
|
||||
result = UserProfileCreatePasswordState()
|
||||
result.setup(flowType, StateType.UserProfileCreatePassword, backState)
|
||||
|
||||
proc delete*(self: UserProfileCreatePasswordState) =
|
||||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: UserProfileCreatePasswordState): State =
|
||||
return newUserProfileConfirmPasswordState(self.State.flowType, self)
|
||||
|
||||
method executeBackCommand*(self: UserProfileCreatePasswordState, controller: Controller) =
|
||||
controller.setPassword("")
|
|
@ -0,0 +1,19 @@
|
|||
import state
|
||||
import ../controller
|
||||
import user_profile_chat_key_state
|
||||
|
||||
type
|
||||
UserProfileCreateState* = ref object of State
|
||||
|
||||
proc newUserProfileCreateState*(flowType: FlowType, backState: State): UserProfileCreateState =
|
||||
result = UserProfileCreateState()
|
||||
result.setup(flowType, StateType.UserProfileCreate, backState)
|
||||
|
||||
proc delete*(self: UserProfileCreateState) =
|
||||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: UserProfileCreateState): State =
|
||||
return newUserProfileChatKeyState(self.State.flowType, self)
|
||||
|
||||
method executeBackCommand*(self: UserProfileCreateState, controller: Controller) =
|
||||
controller.setDisplayName("")
|
|
@ -0,0 +1,26 @@
|
|||
import state
|
||||
import ../controller
|
||||
import user_profile_create_state
|
||||
|
||||
type
|
||||
UserProfileEnterSeedPhraseState* = ref object of State
|
||||
successfulImport: bool
|
||||
|
||||
proc newUserProfileEnterSeedPhraseState*(flowType: FlowType, backState: State): UserProfileEnterSeedPhraseState =
|
||||
result = UserProfileEnterSeedPhraseState()
|
||||
result.setup(flowType, StateType.UserProfileEnterSeedPhrase, backState)
|
||||
result.successfulImport = false
|
||||
|
||||
proc delete*(self: UserProfileEnterSeedPhraseState) =
|
||||
self.State.delete
|
||||
|
||||
method moveToNextPrimaryState*(self: UserProfileEnterSeedPhraseState): bool =
|
||||
return self.successfulImport
|
||||
|
||||
method getNextPrimaryState*(self: UserProfileEnterSeedPhraseState): State =
|
||||
if not self.moveToNextPrimaryState():
|
||||
return nil
|
||||
return newUserProfileCreateState(self.State.flowType, self)
|
||||
|
||||
method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller: Controller) =
|
||||
self.successfulImport = controller.importMnemonic()
|
|
@ -0,0 +1,15 @@
|
|||
import state
|
||||
import user_profile_enter_seed_phrase_state
|
||||
|
||||
type
|
||||
UserProfileImportSeedPhraseState* = ref object of State
|
||||
|
||||
proc newUserProfileImportSeedPhraseState*(flowType: FlowType, backState: State): UserProfileImportSeedPhraseState =
|
||||
result = UserProfileImportSeedPhraseState()
|
||||
result.setup(flowType, StateType.UserProfileImportSeedPhrase, backState)
|
||||
|
||||
proc delete*(self: UserProfileImportSeedPhraseState) =
|
||||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: UserProfileImportSeedPhraseState): State =
|
||||
return newUserProfileEnterSeedPhraseState(self.State.flowType, self)
|
|
@ -0,0 +1,18 @@
|
|||
import state
|
||||
import welcome_state_new_user, welcome_state_old_user
|
||||
|
||||
type
|
||||
WelcomeState* = ref object of State
|
||||
|
||||
proc newWelcomeState*(flowType: FlowType, backState: State): WelcomeState =
|
||||
result = WelcomeState()
|
||||
result.setup(flowType, StateType.Welcome, backState)
|
||||
|
||||
proc delete*(self: WelcomeState) =
|
||||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: WelcomeState): State =
|
||||
return newWelcomeStateNewUser(FlowType.General, self)
|
||||
|
||||
method getNextSecondaryState*(self: WelcomeState): State =
|
||||
return newWelcomeStateOldUser(FlowType.General, self)
|
|
@ -0,0 +1,24 @@
|
|||
import state
|
||||
import user_profile_create_state, user_profile_import_seed_phrase_state
|
||||
|
||||
type
|
||||
WelcomeStateNewUser* = ref object of State
|
||||
|
||||
proc newWelcomeStateNewUser*(flowType: FlowType, backState: State): WelcomeStateNewUser =
|
||||
result = WelcomeStateNewUser()
|
||||
result.setup(flowType, StateType.WelcomeNewStatusUser, backState)
|
||||
|
||||
proc delete*(self: WelcomeStateNewUser) =
|
||||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: WelcomeStateNewUser): State =
|
||||
return newUserProfileCreateState(FlowType.FirstRunNewUserNewKeys, self)
|
||||
|
||||
method getNextSecondaryState*(self: WelcomeStateNewUser): State =
|
||||
# We will handle here a click on `Generate keys for a new Keycard`
|
||||
discard
|
||||
|
||||
method getNextTertiaryState*(self: WelcomeStateNewUser): State =
|
||||
return newUserProfileImportSeedPhraseState(FlowType.FirstRunNewUserImportSeedPhrase, self)
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import state
|
||||
import user_profile_enter_seed_phrase_state
|
||||
|
||||
type
|
||||
WelcomeStateOldUser* = ref object of State
|
||||
|
||||
proc newWelcomeStateOldUser*(flowType: FlowType, backState: State): WelcomeStateOldUser =
|
||||
result = WelcomeStateOldUser()
|
||||
result.setup(flowType, StateType.WelcomeOldStatusUser, backState)
|
||||
|
||||
proc delete*(self: WelcomeStateOldUser) =
|
||||
self.State.delete
|
||||
|
||||
method getNextPrimaryState*(self: WelcomeStateOldUser): State =
|
||||
# We will handle here a click on `Scan sync code`
|
||||
discard
|
||||
|
||||
method getNextSecondaryState*(self: WelcomeStateOldUser): State =
|
||||
# We will handle here a click on `Login with Keycard`
|
||||
discard
|
||||
|
||||
method getNextTertiaryState*(self: WelcomeStateOldUser): State =
|
||||
## This is added as next state in case of import seed for an old user, but this doesn't match the flow
|
||||
## in the design. Need to be fixed correctly.
|
||||
## Why it's not fixed now???
|
||||
## -> Cause this is just a improving and moving to a better form what we currently have, fixing will be done in another issue
|
||||
## and need to be discussed as we haven't had that flow implemented ever before
|
||||
return newUserProfileEnterSeedPhraseState(FlowType.FirstRunOldUserImportSeedPhrase, self)
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
import ../../../app_service/service/accounts/service
|
||||
import models/login_account_item as login_acc_item
|
||||
|
||||
type
|
||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||
|
||||
|
@ -10,25 +13,71 @@ method load*(self: AccessInterface) {.base.} =
|
|||
method moveToAppState*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method startUpUIRaised*(self: AccessInterface) {.base.} =
|
||||
method onBackActionClicked*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onPrimaryActionClicked*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method userLoggedIn*(self: AccessInterface) {.base.} =
|
||||
method onSecondaryActionClicked*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onTertiaryActionClicked*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method startUpUIRaised*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method emitLogOut*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method loginDidLoad*(self: AccessInterface) {.base.} =
|
||||
method getImportedAccount*(self: AccessInterface): GeneratedAccountDto {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onboardingDidLoad*(self: AccessInterface) {.base.} =
|
||||
method generateImage*(self: AccessInterface, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||
method setDisplayName*(self: AccessInterface, value: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getDisplayName*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setPassword*(self: AccessInterface, value: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getPassword*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getPasswordStrengthScore*(self: AccessInterface, password: string, userName: string): int {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setupAccountError*(self: AccessInterface, error: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method validMnemonic*(self: AccessInterface, mnemonic: string): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method importAccountError*(self: AccessInterface, error: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method importAccountSuccess*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setSelectedLoginAccount*(self: AccessInterface, item: login_acc_item.Item) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method onNodeLogin*(self: AccessInterface, error: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method emitAccountLoginError*(self: AccessInterface, error: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method emitObtainingPasswordError*(self: AccessInterface, errorDescription: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method emitObtainingPasswordSuccess*(self: AccessInterface, password: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
# This way (using concepts) is used only for the modules managed by AppController
|
||||
type
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
import NimQml, Tables
|
||||
|
||||
import io_interface
|
||||
import ../../../global/global_singleton
|
||||
import ../../../core/signals/types
|
||||
import ../../../core/eventemitter
|
||||
import ../../../../app_service/service/keychain/service as keychain_service
|
||||
import ../../../../app_service/service/accounts/service as accounts_service
|
||||
|
||||
type
|
||||
Controller* = ref object of RootObj
|
||||
delegate: io_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
keychainService: keychain_service.Service
|
||||
accountsService: accounts_service.Service
|
||||
selectedAccountKeyUid: string
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
keychainService: keychain_service.Service,
|
||||
accountsService: accounts_service.Service):
|
||||
Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.keychainService = keychainService
|
||||
result.accountsService = accountsService
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
discard
|
||||
|
||||
proc init*(self: Controller) =
|
||||
self.events.on(SignalType.NodeLogin.event) do(e:Args):
|
||||
let signal = NodeSignal(e)
|
||||
if signal.event.error != "":
|
||||
self.delegate.emitAccountLoginError(signal.event.error)
|
||||
|
||||
self.events.on("keychainServiceSuccess") do(e:Args):
|
||||
let args = KeyChainServiceArg(e)
|
||||
self.delegate.emitObtainingPasswordSuccess(args.data)
|
||||
|
||||
self.events.on("keychainServiceError") do(e:Args):
|
||||
let args = KeyChainServiceArg(e)
|
||||
# We are notifying user only about keychain errors.
|
||||
if (args.errType == ERROR_TYPE_AUTHENTICATION):
|
||||
return
|
||||
|
||||
singletonInstance.localAccountSettings.removeKey(LS_KEY_STORE_TO_KEYCHAIN)
|
||||
self.delegate.emitObtainingPasswordError(args.errDescription)
|
||||
|
||||
proc getOpenedAccounts*(self: Controller): seq[AccountDto] =
|
||||
return self.accountsService.openedAccounts()
|
||||
|
||||
proc getSelectedAccount(self: Controller): AccountDto =
|
||||
let openedAccounts = self.getOpenedAccounts()
|
||||
for acc in openedAccounts:
|
||||
if(acc.keyUid == self.selectedAccountKeyUid):
|
||||
return acc
|
||||
|
||||
proc setSelectedAccountKeyUid*(self: Controller, keyUid: string) =
|
||||
self.selectedAccountKeyUid = keyUid
|
||||
|
||||
# Dealing with Keychain is the MacOS only feature
|
||||
if(not defined(macosx)):
|
||||
return
|
||||
|
||||
let selectedAccount = self.getSelectedAccount()
|
||||
singletonInstance.localAccountSettings.setFileName(selectedAccount.name)
|
||||
|
||||
let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
|
||||
if (value != LS_VALUE_STORE):
|
||||
return
|
||||
|
||||
self.keychainService.tryToObtainPassword(selectedAccount.name)
|
||||
|
||||
proc login*(self: Controller, password: string) =
|
||||
let selectedAccount = self.getSelectedAccount()
|
||||
|
||||
let error = self.accountsService.login(selectedAccount, password)
|
||||
if(error.len > 0):
|
||||
self.delegate.emitAccountLoginError(error)
|
|
@ -1,33 +0,0 @@
|
|||
import item
|
||||
|
||||
type
|
||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||
|
||||
method delete*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method load*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method isLoaded*(self: AccessInterface): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method emitAccountLoginError*(self: AccessInterface, error: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method emitObtainingPasswordError*(self: AccessInterface, errorDescription: string)
|
||||
{.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method emitObtainingPasswordSuccess*(self: AccessInterface, password: string)
|
||||
{.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setSelectedAccount*(self: AccessInterface, item: Item) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method login*(self: AccessInterface, password: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -1,88 +0,0 @@
|
|||
import NimQml
|
||||
import io_interface
|
||||
import ../io_interface as delegate_interface
|
||||
import view, controller, item
|
||||
import ../../../global/global_singleton
|
||||
import ../../../core/eventemitter
|
||||
import ../../../../app_service/service/keychain/service as keychain_service
|
||||
import ../../../../app_service/service/accounts/service as accounts_service
|
||||
|
||||
export io_interface
|
||||
|
||||
type
|
||||
Module* = ref object of io_interface.AccessInterface
|
||||
delegate: delegate_interface.AccessInterface
|
||||
view: View
|
||||
viewVariant: QVariant
|
||||
controller: Controller
|
||||
moduleLoaded: bool
|
||||
|
||||
proc newModule*(delegate: delegate_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
keychainService: keychain_service.Service,
|
||||
accountsService: accounts_service.Service):
|
||||
Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.view = view.newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, events, keychainService,
|
||||
accountsService)
|
||||
result.moduleLoaded = false
|
||||
|
||||
method delete*(self: Module) =
|
||||
self.view.delete
|
||||
self.viewVariant.delete
|
||||
self.controller.delete
|
||||
|
||||
proc extractImages(self: Module, account: AccountDto, thumbnailImage: var string,
|
||||
largeImage: var string) =
|
||||
for img in account.images:
|
||||
if(img.imgType == "thumbnail"):
|
||||
thumbnailImage = img.uri
|
||||
elif(img.imgType == "large"):
|
||||
largeImage = img.uri
|
||||
|
||||
method load*(self: Module) =
|
||||
singletonInstance.engine.setRootContextProperty("loginModule", self.viewVariant)
|
||||
self.controller.init()
|
||||
self.view.load()
|
||||
|
||||
let openedAccounts = self.controller.getOpenedAccounts()
|
||||
if(openedAccounts.len > 0):
|
||||
var items: seq[Item]
|
||||
for acc in openedAccounts:
|
||||
var thumbnailImage: string
|
||||
var largeImage: string
|
||||
self.extractImages(acc, thumbnailImage, largeImage)
|
||||
items.add(initItem(acc.name, thumbnailImage, largeImage,
|
||||
acc.keyUid, acc.colorHash, acc.colorId))
|
||||
|
||||
self.view.setModelItems(items)
|
||||
|
||||
# set the first account as slected one
|
||||
self.controller.setSelectedAccountKeyUid(items[0].getKeyUid())
|
||||
self.setSelectedAccount(items[0])
|
||||
|
||||
method isLoaded*(self: Module): bool =
|
||||
return self.moduleLoaded
|
||||
|
||||
method viewDidLoad*(self: Module) =
|
||||
self.moduleLoaded = true
|
||||
self.delegate.loginDidLoad()
|
||||
|
||||
method setSelectedAccount*(self: Module, item: Item) =
|
||||
self.controller.setSelectedAccountKeyUid(item.getKeyUid())
|
||||
self.view.setSelectedAccount(item)
|
||||
|
||||
method login*(self: Module, password: string) =
|
||||
self.controller.login(password)
|
||||
|
||||
method emitAccountLoginError*(self: Module, error: string) =
|
||||
self.view.emitAccountLoginError(error)
|
||||
|
||||
method emitObtainingPasswordError*(self: Module, errorDescription: string) =
|
||||
self.view.emitObtainingPasswordError(errorDescription)
|
||||
|
||||
method emitObtainingPasswordSuccess*(self: Module, password: string) =
|
||||
self.view.emitObtainingPasswordSuccess(password)
|
|
@ -1,79 +0,0 @@
|
|||
import NimQml
|
||||
import model, item, selected_account
|
||||
import io_interface
|
||||
|
||||
QtObject:
|
||||
type
|
||||
View* = ref object of QObject
|
||||
delegate: io_interface.AccessInterface
|
||||
selectedAccount: SelectedAccount
|
||||
selectedAccountVariant: QVariant
|
||||
model: Model
|
||||
modelVariant: QVariant
|
||||
|
||||
proc delete*(self: View) =
|
||||
self.selectedAccount.delete
|
||||
self.selectedAccountVariant.delete
|
||||
self.model.delete
|
||||
self.modelVariant.delete
|
||||
self.QObject.delete
|
||||
|
||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||
new(result, delete)
|
||||
result.QObject.setup
|
||||
result.delegate = delegate
|
||||
result.selectedAccount = newSelectedAccount()
|
||||
result.selectedAccountVariant = newQVariant(result.selectedAccount)
|
||||
result.model = newModel()
|
||||
result.modelVariant = newQVariant(result.model)
|
||||
|
||||
proc load*(self: View) =
|
||||
self.delegate.viewDidLoad()
|
||||
|
||||
proc selectedAccountChanged*(self: View) {.signal.}
|
||||
|
||||
proc getSelectedAccount(self: View): QVariant {.slot.} =
|
||||
return self.selectedAccountVariant
|
||||
|
||||
proc setSelectedAccount*(self: View, item: Item) =
|
||||
self.selectedAccount.setSelectedAccountData(item)
|
||||
self.selectedAccountChanged()
|
||||
|
||||
proc setSelectedAccountByIndex*(self: View, index: int) {.slot.} =
|
||||
let item = self.model.getItemAtIndex(index)
|
||||
self.delegate.setSelectedAccount(item)
|
||||
|
||||
QtProperty[QVariant] selectedAccount:
|
||||
read = getSelectedAccount
|
||||
notify = selectedAccountChanged
|
||||
|
||||
proc modelChanged*(self: View) {.signal.}
|
||||
|
||||
proc getModel(self: View): QVariant {.slot.} =
|
||||
return self.modelVariant
|
||||
|
||||
proc setModelItems*(self: View, accounts: seq[Item]) =
|
||||
self.model.setItems(accounts)
|
||||
self.modelChanged()
|
||||
|
||||
QtProperty[QVariant] accountsModel:
|
||||
read = getModel
|
||||
notify = modelChanged
|
||||
|
||||
proc login*(self: View, password: string) {.slot.} =
|
||||
self.delegate.login(password)
|
||||
|
||||
proc accountLoginError*(self: View, error: string) {.signal.}
|
||||
|
||||
proc emitAccountLoginError*(self: View, error: string) =
|
||||
self.accountLoginError(error)
|
||||
|
||||
proc obtainingPasswordError*(self:View, errorDescription: string) {.signal.}
|
||||
|
||||
proc emitObtainingPasswordError*(self: View, errorDescription: string) =
|
||||
self.obtainingPasswordError(errorDescription)
|
||||
|
||||
proc obtainingPasswordSuccess*(self:View, password: string) {.signal.}
|
||||
|
||||
proc emitObtainingPasswordSuccess*(self: View, password: string) =
|
||||
self.obtainingPasswordSuccess(password)
|
|
@ -1,6 +1,6 @@
|
|||
import NimQml, Tables, strutils
|
||||
|
||||
import item
|
||||
import generated_account_item
|
||||
|
||||
type
|
||||
ModelRole {.pure.} = enum
|
|
@ -1,6 +1,6 @@
|
|||
import NimQml, Tables, strutils, strformat
|
||||
import NimQml, Tables, strutils
|
||||
|
||||
import item
|
||||
import login_account_item
|
||||
|
||||
type
|
||||
ModelRole {.pure.} = enum
|
|
@ -1,85 +1,90 @@
|
|||
import NimQml
|
||||
import NimQml, chronicles
|
||||
|
||||
import io_interface
|
||||
import view, controller
|
||||
import internal/[state, notification_state, welcome_state, login_state]
|
||||
import models/generated_account_item as gen_acc_item
|
||||
import models/login_account_item as login_acc_item
|
||||
import ../../global/global_singleton
|
||||
import ../../core/eventemitter
|
||||
import onboarding/module as onboarding_module
|
||||
import login/module as login_module
|
||||
|
||||
import ../../../app_service/service/keychain/service as keychain_service
|
||||
import ../../../app_service/service/accounts/service as accounts_service
|
||||
import ../../../app_service/service/general/service as general_service
|
||||
import ../../../app_service/service/profile/service as profile_service
|
||||
|
||||
export io_interface
|
||||
|
||||
logScope:
|
||||
topics = "startup-module"
|
||||
|
||||
type
|
||||
Module*[T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface
|
||||
delegate: T
|
||||
view: View
|
||||
viewVariant: QVariant
|
||||
controller: Controller
|
||||
onboardingModule: onboarding_module.AccessInterface
|
||||
loginModule: login_module.AccessInterface
|
||||
|
||||
proc newModule*[T](delegate: T,
|
||||
events: EventEmitter,
|
||||
keychainService: keychain_service.Service,
|
||||
accountsService: accounts_service.Service,
|
||||
generalService: general_service.Service):
|
||||
generalService: general_service.Service,
|
||||
profileService: profile_service.Service):
|
||||
Module[T] =
|
||||
result = Module[T]()
|
||||
result.delegate = delegate
|
||||
result.view = view.newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, events, accountsService)
|
||||
|
||||
# Submodules
|
||||
result.onboardingModule = onboarding_module.newModule(result, events, accountsService, generalService)
|
||||
result.loginModule = login_module.newModule(result, events, keychainService,
|
||||
accountsService)
|
||||
result.controller = controller.newController(result, events, generalService, accountsService, keychainService,
|
||||
profileService)
|
||||
|
||||
method delete*[T](self: Module[T]) =
|
||||
self.onboardingModule.delete
|
||||
self.loginModule.delete
|
||||
self.view.delete
|
||||
self.viewVariant.delete
|
||||
self.controller.delete
|
||||
|
||||
proc extractImages(self: Module, account: AccountDto, thumbnailImage: var string,
|
||||
largeImage: var string) =
|
||||
for img in account.images:
|
||||
if(img.imgType == "thumbnail"):
|
||||
thumbnailImage = img.uri
|
||||
elif(img.imgType == "large"):
|
||||
largeImage = img.uri
|
||||
|
||||
method load*[T](self: Module[T]) =
|
||||
singletonInstance.engine.setRootContextProperty("startupModule", self.viewVariant)
|
||||
self.controller.init()
|
||||
self.view.load()
|
||||
|
||||
var initialAppState = AppState.OnboardingState
|
||||
if(not self.controller.shouldStartWithOnboardingScreen()):
|
||||
initialAppState = AppState.LoginState
|
||||
self.view.setAppState(initialAppState)
|
||||
|
||||
self.onboardingModule.load()
|
||||
self.loginModule.load()
|
||||
|
||||
proc checkIfModuleDidLoad[T](self: Module[T]) =
|
||||
if(not self.onboardingModule.isLoaded()):
|
||||
return
|
||||
|
||||
if(not self.loginModule.isLoaded()):
|
||||
return
|
||||
let generatedAccounts = self.controller.getGeneratedAccounts()
|
||||
var accounts: seq[gen_acc_item.Item]
|
||||
for acc in generatedAccounts:
|
||||
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 defined(macosx):
|
||||
self.view.setCurrentStartupState(newNotificationState(FlowType.General, nil))
|
||||
else:
|
||||
self.view.setCurrentStartupState(newWelcomeState(FlowType.General, nil))
|
||||
else:
|
||||
let openedAccounts = self.controller.getOpenedAccounts()
|
||||
if(openedAccounts.len > 0):
|
||||
var items: seq[login_acc_item.Item]
|
||||
for acc in openedAccounts:
|
||||
var thumbnailImage: string
|
||||
var largeImage: string
|
||||
self.extractImages(acc, thumbnailImage, largeImage)
|
||||
items.add(login_acc_item.initItem(acc.name, thumbnailImage, largeImage, acc.keyUid, acc.colorHash, acc.colorId))
|
||||
self.view.setLoginAccountsModelItems(items)
|
||||
# set the first account as slected one
|
||||
if items.len == 0:
|
||||
error "cannot run the app in login flow cause list of login accounts is empty"
|
||||
quit() # quit the app
|
||||
self.setSelectedLoginAccount(items[0])
|
||||
self.view.setCurrentStartupState(newLoginState(FlowType.AppLogin, nil))
|
||||
self.delegate.startupDidLoad()
|
||||
|
||||
method viewDidLoad*[T](self: Module[T]) =
|
||||
self.checkIfModuleDidLoad()
|
||||
|
||||
method onboardingDidLoad*[T](self: Module[T]) =
|
||||
self.checkIfModuleDidLoad()
|
||||
|
||||
method loginDidLoad*[T](self: Module[T]) =
|
||||
self.checkIfModuleDidLoad()
|
||||
|
||||
method userLoggedIn*[T](self: Module[T]) =
|
||||
self.delegate.userLoggedIn()
|
||||
|
||||
method moveToAppState*[T](self: Module[T]) =
|
||||
self.view.setAppState(AppState.MainAppState)
|
||||
|
||||
|
@ -88,3 +93,98 @@ method startUpUIRaised*[T](self: Module[T]) =
|
|||
|
||||
method emitLogOut*[T](self: Module[T]) =
|
||||
self.view.emitLogOut()
|
||||
|
||||
method onBackActionClicked*[T](self: Module[T]) =
|
||||
let currStateObj = self.view.currentStartupStateObj()
|
||||
if not currStateObj.isNil:
|
||||
currStateObj.executeBackCommand(self.controller)
|
||||
let backState = currStateObj.getBackState()
|
||||
self.view.setCurrentStartupState(backState)
|
||||
currStateObj.delete()
|
||||
|
||||
method onPrimaryActionClicked*[T](self: Module[T]) =
|
||||
let currStateObj = self.view.currentStartupStateObj()
|
||||
if not currStateObj.isNil:
|
||||
currStateObj.executePrimaryCommand(self.controller)
|
||||
if currStateObj.moveToNextPrimaryState():
|
||||
let nextState = currStateObj.getNextPrimaryState()
|
||||
self.view.setCurrentStartupState(nextState)
|
||||
|
||||
method onSecondaryActionClicked*[T](self: Module[T]) =
|
||||
let currStateObj = self.view.currentStartupStateObj()
|
||||
if not currStateObj.isNil:
|
||||
currStateObj.executeSecondaryCommand(self.controller)
|
||||
if currStateObj.moveToNextSecondaryState():
|
||||
let nextState = currStateObj.getNextSecondaryState()
|
||||
self.view.setCurrentStartupState(nextState)
|
||||
|
||||
method onTertiaryActionClicked*[T](self: Module[T]) =
|
||||
let currStateObj = self.view.currentStartupStateObj()
|
||||
if not currStateObj.isNil:
|
||||
currStateObj.executeTertiaryCommand(self.controller)
|
||||
if currStateObj.moveToNextTertiaryState():
|
||||
let nextState = currStateObj.getNextTertiaryState()
|
||||
self.view.setCurrentStartupState(nextState)
|
||||
|
||||
method getImportedAccount*[T](self: Module[T]): GeneratedAccountDto =
|
||||
return self.controller.getImportedAccount()
|
||||
|
||||
method generateImage*[T](self: Module[T], imageUrl: string, aX: int, aY: int, bX: int, bY: int): string =
|
||||
return self.controller.generateImage(imageUrl, aX, aY, bX, bY)
|
||||
|
||||
method setDisplayName*[T](self: Module[T], value: string) =
|
||||
self.controller.setDisplayName(value)
|
||||
|
||||
method getDisplayName*[T](self: Module[T]): string =
|
||||
return self.controller.getDisplayName()
|
||||
|
||||
method setPassword*[T](self: Module[T], value: string) =
|
||||
self.controller.setPassword(value)
|
||||
|
||||
method getPassword*[T](self: Module[T]): string =
|
||||
return self.controller.getPassword()
|
||||
|
||||
method getPasswordStrengthScore*[T](self: Module[T], password, userName: string): int =
|
||||
return self.controller.getPasswordStrengthScore(password, userName)
|
||||
|
||||
method setupAccountError*[T](self: Module[T], error: string) =
|
||||
self.view.setupAccountError(error)
|
||||
|
||||
method validMnemonic*[T](self: Module[T], mnemonic: string): bool =
|
||||
return self.controller.validMnemonic(mnemonic)
|
||||
|
||||
method importAccountError*[T](self: Module[T], error: string) =
|
||||
self.view.importAccountError(error)
|
||||
|
||||
method importAccountSuccess*[T](self: Module[T]) =
|
||||
self.view.importAccountSuccess()
|
||||
|
||||
method setSelectedLoginAccount*[T](self: Module[T], item: login_acc_item.Item) =
|
||||
self.controller.setSelectedLoginAccountKeyUid(item.getKeyUid())
|
||||
self.view.setSelectedLoginAccount(item)
|
||||
|
||||
method emitAccountLoginError*[T](self: Module[T], error: string) =
|
||||
self.view.emitAccountLoginError(error)
|
||||
|
||||
method emitObtainingPasswordError*[T](self: Module[T], errorDescription: string) =
|
||||
self.view.emitObtainingPasswordError(errorDescription)
|
||||
|
||||
method emitObtainingPasswordSuccess*[T](self: Module[T], password: string) =
|
||||
self.view.emitObtainingPasswordSuccess(password)
|
||||
|
||||
method onNodeLogin*[T](self: Module[T], error: string) =
|
||||
let currStateObj = self.view.currentStartupStateObj()
|
||||
if currStateObj.isNil:
|
||||
error "error: cannot determine current startup state"
|
||||
quit() # quit the app
|
||||
|
||||
if error.len == 0:
|
||||
self.delegate.userLoggedIn()
|
||||
if currStateObj.flowType() != FlowType.AppLogin:
|
||||
self.controller.storeIdentityImage()
|
||||
else:
|
||||
if currStateObj.flowType() == FlowType.AppLogin:
|
||||
self.emitAccountLoginError(error)
|
||||
else:
|
||||
self.setupAccountError(error)
|
||||
error "error: ", methodName="onNodeLogin", errDesription =error
|
|
@ -1,75 +0,0 @@
|
|||
import Tables, chronicles
|
||||
|
||||
import io_interface
|
||||
|
||||
import ../../../core/signals/types
|
||||
import ../../../core/eventemitter
|
||||
import ../../../../app_service/service/accounts/service as accounts_service
|
||||
import ../../../../app_service/service/general/service as general_service
|
||||
|
||||
logScope:
|
||||
topics = "onboarding-controller"
|
||||
|
||||
type
|
||||
Controller* = ref object of RootObj
|
||||
delegate: io_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
accountsService: accounts_service.Service
|
||||
generalService: general_service.Service
|
||||
selectedAccountId: string
|
||||
displayName: string
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
accountsService: accounts_service.Service,
|
||||
generalService: general_service.Service):
|
||||
Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.accountsService = accountsService
|
||||
result.generalService = generalService
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
discard
|
||||
|
||||
proc init*(self: Controller) =
|
||||
self.events.on(SignalType.NodeLogin.event) do(e:Args):
|
||||
let signal = NodeSignal(e)
|
||||
if signal.event.error != "":
|
||||
self.delegate.setupAccountError(signal.event.error)
|
||||
|
||||
proc getGeneratedAccounts*(self: Controller): seq[GeneratedAccountDto] =
|
||||
return self.accountsService.generatedAccounts()
|
||||
|
||||
proc getImportedAccount*(self: Controller): GeneratedAccountDto =
|
||||
return self.accountsService.getImportedAccount()
|
||||
|
||||
proc setSelectedAccountByIndex*(self: Controller, index: int) =
|
||||
let accounts = self.getGeneratedAccounts()
|
||||
self.selectedAccountId = accounts[index].id
|
||||
|
||||
proc setDisplayName*(self: Controller, displayName: string) =
|
||||
self.displayName = displayName
|
||||
|
||||
proc storeSelectedAccountAndLogin*(self: Controller, password: string) =
|
||||
let error = self.accountsService.setupAccount(self.selectedAccountId, password, self.displayName)
|
||||
if error != "":
|
||||
self.delegate.setupAccountError(error)
|
||||
|
||||
proc validateMnemonic*(self: Controller, mnemonic: string): string =
|
||||
return self.accountsService.validateMnemonic(mnemonic)
|
||||
|
||||
proc importMnemonic*(self: Controller, mnemonic: string) =
|
||||
let error = self.accountsService.importMnemonic(mnemonic)
|
||||
if(error == ""):
|
||||
self.selectedAccountId = self.getImportedAccount().id
|
||||
self.delegate.importAccountSuccess()
|
||||
else:
|
||||
self.delegate.importAccountError(error)
|
||||
|
||||
proc getPasswordStrengthScore*(self: Controller, password, userName: string): int =
|
||||
return self.generalService.getPasswordStrengthScore(password, userName)
|
||||
|
||||
proc generateImages*(self: Controller, image: string, aX: int, aY: int, bX: int, bY: int): seq[general_service.Image] =
|
||||
return self.generalService.generateImages(image, aX, aY, bX, bY)
|
|
@ -1,51 +0,0 @@
|
|||
import ../../../../app_service/service/accounts/service
|
||||
|
||||
type
|
||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||
|
||||
method delete*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method load*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method isLoaded*(self: AccessInterface): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setupAccountError*(self: AccessInterface, error: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method importAccountError*(self: AccessInterface, error: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method importAccountSuccess*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setSelectedAccountByIndex*(self: AccessInterface, index: int) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method storeSelectedAccountAndLogin*(self: AccessInterface, password: string)
|
||||
{.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getImportedAccount*(self: AccessInterface): GeneratedAccountDto {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method validateMnemonic*(self: AccessInterface, mnemonic: string):
|
||||
string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method importMnemonic*(self: AccessInterface, mnemonic: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method setDisplayName*(self: AccessInterface, displayName: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getPasswordStrengthScore*(self: AccessInterface, password: string, userName: string): int {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method generateImage*(self: AccessInterface, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -1,93 +0,0 @@
|
|||
import NimQml
|
||||
import io_interface
|
||||
import ../io_interface as delegate_interface
|
||||
import view, controller, item
|
||||
import ../../../global/global_singleton
|
||||
import ../../../core/eventemitter
|
||||
import ../../../../app_service/service/accounts/service as accounts_service
|
||||
import ../../../../app_service/service/general/service as general_service
|
||||
|
||||
export io_interface
|
||||
|
||||
type
|
||||
Module* = ref object of io_interface.AccessInterface
|
||||
delegate: delegate_interface.AccessInterface
|
||||
view: View
|
||||
viewVariant: QVariant
|
||||
controller: Controller
|
||||
moduleLoaded: bool
|
||||
|
||||
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter,
|
||||
accountsService: accounts_service.Service,
|
||||
generalService: general_service.Service):
|
||||
Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.view = view.newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, events, accountsService, generalService)
|
||||
result.moduleLoaded = false
|
||||
|
||||
method delete*(self: Module) =
|
||||
self.view.delete
|
||||
self.viewVariant.delete
|
||||
self.controller.delete
|
||||
|
||||
method load*(self: Module) =
|
||||
singletonInstance.engine.setRootContextProperty("onboardingModule", self.viewVariant)
|
||||
self.controller.init()
|
||||
self.view.load()
|
||||
|
||||
let generatedAccounts = self.controller.getGeneratedAccounts()
|
||||
var accounts: seq[Item]
|
||||
for acc in generatedAccounts:
|
||||
accounts.add(initItem(acc.id, acc.alias, acc.address, acc.derivedAccounts.whisper.publicKey, acc.keyUid))
|
||||
|
||||
self.view.setAccountList(accounts)
|
||||
|
||||
method isLoaded*(self: Module): bool =
|
||||
return self.moduleLoaded
|
||||
|
||||
method viewDidLoad*(self: Module) =
|
||||
self.moduleLoaded = true
|
||||
self.delegate.onboardingDidLoad()
|
||||
|
||||
method setSelectedAccountByIndex*(self: Module, index: int) =
|
||||
self.controller.setSelectedAccountByIndex(index)
|
||||
|
||||
method setDisplayName*(self: Module, displayName: string) =
|
||||
self.controller.setDisplayName(displayName)
|
||||
|
||||
method storeSelectedAccountAndLogin*(self: Module, password: string) =
|
||||
self.controller.storeSelectedAccountAndLogin(password)
|
||||
|
||||
method setupAccountError*(self: Module, error: string) =
|
||||
self.view.setupAccountError(error)
|
||||
|
||||
method getImportedAccount*(self: Module): GeneratedAccountDto =
|
||||
return self.controller.getImportedAccount()
|
||||
|
||||
method validateMnemonic*(self: Module, mnemonic: string): string =
|
||||
return self.controller.validateMnemonic(mnemonic)
|
||||
|
||||
method importMnemonic*(self: Module, mnemonic: string) =
|
||||
self.controller.importMnemonic(mnemonic)
|
||||
|
||||
method importAccountError*(self: Module, error: string) =
|
||||
self.view.importAccountError(error)
|
||||
|
||||
method importAccountSuccess*(self: Module) =
|
||||
self.view.importAccountSuccess()
|
||||
|
||||
method getPasswordStrengthScore*(self: Module, password, userName: string): int =
|
||||
return self.controller.getPasswordStrengthScore(password, userName)
|
||||
|
||||
method generateImage*(self: Module, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string =
|
||||
let formatedImg = singletonInstance.utils.formatImagePath(imageUrl)
|
||||
let images = self.controller.generateImages(formatedImg, aX, aY, bX, bY)
|
||||
if(images.len == 0):
|
||||
return
|
||||
|
||||
for img in images:
|
||||
if(img.imgType == "large"):
|
||||
return img.uri
|
|
@ -1,100 +0,0 @@
|
|||
import NimQml
|
||||
import model, item
|
||||
import io_interface
|
||||
|
||||
QtObject:
|
||||
type
|
||||
View* = ref object of QObject
|
||||
delegate: io_interface.AccessInterface
|
||||
model: Model
|
||||
modelVariant: QVariant
|
||||
|
||||
proc delete*(self: View) =
|
||||
self.model.delete
|
||||
self.modelVariant.delete
|
||||
self.QObject.delete
|
||||
|
||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||
new(result, delete)
|
||||
result.QObject.setup
|
||||
result.delegate = delegate
|
||||
result.model = newModel()
|
||||
result.modelVariant = newQVariant(result.model)
|
||||
|
||||
proc load*(self: View) =
|
||||
self.delegate.viewDidLoad()
|
||||
|
||||
proc modelChanged*(self: View) {.signal.}
|
||||
|
||||
proc getModel(self: View): QVariant {.slot.} =
|
||||
return self.modelVariant
|
||||
|
||||
proc setAccountList*(self: View, accounts: seq[Item]) =
|
||||
self.model.setItems(accounts)
|
||||
self.modelChanged()
|
||||
|
||||
QtProperty[QVariant] accountsModel:
|
||||
read = getModel
|
||||
notify = modelChanged
|
||||
|
||||
proc importedAccountChanged*(self: View) {.signal.}
|
||||
|
||||
proc getImportedAccountAlias*(self: View): string {.slot.} =
|
||||
return self.delegate.getImportedAccount().alias
|
||||
|
||||
QtProperty[string] importedAccountAlias:
|
||||
read = getImportedAccountAlias
|
||||
notify = importedAccountChanged
|
||||
|
||||
proc getImportedAccountAddress*(self: View): string {.slot.} =
|
||||
return self.delegate.getImportedAccount().address
|
||||
|
||||
QtProperty[string] importedAccountAddress:
|
||||
read = getImportedAccountAddress
|
||||
notify = importedAccountChanged
|
||||
|
||||
proc getImportedAccountPubKey*(self: View): string {.slot.} =
|
||||
return self.delegate.getImportedAccount().derivedAccounts.whisper.publicKey
|
||||
|
||||
QtProperty[string] importedAccountPubKey:
|
||||
read = getImportedAccountPubKey
|
||||
notify = importedAccountChanged
|
||||
|
||||
proc setDisplayName*(self: View, displayName: string) {.slot.} =
|
||||
self.delegate.setDisplayName(displayName)
|
||||
|
||||
proc setSelectedAccountByIndex*(self: View, index: int) {.slot.} =
|
||||
self.delegate.setSelectedAccountByIndex(index)
|
||||
|
||||
proc storeSelectedAccountAndLogin*(self: View, password: string) {.slot.} =
|
||||
self.delegate.storeSelectedAccountAndLogin(password)
|
||||
|
||||
proc accountSetupError*(self: View, error: string) {.signal.}
|
||||
|
||||
proc setupAccountError*(self: View, error: string) =
|
||||
self.accountSetupError(error)
|
||||
|
||||
proc validateMnemonic*(self: View, mnemonic: string): string {.slot.} =
|
||||
return self.delegate.validateMnemonic(mnemonic)
|
||||
|
||||
proc importMnemonic*(self: View, mnemonic: string) {.slot.} =
|
||||
self.delegate.importMnemonic(mnemonic)
|
||||
|
||||
proc accountImportError*(self: View, error: string) {.signal.}
|
||||
|
||||
proc importAccountError*(self: View, error: string) =
|
||||
# In QML we can connect to this signal and notify a user
|
||||
# before refactoring we didn't have this signal
|
||||
self.accountImportError(error)
|
||||
|
||||
proc accountImportSuccess*(self: View) {.signal.}
|
||||
|
||||
proc importAccountSuccess*(self: View) =
|
||||
self.importedAccountChanged()
|
||||
self.accountImportSuccess()
|
||||
|
||||
proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} =
|
||||
return self.delegate.getPasswordStrengthScore(password, userName)
|
||||
|
||||
proc generateImage*(self: View, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.slot.} =
|
||||
self.delegate.generateImage(imageUrl, aX, aY, bX, bY)
|
|
@ -1,54 +1,54 @@
|
|||
import NimQml
|
||||
import item
|
||||
import models/login_account_item
|
||||
|
||||
QtObject:
|
||||
type SelectedAccount* = ref object of QObject
|
||||
type SelectedLoginAccount* = ref object of QObject
|
||||
item: Item
|
||||
|
||||
proc setup(self: SelectedAccount) =
|
||||
proc setup(self: SelectedLoginAccount) =
|
||||
self.QObject.setup
|
||||
|
||||
proc delete*(self: SelectedAccount) =
|
||||
proc delete*(self: SelectedLoginAccount) =
|
||||
self.QObject.delete
|
||||
|
||||
proc newSelectedAccount*(): SelectedAccount =
|
||||
proc newSelectedLoginAccount*(): SelectedLoginAccount =
|
||||
new(result, delete)
|
||||
result.setup
|
||||
|
||||
proc setSelectedAccountData*(self: SelectedAccount, item: Item) =
|
||||
proc setData*(self: SelectedLoginAccount, item: Item) =
|
||||
self.item = item
|
||||
|
||||
proc getName(self: SelectedAccount): string {.slot.} =
|
||||
proc getName(self: SelectedLoginAccount): string {.slot.} =
|
||||
return self.item.getName()
|
||||
|
||||
QtProperty[string] username:
|
||||
read = getName
|
||||
|
||||
proc getKeyUid(self: SelectedAccount): string {.slot.} =
|
||||
proc getKeyUid(self: SelectedLoginAccount): string {.slot.} =
|
||||
return self.item.getKeyUid()
|
||||
|
||||
QtProperty[string] keyUid:
|
||||
read = getKeyUid
|
||||
|
||||
proc getColorHash(self: SelectedAccount): QVariant {.slot.} =
|
||||
proc getColorHash(self: SelectedLoginAccount): QVariant {.slot.} =
|
||||
return self.item.getColorHashVariant()
|
||||
|
||||
QtProperty[QVariant] colorHash:
|
||||
read = getColorHash
|
||||
|
||||
proc getColorId(self: SelectedAccount): int {.slot.} =
|
||||
proc getColorId(self: SelectedLoginAccount): int {.slot.} =
|
||||
return self.item.getColorId()
|
||||
|
||||
QtProperty[int] colorId:
|
||||
read = getColorId
|
||||
|
||||
proc getThumbnailImage(self: SelectedAccount): string {.slot.} =
|
||||
proc getThumbnailImage(self: SelectedLoginAccount): string {.slot.} =
|
||||
return self.item.getThumbnailImage()
|
||||
|
||||
QtProperty[string] thumbnailImage:
|
||||
read = getThumbnailImage
|
||||
|
||||
proc getLargeImage(self: SelectedAccount): string {.slot.} =
|
||||
proc getLargeImage(self: SelectedLoginAccount): string {.slot.} =
|
||||
return self.item.getLargeImage()
|
||||
|
||||
QtProperty[string] largeImage:
|
|
@ -1,49 +1,203 @@
|
|||
import NimQml
|
||||
import io_interface
|
||||
import selected_login_account
|
||||
import internal/[state, state_wrapper]
|
||||
import models/generated_account_model as gen_acc_model
|
||||
import models/generated_account_item as gen_acc_item
|
||||
import models/login_account_model as login_acc_model
|
||||
import models/login_account_item as login_acc_item
|
||||
|
||||
type
|
||||
AppState* {.pure.} = enum
|
||||
OnboardingState = 0
|
||||
LoginState
|
||||
StartupState = 0
|
||||
MainAppState
|
||||
|
||||
QtObject:
|
||||
type
|
||||
View* = ref object of QObject
|
||||
delegate: io_interface.AccessInterface
|
||||
showBeforeGetStartedPopup: bool
|
||||
currentStartupState: StateWrapper
|
||||
currentStartupStateVariant: QVariant
|
||||
generatedAccountsModel: gen_acc_model.Model
|
||||
generatedAccountsModelVariant: QVariant
|
||||
selectedLoginAccount: SelectedLoginAccount
|
||||
selectedLoginAccountVariant: QVariant
|
||||
loginAccountsModel: login_acc_model.Model
|
||||
loginAccountsModelVariant: QVariant
|
||||
appState: AppState
|
||||
|
||||
proc delete*(self: View) =
|
||||
self.currentStartupStateVariant.delete
|
||||
self.currentStartupState.delete
|
||||
self.generatedAccountsModel.delete
|
||||
self.generatedAccountsModelVariant.delete
|
||||
self.selectedLoginAccount.delete
|
||||
self.selectedLoginAccountVariant.delete
|
||||
self.loginAccountsModel.delete
|
||||
self.loginAccountsModelVariant.delete
|
||||
self.QObject.delete
|
||||
|
||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||
new(result, delete)
|
||||
result.QObject.setup
|
||||
result.delegate = delegate
|
||||
result.appState = AppState.OnboardingState
|
||||
result.showBeforeGetStartedPopup = true
|
||||
result.appState = AppState.StartupState
|
||||
result.currentStartupState = newStateWrapper()
|
||||
result.currentStartupStateVariant = newQVariant(result.currentStartupState)
|
||||
result.generatedAccountsModel = gen_acc_model.newModel()
|
||||
result.generatedAccountsModelVariant = newQVariant(result.generatedAccountsModel)
|
||||
result.selectedLoginAccount = newSelectedLoginAccount()
|
||||
result.selectedLoginAccountVariant = newQVariant(result.selectedLoginAccount)
|
||||
result.loginAccountsModel = login_acc_model.newModel()
|
||||
result.loginAccountsModelVariant = newQVariant(result.loginAccountsModel)
|
||||
|
||||
proc load*(self: View) =
|
||||
# In some point, here, we will setup some exposed main module related things.
|
||||
self.delegate.viewDidLoad()
|
||||
signalConnect(result.currentStartupState, "backActionClicked()", result, "onBackActionClicked()", 2)
|
||||
signalConnect(result.currentStartupState, "primaryActionClicked()", result, "onPrimaryActionClicked()", 2)
|
||||
signalConnect(result.currentStartupState, "secondaryActionClicked()", result, "onSecondaryActionClicked()", 2)
|
||||
signalConnect(result.currentStartupState, "tertiaryActionClicked()", result, "onTertiaryActionClicked()", 2)
|
||||
|
||||
proc currentStartupStateObj*(self: View): State =
|
||||
return self.currentStartupState.getStateObj()
|
||||
|
||||
proc setCurrentStartupState*(self: View, state: State) =
|
||||
self.currentStartupState.setStateObj(state)
|
||||
proc getCurrentStartupState(self: View): QVariant {.slot.} =
|
||||
return self.currentStartupStateVariant
|
||||
QtProperty[QVariant] currentStartupState:
|
||||
read = getCurrentStartupState
|
||||
|
||||
proc onBackActionClicked*(self: View) {.slot.} =
|
||||
self.delegate.onBackActionClicked()
|
||||
|
||||
proc onPrimaryActionClicked*(self: View) {.slot.} =
|
||||
self.delegate.onPrimaryActionClicked()
|
||||
|
||||
proc onSecondaryActionClicked*(self: View) {.slot.} =
|
||||
self.delegate.onSecondaryActionClicked()
|
||||
|
||||
proc onTertiaryActionClicked*(self: View) {.slot.} =
|
||||
self.delegate.onTertiaryActionClicked()
|
||||
|
||||
proc startUpUIRaised*(self: View) {.signal.}
|
||||
proc appStateChanged*(self: View, state: int) {.signal.}
|
||||
|
||||
proc showBeforeGetStartedPopup*(self: View): bool {.slot.} =
|
||||
return self.showBeforeGetStartedPopup
|
||||
proc beforeGetStartedPopupAccepted*(self: View) {.slot.} =
|
||||
self.showBeforeGetStartedPopup = false
|
||||
|
||||
proc appStateChanged*(self: View, state: int) {.signal.}
|
||||
proc getAppState(self: View): int {.slot.} =
|
||||
return self.appState.int
|
||||
|
||||
proc setAppState*(self: View, state: AppState) =
|
||||
if(self.appState == state):
|
||||
return
|
||||
|
||||
self.appState = state
|
||||
self.appStateChanged(self.appState.int)
|
||||
|
||||
QtProperty[int] appState:
|
||||
read = getAppState
|
||||
notify = appStateChanged
|
||||
|
||||
proc logOut*(self: View) {.signal.}
|
||||
|
||||
proc emitLogOut*(self: View) =
|
||||
self.logOut()
|
||||
|
||||
proc generatedAccountsModelChanged*(self: View) {.signal.}
|
||||
proc getGeneratedAccountsModel(self: View): QVariant {.slot.} =
|
||||
return self.generatedAccountsModelVariant
|
||||
proc setGeneratedAccountList*(self: View, accounts: seq[gen_acc_item.Item]) =
|
||||
self.generatedAccountsModel.setItems(accounts)
|
||||
self.generatedAccountsModelChanged()
|
||||
QtProperty[QVariant] generatedAccountsModel:
|
||||
read = getGeneratedAccountsModel
|
||||
notify = generatedAccountsModelChanged
|
||||
|
||||
proc importedAccountChanged*(self: View) {.signal.}
|
||||
proc getImportedAccountAlias*(self: View): string {.slot.} =
|
||||
return self.delegate.getImportedAccount().alias
|
||||
QtProperty[string] importedAccountAlias:
|
||||
read = getImportedAccountAlias
|
||||
notify = importedAccountChanged
|
||||
|
||||
proc getImportedAccountAddress*(self: View): string {.slot.} =
|
||||
return self.delegate.getImportedAccount().address
|
||||
QtProperty[string] importedAccountAddress:
|
||||
read = getImportedAccountAddress
|
||||
notify = importedAccountChanged
|
||||
|
||||
proc getImportedAccountPubKey*(self: View): string {.slot.} =
|
||||
return self.delegate.getImportedAccount().derivedAccounts.whisper.publicKey
|
||||
QtProperty[string] importedAccountPubKey:
|
||||
read = getImportedAccountPubKey
|
||||
notify = importedAccountChanged
|
||||
|
||||
proc generateImage*(self: View, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.slot.} =
|
||||
self.delegate.generateImage(imageUrl, aX, aY, bX, bY)
|
||||
|
||||
proc setDisplayName*(self: View, value: string) {.slot.} =
|
||||
self.delegate.setDisplayName(value)
|
||||
|
||||
proc getDisplayName*(self: View): string {.slot.} =
|
||||
return self.delegate.getDisplayName()
|
||||
|
||||
proc setPassword*(self: View, value: string) {.slot.} =
|
||||
self.delegate.setPassword(value)
|
||||
|
||||
proc getPassword*(self: View): string {.slot.} =
|
||||
return self.delegate.getPassword()
|
||||
|
||||
proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} =
|
||||
return self.delegate.getPasswordStrengthScore(password, userName)
|
||||
|
||||
proc accountSetupError*(self: View, error: string) {.signal.}
|
||||
proc setupAccountError*(self: View, error: string) =
|
||||
self.accountSetupError(error)
|
||||
|
||||
proc validMnemonic*(self: View, mnemonic: string): bool {.slot.} =
|
||||
return self.delegate.validMnemonic(mnemonic)
|
||||
|
||||
proc accountImportError*(self: View, error: string) {.signal.}
|
||||
proc importAccountError*(self: View, error: string) =
|
||||
# In QML we can connect to this signal and notify user, before refactoring we didn't have this signal
|
||||
self.accountImportError(error)
|
||||
|
||||
proc accountImportSuccess*(self: View) {.signal.}
|
||||
proc importAccountSuccess*(self: View) =
|
||||
self.importedAccountChanged()
|
||||
self.accountImportSuccess()
|
||||
|
||||
proc selectedLoginAccountChanged*(self: View) {.signal.}
|
||||
proc getSelectedLoginAccount(self: View): QVariant {.slot.} =
|
||||
return self.selectedLoginAccountVariant
|
||||
proc setSelectedLoginAccount*(self: View, item: login_acc_item.Item) =
|
||||
self.selectedLoginAccount.setData(item)
|
||||
self.selectedLoginAccountChanged()
|
||||
proc setSelectedLoginAccountByIndex*(self: View, index: int) {.slot.} =
|
||||
let item = self.loginAccountsModel.getItemAtIndex(index)
|
||||
self.delegate.setSelectedLoginAccount(item)
|
||||
QtProperty[QVariant] selectedLoginAccount:
|
||||
read = getSelectedLoginAccount
|
||||
notify = selectedLoginAccountChanged
|
||||
|
||||
proc loginAccountsModelChanged*(self: View) {.signal.}
|
||||
proc getLoginAccountsModel(self: View): QVariant {.slot.} =
|
||||
return self.loginAccountsModelVariant
|
||||
proc setLoginAccountsModelItems*(self: View, accounts: seq[login_acc_item.Item]) =
|
||||
self.loginAccountsModel.setItems(accounts)
|
||||
self.loginAccountsModelChanged()
|
||||
QtProperty[QVariant] loginAccountsModel:
|
||||
read = getLoginAccountsModel
|
||||
notify = loginAccountsModelChanged
|
||||
|
||||
proc accountLoginError*(self: View, error: string) {.signal.}
|
||||
proc emitAccountLoginError*(self: View, error: string) =
|
||||
self.accountLoginError(error)
|
||||
|
||||
proc obtainingPasswordError*(self:View, errorDescription: string) {.signal.}
|
||||
proc emitObtainingPasswordError*(self: View, errorDescription: string) =
|
||||
self.obtainingPasswordError(errorDescription)
|
||||
|
||||
proc obtainingPasswordSuccess*(self:View, password: string) {.signal.}
|
||||
proc emitObtainingPasswordSuccess*(self: View, password: string) =
|
||||
self.obtainingPasswordSuccess(password)
|
|
@ -44,7 +44,8 @@ proc toAccountDto*(jsonObj: JsonNode): AccountDto =
|
|||
discard jsonObj.getProp("keycard-pairing", result.keycardPairing)
|
||||
discard jsonObj.getProp("key-uid", result.keyUid)
|
||||
discard jsonObj.getProp("colorId", result.colorId)
|
||||
result.colorHash = toColorHashDto(jsonObj["colorHash"])
|
||||
if jsonObj.hasKey("colorHash"):
|
||||
result.colorHash = toColorHashDto(jsonObj["colorHash"])
|
||||
|
||||
var imagesObj: JsonNode
|
||||
if(jsonObj.getProp("images", imagesObj) and imagesObj.kind == JArray):
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import os, json, sequtils, strutils, uuids
|
||||
import json_serialization, chronicles
|
||||
|
||||
import ../../../app/global/global_singleton
|
||||
import ./dto/accounts as dto_accounts
|
||||
import ./dto/generated_accounts as dto_generated_accounts
|
||||
import ../../../backend/accounts as status_account
|
||||
import ../../../backend/general as status_general
|
||||
import ../../../backend/core as status_core
|
||||
|
||||
|
@ -21,6 +21,8 @@ logScope:
|
|||
const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET]
|
||||
const ACCOUNT_ALREADY_EXISTS_ERROR = "account already exists"
|
||||
|
||||
include utils
|
||||
|
||||
type
|
||||
Service* = ref object of RootObj
|
||||
fleetConfiguration: FleetConfiguration
|
||||
|
@ -53,22 +55,6 @@ proc setKeyStoreDir(self: Service, key: string) =
|
|||
self.keyStoreDir = joinPath(main_constants.ROOTKEYSTOREDIR, key) & main_constants.sep
|
||||
discard status_general.initKeystore(self.keyStoreDir)
|
||||
|
||||
proc compressPk*(publicKey: string): string =
|
||||
try:
|
||||
let response = status_account.compressPk(publicKey)
|
||||
if(not response.error.isNil):
|
||||
error "error compressPk: ", errDescription = response.error.message
|
||||
result = response.result
|
||||
|
||||
except Exception as e:
|
||||
error "error: ", procName="compressPk", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc generateAliasFromPk*(publicKey: string): string =
|
||||
return status_account.generateAlias(publicKey).result.getStr
|
||||
|
||||
proc isAlias*(value: string): bool =
|
||||
return status_account.isAlias(value)
|
||||
|
||||
proc init*(self: Service) =
|
||||
try:
|
||||
let response = status_account.generateAddresses(PATHS)
|
||||
|
@ -269,6 +255,10 @@ proc getDefaultNodeConfig*(self: Service, installationId: string): JsonNode =
|
|||
|
||||
result["KeyStoreDir"] = newJString(self.keyStoreDir.replace(main_constants.STATUSGODIR, ""))
|
||||
|
||||
proc setLocalAccountSettingsFile(self: Service) =
|
||||
if(defined(macosx) and self.getLoggedInAccount.isValid()):
|
||||
singletonInstance.localAccountSettings.setFileName(self.getLoggedInAccount.name)
|
||||
|
||||
proc setupAccount*(self: Service, accountId, password, displayName: string): string =
|
||||
try:
|
||||
let installationId = $genUUID()
|
||||
|
@ -292,6 +282,7 @@ proc setupAccount*(self: Service, accountId, password, displayName: string): str
|
|||
|
||||
self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, accountDataJson,
|
||||
subaccountDataJson, settingsJson, nodeConfigJson)
|
||||
self.setLocalAccountSettingsFile()
|
||||
|
||||
if self.getLoggedInAccount.isValid():
|
||||
return ""
|
||||
|
@ -377,6 +368,7 @@ proc login*(self: Service, account: AccountDto, password: string): string =
|
|||
if error == "":
|
||||
debug "Account logged in"
|
||||
self.loggedInAccount = account
|
||||
self.setLocalAccountSettingsFile()
|
||||
|
||||
return error
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import json
|
||||
import ../../../backend/accounts as status_account
|
||||
|
||||
proc compressPk*(publicKey: string): string =
|
||||
try:
|
||||
let response = status_account.compressPk(publicKey)
|
||||
if(not response.error.isNil):
|
||||
echo "error compressPk: " & response.error.message
|
||||
result = response.result
|
||||
|
||||
except Exception as e:
|
||||
echo "error: `compressPk` " & $e.name & " msg: " & $e.msg
|
||||
|
||||
proc generateAliasFromPk*(publicKey: string): string =
|
||||
return status_account.generateAlias(publicKey).result.getStr
|
||||
|
||||
proc isAlias*(value: string): bool =
|
||||
return status_account.isAlias(value)
|
|
@ -8,6 +8,9 @@ logScope:
|
|||
const ERROR_TYPE_AUTHENTICATION* = "authentication"
|
||||
const ERROR_TYPE_KEYCHAIN* = "keychain"
|
||||
|
||||
const SIGNAL_KEYCHAIN_SERVICE_SUCCESS* = "keychainServiceSuccess"
|
||||
const SIGNAL_KEYCHAIN_SERVICE_ERROR* = "keychainServiceError"
|
||||
|
||||
type
|
||||
KeyChainServiceArg* = ref object of Args
|
||||
data*: string
|
||||
|
@ -53,9 +56,9 @@ QtObject:
|
|||
|
||||
let arg = KeyChainServiceArg(errCode: errorCode, errType: errorType,
|
||||
errDescription: errorDescription)
|
||||
self.events.emit("keychainServiceError", arg)
|
||||
self.events.emit("", arg)
|
||||
|
||||
proc onKeychainManagerSuccess*(self: Service, data: string) {.slot.} =
|
||||
## This slot is called in case a password is successfully retrieved from the
|
||||
## Keychain. In this case @data contains required password.
|
||||
self.events.emit("keychainServiceSuccess", KeyChainServiceArg(data: data))
|
||||
self.events.emit(SIGNAL_KEYCHAIN_SERVICE_SUCCESS, KeyChainServiceArg(data: data))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import json, json_serialization, chronicles, nimcrypto, strutils
|
||||
import json, json_serialization, chronicles, strutils
|
||||
import ./core, ./utils
|
||||
import ./response_type
|
||||
|
||||
|
|
|
@ -1,307 +1,172 @@
|
|||
import QtQuick 2.12
|
||||
import QtQml.StateMachine 1.14 as DSM
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Controls 2.14
|
||||
import QtQuick.Dialogs 1.3
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import "controls"
|
||||
import "views"
|
||||
import "stores"
|
||||
|
||||
QtObject {
|
||||
OnboardingBasePage {
|
||||
id: root
|
||||
property bool hasAccounts
|
||||
property string keysMainSetState: ""
|
||||
property string prevState: ""
|
||||
|
||||
signal loadApp()
|
||||
signal onBoardingStepChanged(var view, string state)
|
||||
property var startupStore: StartupStore {}
|
||||
|
||||
property var stateMachine: DSM.StateMachine {
|
||||
id: stateMachine
|
||||
initialState: onboardingState
|
||||
running: true
|
||||
backButtonVisible: root.startupStore.currentStartupState.displayBackButton
|
||||
|
||||
DSM.State {
|
||||
id: onboardingState
|
||||
initialState: root.hasAccounts ? stateLogin : (Qt.platform.os === "osx" ? allowNotificationsState : welcomeMainState)
|
||||
onBackClicked: {
|
||||
root.startupStore.backAction()
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: allowNotificationsState
|
||||
onEntered: { onBoardingStepChanged(allowNotificationsMain, ""); }
|
||||
function unload() {
|
||||
loader.sourceComponent = undefined
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: welcomeMainState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "WelcomeMain"
|
||||
}
|
||||
Loader {
|
||||
id: loader
|
||||
anchors.fill: parent
|
||||
sourceComponent: {
|
||||
if (root.startupStore.currentStartupState.stateType === Constants.startupState.allowNotifications)
|
||||
{
|
||||
return allowNotificationsViewComponent
|
||||
}
|
||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.welcome)
|
||||
{
|
||||
return welcomeViewComponent
|
||||
}
|
||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.welcomeNewStatusUser ||
|
||||
root.startupStore.currentStartupState.stateType === Constants.startupState.welcomeOldStatusUser ||
|
||||
root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileImportSeedPhrase)
|
||||
{
|
||||
return keysMainViewComponent
|
||||
}
|
||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileCreate ||
|
||||
root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileChatKey)
|
||||
{
|
||||
return insertDetailsViewComponent
|
||||
}
|
||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileCreatePassword)
|
||||
{
|
||||
return createPasswordViewComponent
|
||||
}
|
||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileConfirmPassword)
|
||||
{
|
||||
return confirmPasswordViewComponent
|
||||
}
|
||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.biometrics)
|
||||
{
|
||||
return touchIdAuthViewComponent
|
||||
}
|
||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileEnterSeedPhrase)
|
||||
{
|
||||
return seedPhraseInputViewComponent
|
||||
}
|
||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.login)
|
||||
{
|
||||
return loginViewComponent
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: welcomeMainState
|
||||
onEntered: { onBoardingStepChanged(welcomeMain, ""); }
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: keysMainState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "KeyMain"
|
||||
}
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: keysMainState
|
||||
onEntered: { onBoardingStepChanged(keysMain, root.keysMainSetState); }
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: genKeyState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "GenKey"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: importSeedState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "ImportSeed"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: welcomeMainState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "Welcome"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: stateLogin
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "LogIn"
|
||||
}
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: genKeyState
|
||||
onEntered: { onBoardingStepChanged(genKey, ""); }
|
||||
DSM.SignalTransition {
|
||||
targetState: welcomeMainState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "Welcome"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: appState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "LoggedIn"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: stateLogin
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "LogIn"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: importSeedState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "ImportSeed"
|
||||
}
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: importSeedState
|
||||
property string seedInputState: "existingUser"
|
||||
onEntered: { onBoardingStepChanged(seedPhrase, seedInputState); }
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: keysMainState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "KeyMain"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: genKeyState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "GenKey"
|
||||
}
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: keycardState
|
||||
onEntered: { onBoardingStepChanged(keycardFlowSelection, ""); }
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: appState
|
||||
signal: startupModule.appStateChanged
|
||||
guard: state === Constants.appState.main
|
||||
}
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: stateLogin
|
||||
onEntered: { onBoardingStepChanged(login, ""); }
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: appState
|
||||
signal: startupModule.appStateChanged
|
||||
guard: state === Constants.appState.main
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: genKeyState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "GenKey"
|
||||
}
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: root.hasAccounts ? stateLogin : keysMainState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "InitialState"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: keysMainState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "KeysMain"
|
||||
}
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: keycardState
|
||||
signal: Global.applicationWindow.navigateTo
|
||||
guard: path === "KeycardFlowSelection"
|
||||
}
|
||||
|
||||
DSM.FinalState {
|
||||
id: onboardingDoneState
|
||||
}
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: appState
|
||||
onEntered: loadApp();
|
||||
|
||||
DSM.SignalTransition {
|
||||
targetState: stateLogin
|
||||
signal: startupModule.logOut
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
property var allowNotificationsComponent: Component {
|
||||
id: allowNotificationsMain
|
||||
Connections {
|
||||
target: root.startupStore.startupModuleInst
|
||||
onAccountSetupError: {
|
||||
if (error === Constants.existingAccountError) {
|
||||
msgDialog.title = qsTr("Keys for this account already exist")
|
||||
msgDialog.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase")
|
||||
} else {
|
||||
msgDialog.title = qsTr("Login failed")
|
||||
msgDialog.text = qsTr("Login failed. Please re-enter your password and try again.")
|
||||
}
|
||||
msgDialog.open()
|
||||
}
|
||||
|
||||
onAccountImportError: {
|
||||
if (error === Constants.existingAccountError) {
|
||||
msgDialog.title = qsTr("Keys for this account already exist")
|
||||
msgDialog.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase")
|
||||
} else {
|
||||
msgDialog.title = qsTr("Error importing seed")
|
||||
msgDialog.text = error
|
||||
}
|
||||
msgDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
id: msgDialog
|
||||
title: qsTr("Login failed")
|
||||
text: qsTr("Login failed. Please re-enter your password and try again.")
|
||||
icon: StandardIcon.Critical
|
||||
standardButtons: StandardButton.Ok
|
||||
onAccepted: {
|
||||
console.log("TODO: restart flow...")
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: allowNotificationsViewComponent
|
||||
AllowNotificationsView {
|
||||
onBtnOkClicked: {
|
||||
Global.applicationWindow.navigateTo("WelcomeMain");
|
||||
}
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
|
||||
property var welcomeComponent: Component {
|
||||
id: welcomeMain
|
||||
Component {
|
||||
id: welcomeViewComponent
|
||||
WelcomeView {
|
||||
onBtnNewUserClicked: {
|
||||
root.keysMainSetState = "getkeys";
|
||||
Global.applicationWindow.navigateTo("KeyMain");
|
||||
}
|
||||
onBtnExistingUserClicked: {
|
||||
root.keysMainSetState = "connectkeys";
|
||||
Global.applicationWindow.navigateTo("KeyMain");
|
||||
}
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
|
||||
property var keysMainComponent: Component {
|
||||
id: keysMain
|
||||
Component {
|
||||
id: keysMainViewComponent
|
||||
KeysMainView {
|
||||
onButtonClicked: {
|
||||
if (state === "importseed") {
|
||||
importSeedState.seedInputState = "existingUser";
|
||||
Global.applicationWindow.navigateTo("ImportSeed");
|
||||
} else {
|
||||
importSeedState.seedInputState = "newUser";
|
||||
Global.applicationWindow.navigateTo("GenKey");
|
||||
}
|
||||
}
|
||||
onKeycardLinkClicked: {
|
||||
Global.applicationWindow.navigateTo("KeycardFlowSelection");
|
||||
}
|
||||
onSeedLinkClicked: {
|
||||
if (state === "getkeys") {
|
||||
importSeedState.seedInputState = "newUser";
|
||||
state = "importseed";
|
||||
} else {
|
||||
importSeedState.seedInputState = "existingUser";
|
||||
Global.applicationWindow.navigateTo("ImportSeed");
|
||||
}
|
||||
}
|
||||
onBackClicked: {
|
||||
if (state === "importseed") {
|
||||
state = "getkeys";
|
||||
} else if ((root.keysMainSetState === "connectkeys" && LoginStore.currentAccount.username !== "") || root.prevState === "LogIn") {
|
||||
Global.applicationWindow.navigateTo("LogIn");
|
||||
} else {
|
||||
Global.applicationWindow.navigateTo("Welcome");
|
||||
}
|
||||
}
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
|
||||
property var seedPhraseInputComponent: Component {
|
||||
id: seedPhrase
|
||||
Component {
|
||||
id: insertDetailsViewComponent
|
||||
InsertDetailsView {
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: createPasswordViewComponent
|
||||
CreatePasswordView {
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: confirmPasswordViewComponent
|
||||
ConfirmPasswordView {
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: touchIdAuthViewComponent
|
||||
TouchIDAuthView {
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: seedPhraseInputViewComponent
|
||||
SeedPhraseInputView {
|
||||
onExit: {
|
||||
if (root.keysMainSetState !== "connectkeys") {
|
||||
root.keysMainSetState = "importseed";
|
||||
}
|
||||
Global.applicationWindow.navigateTo("KeyMain");
|
||||
}
|
||||
onSeedValidated: {
|
||||
root.keysMainSetState = "importseed";
|
||||
Global.applicationWindow.navigateTo("GenKey");
|
||||
}
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
|
||||
property var genKeyComponent: Component {
|
||||
id: genKey
|
||||
GenKeyView {
|
||||
onExit: {
|
||||
if (root.keysMainSetState === "importseed") {
|
||||
root.keysMainSetState = "connectkeys"
|
||||
Global.applicationWindow.navigateTo("ImportSeed");
|
||||
} else if (LoginStore.currentAccount.username !== "" && importSeedState.seedInputState === "existingUser") {
|
||||
Global.applicationWindow.navigateTo("LogIn");
|
||||
} else {
|
||||
Global.applicationWindow.navigateTo("KeysMain");
|
||||
}
|
||||
}
|
||||
onKeysGenerated: {
|
||||
Global.applicationWindow.navigateTo("LoggedIn")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var keycardFlowSelectionComponent: Component {
|
||||
id: keycardFlowSelection
|
||||
KeycardFlowSelectionView {
|
||||
onClosed: {
|
||||
if (root.hasAccounts) {
|
||||
Global.applicationWindow.navigateTo("InitialState")
|
||||
} else {
|
||||
Global.applicationWindow.navigateTo("KeysMain")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var loginComponent: Component {
|
||||
id: login
|
||||
Component {
|
||||
id: loginViewComponent
|
||||
LoginView {
|
||||
onAddNewUserClicked: {
|
||||
root.keysMainSetState = "getkeys";
|
||||
root.prevState = "LogIn"
|
||||
Global.applicationWindow.navigateTo("KeysMain");
|
||||
}
|
||||
onAddExistingKeyClicked: {
|
||||
root.keysMainSetState = "connectkeys";
|
||||
root.prevState = "LogIn"
|
||||
Global.applicationWindow.navigateTo("KeysMain");
|
||||
}
|
||||
startupStore: root.startupStore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ Page {
|
|||
|
||||
property alias backButtonVisible: backButton.visible
|
||||
|
||||
signal exit()
|
||||
signal backClicked()
|
||||
|
||||
background: Rectangle {
|
||||
|
|
|
@ -12,23 +12,27 @@ import "../stores"
|
|||
|
||||
// TODO: replace with StatusModal
|
||||
ModalPopup {
|
||||
id: root
|
||||
|
||||
property StartupStore startupStore
|
||||
|
||||
signal accountSelected(int index)
|
||||
signal openModalClicked()
|
||||
id: popup
|
||||
|
||||
title: qsTr("Your keys")
|
||||
|
||||
AccountListPanel {
|
||||
id: accountList
|
||||
anchors.fill: parent
|
||||
|
||||
model: LoginStore.loginModuleInst.accountsModel
|
||||
model: root.startupStore.startupModuleInst.loginAccountsModel
|
||||
isSelected: function (index, keyUid) {
|
||||
return LoginStore.currentAccount.keyUid === keyUid
|
||||
return root.startupStore.selectedLoginAccount.keyUid === keyUid
|
||||
}
|
||||
|
||||
onAccountSelect: function(index) {
|
||||
popup.accountSelected(index)
|
||||
popup.close()
|
||||
root.accountSelected(index)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +44,7 @@ ModalPopup {
|
|||
|
||||
onClicked : {
|
||||
openModalClicked()
|
||||
popup.close()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.popups 1.0
|
||||
|
||||
import "../stores"
|
||||
|
||||
StatusModal {
|
||||
id: popup
|
||||
|
||||
height: 510
|
||||
header.title: qsTr("Upload profile picture")
|
||||
|
||||
property string currentProfileImg: ""
|
||||
property string croppedImg: ""
|
||||
|
||||
signal setProfileImage(string image)
|
||||
|
||||
// Internals
|
||||
//
|
||||
onOpened: imageEditor.userSelectedImage = false
|
||||
onClosed: popup.croppedImg = ""
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: parent
|
||||
|
||||
EditCroppedImagePanel {
|
||||
id: imageEditor
|
||||
|
||||
width: 160
|
||||
height: 160
|
||||
anchors.centerIn: parent
|
||||
|
||||
imageFileDialogTitle: qsTr("Choose an image for profile picture")
|
||||
title: qsTr("Profile picture")
|
||||
acceptButtonText: qsTr("Make this my profile picture")
|
||||
|
||||
aspectRatio: 1
|
||||
|
||||
dataImage: popup.currentProfileImg
|
||||
|
||||
NoImageUploadedPanel {
|
||||
anchors.centerIn: parent
|
||||
|
||||
visible: imageEditor.nothingToShow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rightButtons: [
|
||||
StatusFlatButton {
|
||||
visible: !!popup.currentProfileImg
|
||||
type: StatusBaseButton.Type.Danger
|
||||
text: qsTr("Remove")
|
||||
onClicked: {
|
||||
OnboardingStore.clearImageProps()
|
||||
popup.setProfileImage("")
|
||||
close();
|
||||
}
|
||||
},
|
||||
StatusButton {
|
||||
id: uploadBtn
|
||||
text: imageEditor.userSelectedImage ? qsTr("Upload") : qsTr("Done")
|
||||
onClicked: {
|
||||
if (imageEditor.userSelectedImage) {
|
||||
popup.croppedImg = OnboardingStore.generateImage(imageEditor.source,
|
||||
imageEditor.cropRect.x.toFixed(),
|
||||
imageEditor.cropRect.y.toFixed(),
|
||||
(imageEditor.cropRect.x + imageEditor.cropRect.width).toFixed(),
|
||||
(imageEditor.cropRect.y + imageEditor.cropRect.height).toFixed())
|
||||
popup.setProfileImage(popup.croppedImg)
|
||||
}
|
||||
close();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
pragma Singleton
|
||||
|
||||
import QtQuick 2.13
|
||||
|
||||
QtObject {
|
||||
// Not Refactored Yet
|
||||
// property var keycardModelInst: keycardModel
|
||||
|
||||
function startConnection() {
|
||||
// keycardModel.startConnection()
|
||||
}
|
||||
|
||||
function init(pin) {
|
||||
// keycardModel.init(pin)
|
||||
}
|
||||
|
||||
function recoverAccount() {
|
||||
// keycardModel.recoverAccount()
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
pragma Singleton
|
||||
|
||||
import QtQuick 2.13
|
||||
|
||||
QtObject {
|
||||
property var loginModuleInst: loginModule
|
||||
property var currentAccount: loginModuleInst.selectedAccount
|
||||
|
||||
function login(password) {
|
||||
loginModuleInst.login(password)
|
||||
}
|
||||
|
||||
function setCurrentAccount(index) {
|
||||
loginModuleInst.setSelectedAccountByIndex(index)
|
||||
}
|
||||
|
||||
function rowCount() {
|
||||
return loginModuleInst.accountsModel.rowCount()
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
pragma Singleton
|
||||
|
||||
import QtQuick 2.13
|
||||
import utils 1.0
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
property var profileSectionModuleInst: profileSectionModule
|
||||
property var profileModule: profileSectionModuleInst.profileModule
|
||||
property var onboardingModuleInst: onboardingModule
|
||||
property var mainModuleInst: !!mainModule ? mainModule : undefined
|
||||
property var accountSettings: localAccountSettings
|
||||
property var privacyModule: profileSectionModuleInst.privacyModule
|
||||
property string displayName: userProfile !== undefined ? userProfile.displayName : ""
|
||||
|
||||
property url profImgUrl: ""
|
||||
property real profImgAX: 0.0
|
||||
property real profImgAY: 0.0
|
||||
property real profImgBX: 0.0
|
||||
property real profImgBY: 0.0
|
||||
property bool accountCreated: false
|
||||
|
||||
property bool showBeforeGetStartedPopup: true
|
||||
|
||||
function generateImage(source, aX, aY, bX, bY) {
|
||||
profImgUrl = source
|
||||
profImgAX = aX
|
||||
profImgAY = aY
|
||||
profImgBX = bX
|
||||
profImgBY = bY
|
||||
return onboardingModuleInst.generateImage(source, aX, aY, bX, bY)
|
||||
}
|
||||
|
||||
function importMnemonic(mnemonic) {
|
||||
onboardingModuleInst.importMnemonic(mnemonic)
|
||||
}
|
||||
|
||||
function setCurrentAccountAndDisplayName(displayName) {
|
||||
onboardingModuleInst.setDisplayName(displayName);
|
||||
if (!onboardingModuleInst.importedAccountPubKey) {
|
||||
onboardingModuleInst.setSelectedAccountByIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
function updatedDisplayName(displayName) {
|
||||
if (displayName !== root.displayName) {
|
||||
print(displayName, root.displayName)
|
||||
root.profileModule.setDisplayName(displayName);
|
||||
}
|
||||
}
|
||||
|
||||
function saveImage() {
|
||||
root.profileModule.upload(root.profImgUrl, root.profImgAX, root.profImgAY, root.profImgBX, root.profImgBY);
|
||||
}
|
||||
|
||||
function setImageProps(source, aX, aY, bX, bY) {
|
||||
root.profImgUrl = source;
|
||||
root.profImgAX = aX;
|
||||
root.profImgAY = aY;
|
||||
root.profImgBX = bX;
|
||||
root.profImgBY = bY;
|
||||
}
|
||||
|
||||
function clearImageProps() {
|
||||
root.profImgUrl = "";
|
||||
root.profImgAX = 0.0;
|
||||
root.profImgAY = 0.0;
|
||||
root.profImgBX = 0.0;
|
||||
root.profImgBY = 0.0;
|
||||
}
|
||||
|
||||
function removeImage() {
|
||||
return root.profileModule.remove();
|
||||
}
|
||||
|
||||
function finishCreatingAccount(pass) {
|
||||
root.onboardingModuleInst.storeSelectedAccountAndLogin(pass);
|
||||
}
|
||||
|
||||
function storeToKeyChain(pass) {
|
||||
mainModule.storePassword(pass);
|
||||
}
|
||||
|
||||
function changePassword(password, newPassword) {
|
||||
root.privacyModule.changePassword(password, newPassword);
|
||||
}
|
||||
|
||||
function validateMnemonic(text) {
|
||||
return root.onboardingModuleInst.validateMnemonic(text);
|
||||
}
|
||||
|
||||
property ListModel accountsSampleData: ListModel {
|
||||
ListElement {
|
||||
username: "Ferocious Herringbone Sinewave2"
|
||||
address: "0x123456789009876543211234567890"
|
||||
}
|
||||
ListElement {
|
||||
username: "Another Account"
|
||||
address: "0x123456789009876543211234567890"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import QtQuick 2.14
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
property var startupModuleInst: startupModule
|
||||
property var currentStartupState: startupModuleInst.currentStartupState
|
||||
property var selectedLoginAccount: startupModuleInst.selectedLoginAccount
|
||||
|
||||
function backAction() {
|
||||
root.currentStartupState.backAction()
|
||||
}
|
||||
|
||||
function doPrimaryAction() {
|
||||
root.currentStartupState.doPrimaryAction()
|
||||
}
|
||||
|
||||
function doSecondaryAction() {
|
||||
root.currentStartupState.doSecondaryAction()
|
||||
}
|
||||
|
||||
function doTertiaryAction() {
|
||||
root.currentStartupState.doTertiaryAction()
|
||||
}
|
||||
|
||||
function showBeforeGetStartedPopup() {
|
||||
return root.startupModuleInst.showBeforeGetStartedPopup()
|
||||
}
|
||||
|
||||
function beforeGetStartedPopupAccepted() {
|
||||
root.startupModuleInst.beforeGetStartedPopupAccepted()
|
||||
}
|
||||
|
||||
function generateImage(source, aX, aY, bX, bY) {
|
||||
return root.startupModuleInst.generateImage(source, aX, aY, bX, bY)
|
||||
}
|
||||
|
||||
function setDisplayName(value) {
|
||||
root.startupModuleInst.setDisplayName(value)
|
||||
}
|
||||
|
||||
function getDisplayName() {
|
||||
return root.startupModuleInst.getDisplayName()
|
||||
}
|
||||
|
||||
function setPassword(value) {
|
||||
root.startupModuleInst.setPassword(value)
|
||||
}
|
||||
|
||||
function getPassword() {
|
||||
return root.startupModuleInst.getPassword()
|
||||
}
|
||||
|
||||
function getPasswordStrengthScore(password) {
|
||||
let userName = root.startupModuleInst.importedAccountAlias
|
||||
return root.startupModuleInst.getPasswordStrengthScore(password, userName)
|
||||
}
|
||||
|
||||
function validMnemonic(mnemonic) {
|
||||
return root.startupModuleInst.validMnemonic(mnemonic)
|
||||
}
|
||||
|
||||
function setSelectedLoginAccountByIndex(index) {
|
||||
root.startupModuleInst.setSelectedLoginAccountByIndex(index)
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
singleton OnboardingStore 1.0 OnboardingStore.qml
|
||||
singleton LoginStore 1.0 LoginStore.qml
|
||||
singleton KeycardStore 1.0 KeycardStore.qml
|
|
@ -8,14 +8,12 @@ import shared.panels 1.0
|
|||
|
||||
import utils 1.0
|
||||
|
||||
import "../controls"
|
||||
import "../stores"
|
||||
|
||||
OnboardingBasePage {
|
||||
id: page
|
||||
Item {
|
||||
id: root
|
||||
|
||||
signal btnOkClicked()
|
||||
|
||||
backButtonVisible: false
|
||||
property StartupStore startupStore
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
@ -61,7 +59,7 @@ OnboardingBasePage {
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: qsTr("Ok, got it")
|
||||
onClicked: {
|
||||
page.btnOkClicked();
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import QtQuick 2.0
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Dialogs 1.3
|
||||
|
||||
import shared.controls 1.0
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.stores 1.0
|
||||
import utils 1.0
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
|
@ -15,12 +15,17 @@ import StatusQ.Core.Theme 0.1
|
|||
import "../stores"
|
||||
import "../controls"
|
||||
|
||||
OnboardingBasePage {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property StartupStore startupStore
|
||||
|
||||
property string password
|
||||
property string tmpPass
|
||||
property string displayName
|
||||
|
||||
Component.onCompleted: {
|
||||
root.password = root.startupStore.getPassword()
|
||||
}
|
||||
|
||||
function forcePswInputFocus() { confPswInput.forceActiveFocus(Qt.MouseFocusReason)}
|
||||
|
||||
QtObject {
|
||||
|
@ -39,22 +44,7 @@ OnboardingBasePage {
|
|||
return
|
||||
}
|
||||
|
||||
if (OnboardingStore.accountCreated) {
|
||||
if (root.password !== root.tmpPass) {
|
||||
OnboardingStore.changePassword(root.tmpPass, root.password)
|
||||
root.tmpPass = root.password
|
||||
}
|
||||
else {
|
||||
submitBtn.loading = false
|
||||
root.exit();
|
||||
}
|
||||
}
|
||||
else {
|
||||
root.tmpPass = root.password
|
||||
submitBtn.loading = true
|
||||
OnboardingStore.setCurrentAccountAndDisplayName(root.displayName)
|
||||
pause.start()
|
||||
}
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,72 +148,12 @@ OnboardingBasePage {
|
|||
text: qsTr("Finalise Status Password Creation")
|
||||
enabled: !submitBtn.loading && (confPswInput.text === root.password)
|
||||
|
||||
property Timer sim: Timer {
|
||||
id: pause
|
||||
interval: 20
|
||||
onTriggered: {
|
||||
// Create account operation blocks the UI so loading = true; will never have any affect until it is done.
|
||||
// Getting around it with a small pause (timer) in order to get the desired behavior
|
||||
OnboardingStore.finishCreatingAccount(root.password)
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: { d.submit() }
|
||||
|
||||
Connections {
|
||||
target: onboardingModule
|
||||
onAccountSetupError: {
|
||||
if (error === Constants.existingAccountError) {
|
||||
importLoginError.title = qsTr("Keys for this account already exist")
|
||||
importLoginError.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase")
|
||||
} else {
|
||||
importLoginError.title = qsTr("Login failed")
|
||||
importLoginError.text = qsTr("Login failed. Please re-enter your password and try again.")
|
||||
}
|
||||
importLoginError.open()
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
id: importLoginError
|
||||
title: qsTr("Login failed")
|
||||
text: qsTr("Login failed. Please re-enter your password and try again.")
|
||||
icon: StandardIcon.Critical
|
||||
standardButtons: StandardButton.Ok
|
||||
onVisibilityChanged: {
|
||||
submitBtn.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Back button:
|
||||
StatusRoundButton {
|
||||
enabled: !submitBtn.loading
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Style.current.padding
|
||||
icon.name: "arrow-left"
|
||||
onClicked: { root.backClicked() }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: startupModule
|
||||
onAppStateChanged: {
|
||||
if (state === Constants.appState.main) {
|
||||
if (!!OnboardingStore.profImgUrl) {
|
||||
OnboardingStore.saveImage();
|
||||
OnboardingStore.accountCreated = true;
|
||||
}
|
||||
submitBtn.loading = false
|
||||
root.exit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: OnboardingStore.privacyModule
|
||||
target: RootStore.privacyModule
|
||||
onPasswordChanged: {
|
||||
if (success) {
|
||||
submitBtn.loading = false
|
||||
|
|
|
@ -8,12 +8,18 @@ import shared.views 1.0
|
|||
|
||||
import "../../Profile/views"
|
||||
import "../controls"
|
||||
import "../stores"
|
||||
|
||||
OnboardingBasePage {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string newPassword
|
||||
property string confirmationPassword
|
||||
property StartupStore startupStore
|
||||
|
||||
Component.onCompleted: {
|
||||
view.newPswText = root.startupStore.getPassword()
|
||||
view.confirmationPswText = root.startupStore.getPassword()
|
||||
}
|
||||
|
||||
function forceNewPswInputFocus() { view.forceNewPswInputFocus() }
|
||||
|
||||
QtObject {
|
||||
|
@ -22,9 +28,8 @@ OnboardingBasePage {
|
|||
readonly property int zFront: 100
|
||||
|
||||
function submit() {
|
||||
root.newPassword = view.newPswText
|
||||
root.confirmationPassword = view.confirmationPswText
|
||||
root.exit()
|
||||
root.startupStore.setPassword(view.newPswText)
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,9 +39,7 @@ OnboardingBasePage {
|
|||
z: view.zFront
|
||||
PasswordView {
|
||||
id: view
|
||||
onboarding: true
|
||||
newPswText: root.newPassword
|
||||
confirmationPswText: root.confirmationPassword
|
||||
passwordStrengthScoreFunction: root.startupStore.getPasswordStrengthScore
|
||||
onReturnPressed: { if(view.ready) d.submit() }
|
||||
}
|
||||
StatusButton {
|
||||
|
@ -48,21 +51,4 @@ OnboardingBasePage {
|
|||
onClicked: { d.submit() }
|
||||
}
|
||||
}
|
||||
|
||||
// Back button:
|
||||
StatusRoundButton {
|
||||
z: d.zFront // Focusable / clickable component
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Style.current.padding
|
||||
icon.name: "arrow-left"
|
||||
onClicked: { root.backClicked() }
|
||||
}
|
||||
// By clicking anywhere outside password entries fields or focusable element in the view, it is needed to check if passwords entered matches
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
z: d.zBehind // Behind focusable components
|
||||
onClicked: { view.checkPasswordMatches() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import shared.panels 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import "../controls"
|
||||
import "../panels"
|
||||
import "../stores"
|
||||
|
||||
|
||||
OnboardingBasePage {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
Behavior on opacity { NumberAnimation { duration: 200 }}
|
||||
state: "username"
|
||||
|
||||
signal keysGenerated()
|
||||
|
||||
function gotoKeysStack(stackIndex) { createKeysStack.currentIndex = stackIndex }
|
||||
|
||||
enum KeysStack {
|
||||
DETAILS,
|
||||
CREATE_PWD,
|
||||
CONFRIM_PWD,
|
||||
TOUCH_ID
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
property string newPassword
|
||||
property string confirmationPassword
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: createKeysStack
|
||||
anchors.fill: parent
|
||||
currentIndex: GenKeyView.KeysStack.DETAILS
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
// Set focus:
|
||||
if(currentIndex === GenKeyView.KeysStack.CREATE_PWD)
|
||||
createPswView.forceNewPswInputFocus()
|
||||
else if(currentIndex === GenKeyView.KeysStack.CONFRIM_PWD)
|
||||
confirmPswView.forcePswInputFocus()
|
||||
}
|
||||
|
||||
InsertDetailsView {
|
||||
id: userDetailsPanel
|
||||
onCreatePassword: { gotoKeysStack(GenKeyView.KeysStack.CREATE_PWD) }
|
||||
}
|
||||
CreatePasswordView {
|
||||
id: createPswView
|
||||
newPassword: d.newPassword
|
||||
confirmationPassword: d.confirmationPassword
|
||||
|
||||
onExit: {
|
||||
d.newPassword = newPassword
|
||||
d.confirmationPassword = confirmationPassword
|
||||
gotoKeysStack(GenKeyView.KeysStack.CONFRIM_PWD)
|
||||
}
|
||||
onBackClicked: {
|
||||
d.newPassword = ""
|
||||
d.confirmationPassword = ""
|
||||
gotoKeysStack(GenKeyView.KeysStack.DETAILS)
|
||||
}
|
||||
}
|
||||
ConfirmPasswordView {
|
||||
id: confirmPswView
|
||||
password: d.newPassword
|
||||
displayName: userDetailsPanel.displayName
|
||||
onExit: {
|
||||
if (Qt.platform.os == "osx") {
|
||||
gotoKeysStack(GenKeyView.KeysStack.TOUCH_ID);
|
||||
} else {
|
||||
root.keysGenerated();
|
||||
}
|
||||
}
|
||||
onBackClicked: { gotoKeysStack(GenKeyView.KeysStack.CREATE_PWD) }
|
||||
}
|
||||
TouchIDAuthView {
|
||||
userPass: d.newPassword
|
||||
onGenKeysDone: { root.keysGenerated() }
|
||||
}
|
||||
}
|
||||
|
||||
onBackClicked: {
|
||||
if (userDetailsPanel.state === "chatkey") {
|
||||
userDetailsPanel.state = "username";
|
||||
} else {
|
||||
root.exit();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,25 +22,26 @@ import "../shared"
|
|||
Item {
|
||||
id: root
|
||||
|
||||
property StartupStore startupStore
|
||||
|
||||
property string pubKey
|
||||
property string address
|
||||
property string displayName
|
||||
signal createPassword()
|
||||
|
||||
state: "username"
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!!OnboardingStore.onboardingModuleInst.importedAccountPubKey) {
|
||||
root.address = OnboardingStore.onboardingModuleInst.importedAccountAddress ;
|
||||
root.pubKey = OnboardingStore.onboardingModuleInst.importedAccountPubKey;
|
||||
if (!!root.startupStore.startupModuleInst.importedAccountPubKey) {
|
||||
root.address = root.startupStore.startupModuleInst.importedAccountAddress ;
|
||||
root.pubKey = root.startupStore.startupModuleInst.importedAccountPubKey;
|
||||
}
|
||||
nameInput.text = root.startupStore.getDisplayName();
|
||||
nameInput.input.edit.forceActiveFocus();
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !OnboardingStore.onboardingModuleInst.importedAccountPubKey
|
||||
active: !root.startupStore.startupModuleInst.importedAccountPubKey
|
||||
sourceComponent: StatusListView {
|
||||
model: OnboardingStore.onboardingModuleInst.accountsModel
|
||||
model: root.startupStore.startupModuleInst.generatedAccountsModel
|
||||
delegate: Item {
|
||||
Component.onCompleted: {
|
||||
if (index === 0) {
|
||||
|
@ -66,7 +67,7 @@ Item {
|
|||
|
||||
StyledText {
|
||||
id: txtDesc
|
||||
Layout.preferredWidth: (root.state === "username") ? 338 : 643
|
||||
Layout.preferredWidth: root.state === Constants.startupState.userProfileCreate? 338 : 643
|
||||
Layout.preferredHeight: 44
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
Layout.topMargin: Style.current.padding
|
||||
|
@ -211,16 +212,11 @@ Item {
|
|||
enabled: !!nameInput.text && nameInput.valid
|
||||
text: qsTr("Next")
|
||||
onClicked: {
|
||||
if (root.state === "username") {
|
||||
if (OnboardingStore.accountCreated) {
|
||||
OnboardingStore.updatedDisplayName(nameInput.text);
|
||||
}
|
||||
OnboardingStore.displayName = nameInput.text;
|
||||
if (root.state === Constants.startupState.userProfileCreate) {
|
||||
root.startupStore.setDisplayName(nameInput.text)
|
||||
root.displayName = nameInput.text;
|
||||
root.state = "chatkey";
|
||||
} else {
|
||||
createPassword();
|
||||
}
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,7 +226,7 @@ Item {
|
|||
title: qsTr("Profile picture")
|
||||
acceptButtonText: qsTr("Make this my profile picture")
|
||||
onImageCropped: {
|
||||
const croppedImg = OnboardingStore.generateImage(image,
|
||||
const croppedImg = root.startupStore.generateImage(image,
|
||||
cropRect.x.toFixed(),
|
||||
cropRect.y.toFixed(),
|
||||
(cropRect.x + cropRect.width).toFixed(),
|
||||
|
@ -242,7 +238,8 @@ Item {
|
|||
|
||||
states: [
|
||||
State {
|
||||
name: "username"
|
||||
name: Constants.startupState.userProfileCreate
|
||||
when: root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileCreate
|
||||
PropertyChanges {
|
||||
target: usernameText
|
||||
text: qsTr("Your profile")
|
||||
|
@ -273,7 +270,8 @@ Item {
|
|||
}
|
||||
},
|
||||
State {
|
||||
name: "chatkey"
|
||||
name: Constants.startupState.userProfileChatKey
|
||||
when: root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileChatKey
|
||||
PropertyChanges {
|
||||
target: usernameText
|
||||
text: qsTr("Your emojihash and identicon ring")
|
||||
|
|
|
@ -11,15 +11,14 @@ import shared 1.0
|
|||
import shared.panels 1.0
|
||||
import "../popups"
|
||||
import "../controls"
|
||||
import "../stores"
|
||||
|
||||
import utils 1.0
|
||||
|
||||
OnboardingBasePage {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
signal buttonClicked()
|
||||
signal keycardLinkClicked()
|
||||
signal seedLinkClicked()
|
||||
property StartupStore startupStore
|
||||
|
||||
Item {
|
||||
id: container
|
||||
|
@ -78,7 +77,7 @@ OnboardingBasePage {
|
|||
enabled: (opacity > 0.1)
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: {
|
||||
root.buttonClicked();
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +96,7 @@ OnboardingBasePage {
|
|||
parent.font.underline = false
|
||||
}
|
||||
onClicked: {
|
||||
root.keycardLinkClicked();
|
||||
root.startupStore.doSecondaryAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +117,7 @@ OnboardingBasePage {
|
|||
parent.font.underline = false
|
||||
}
|
||||
onClicked: {
|
||||
root.seedLinkClicked();
|
||||
root.startupStore.doTertiaryAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +126,8 @@ OnboardingBasePage {
|
|||
|
||||
states: [
|
||||
State {
|
||||
name: "connectkeys"
|
||||
name: Constants.startupState.welcomeOldStatusUser
|
||||
when: root.startupStore.currentStartupState.stateType === Constants.startupState.welcomeOldStatusUser
|
||||
PropertyChanges {
|
||||
target: keysImg
|
||||
width: 160
|
||||
|
@ -157,7 +157,8 @@ OnboardingBasePage {
|
|||
}
|
||||
},
|
||||
State {
|
||||
name: "getkeys"
|
||||
name: Constants.startupState.welcomeNewStatusUser
|
||||
when: root.startupStore.currentStartupState.stateType === Constants.startupState.welcomeNewStatusUser
|
||||
PropertyChanges {
|
||||
target: keysImg
|
||||
width: 160
|
||||
|
@ -187,7 +188,8 @@ OnboardingBasePage {
|
|||
}
|
||||
},
|
||||
State {
|
||||
name: "importseed"
|
||||
name: Constants.startupState.userProfileImportSeedPhrase
|
||||
when: root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileImportSeedPhrase
|
||||
PropertyChanges {
|
||||
target: keysImg
|
||||
width: 257
|
||||
|
|
|
@ -23,20 +23,20 @@ import "../stores"
|
|||
import utils 1.0
|
||||
|
||||
Item {
|
||||
property bool loading: false
|
||||
signal addNewUserClicked()
|
||||
signal addExistingKeyClicked()
|
||||
id: root
|
||||
|
||||
id: loginView
|
||||
anchors.fill: parent
|
||||
property StartupStore startupStore
|
||||
|
||||
property bool loading: false
|
||||
|
||||
function doLogin(password) {
|
||||
if (loading || password.length === 0)
|
||||
return
|
||||
|
||||
loading = true
|
||||
LoginStore.login(password)
|
||||
txtPassword.textField.clear()
|
||||
root.startupStore.setPassword(password)
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
|
||||
function resetLogin() {
|
||||
|
@ -57,7 +57,7 @@ Item {
|
|||
|
||||
Connections{
|
||||
id: connection
|
||||
target: LoginStore.loginModuleInst
|
||||
target: root.startupStore.startupModuleInst
|
||||
|
||||
onObtainingPasswordError: {
|
||||
enabled = false
|
||||
|
@ -115,18 +115,19 @@ Item {
|
|||
ConfirmAddExistingKeyModal {
|
||||
id: confirmAddExstingKeyModal
|
||||
onOpenModalClicked: {
|
||||
addExistingKeyClicked()
|
||||
root.startupStore.doTertiaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
SelectAnotherAccountModal {
|
||||
id: selectAnotherAccountModal
|
||||
startupStore: root.startupStore
|
||||
onAccountSelected: {
|
||||
LoginStore.setCurrentAccount(index)
|
||||
root.startupStore.setSelectedLoginAccountByIndex(index)
|
||||
resetLogin()
|
||||
}
|
||||
onOpenModalClicked: {
|
||||
addExistingKeyClicked()
|
||||
root.startupStore.doTertiaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,16 +141,16 @@ Item {
|
|||
|
||||
UserImage {
|
||||
id: userImage
|
||||
image: LoginStore.currentAccount.thumbnailImage
|
||||
name: LoginStore.currentAccount.username
|
||||
colorId: LoginStore.currentAccount.colorId
|
||||
colorHash: LoginStore.currentAccount.colorHash
|
||||
image: root.startupStore.selectedLoginAccount.thumbnailImage
|
||||
name: root.startupStore.selectedLoginAccount.username
|
||||
colorId: root.startupStore.selectedLoginAccount.colorId
|
||||
colorHash: root.startupStore.selectedLoginAccount.colorHash
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: usernameText
|
||||
text: LoginStore.currentAccount.username
|
||||
text: root.startupStore.selectedLoginAccount.username
|
||||
font.pixelSize: 17
|
||||
anchors.left: userImage.right
|
||||
anchors.leftMargin: 16
|
||||
|
@ -182,14 +183,14 @@ Item {
|
|||
dim: false
|
||||
Repeater {
|
||||
id: accounts
|
||||
model: LoginStore.loginModuleInst.accountsModel
|
||||
model: root.startupStore.startupModuleInst.loginAccountsModel
|
||||
delegate: AccountMenuItemPanel {
|
||||
label: model.username
|
||||
image: model.thumbnailImage
|
||||
colorId: model.colorId
|
||||
colorHash: model.colorHash
|
||||
onClicked: {
|
||||
LoginStore.setCurrentAccount(index)
|
||||
root.startupStore.setSelectedLoginAccountByIndex(index)
|
||||
resetLogin()
|
||||
accountsPopup.close()
|
||||
}
|
||||
|
@ -200,7 +201,7 @@ Item {
|
|||
label: qsTr("Add new user")
|
||||
onClicked: {
|
||||
accountsPopup.close()
|
||||
addNewUserClicked();
|
||||
root.startupStore.doSecondaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +210,7 @@ Item {
|
|||
iconSettings.name: "wallet"
|
||||
onClicked: {
|
||||
accountsPopup.close()
|
||||
addExistingKeyClicked();
|
||||
root.startupStore.doTertiaryAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -268,7 +269,7 @@ Item {
|
|||
}
|
||||
|
||||
Connections {
|
||||
target: LoginStore.loginModuleInst
|
||||
target: root.startupStore.startupModuleInst
|
||||
onAccountLoginError: {
|
||||
if (error) {
|
||||
// SQLITE_NOTADB: "file is not a database"
|
||||
|
|
|
@ -15,12 +15,11 @@ import shared.controls 1.0
|
|||
import "../controls"
|
||||
import "../stores"
|
||||
|
||||
OnboardingBasePage {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
state: "existingUser"
|
||||
property StartupStore startupStore
|
||||
|
||||
property bool existingUser: (root.state === "existingUser")
|
||||
property var mnemonicInput: []
|
||||
|
||||
signal seedValidated()
|
||||
|
@ -72,29 +71,6 @@ OnboardingBasePage {
|
|||
return true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: OnboardingStore.onboardingModuleInst
|
||||
onAccountImportError: {
|
||||
if (error === Constants.existingAccountError) {
|
||||
importSeedError.title = qsTr("Keys for this account already exist")
|
||||
importSeedError.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase")
|
||||
} else {
|
||||
importSeedError.title = qsTr("Error importing seed")
|
||||
importSeedError.text = error
|
||||
}
|
||||
importSeedError.open()
|
||||
}
|
||||
onAccountImportSuccess: {
|
||||
root.seedValidated()
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
id: importSeedError
|
||||
icon: StandardIcon.Critical
|
||||
standardButtons: StandardButton.Ok
|
||||
}
|
||||
|
||||
Item {
|
||||
implicitWidth: 565
|
||||
implicitHeight: parent.height
|
||||
|
@ -299,7 +275,9 @@ OnboardingBasePage {
|
|||
function checkMnemonicLength() {
|
||||
submitButton.enabled = (root.mnemonicInput.length === root.tabs[switchTabBar.currentIndex])
|
||||
}
|
||||
text: root.existingUser ? qsTr("Restore Status Profile") : qsTr("Import")
|
||||
text: root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserImportSeedPhrase?
|
||||
qsTr("Import") :
|
||||
qsTr("Restore Status Profile")
|
||||
onClicked: {
|
||||
let mnemonicString = "";
|
||||
var sortTable = mnemonicInput.sort(function (a, b) {
|
||||
|
@ -308,9 +286,9 @@ OnboardingBasePage {
|
|||
for (var i = 0; i < mnemonicInput.length; i++) {
|
||||
mnemonicString += sortTable[i].seed + ((i === (grid.count-1)) ? "" : " ");
|
||||
}
|
||||
if (Utils.isMnemonic(mnemonicString) && !OnboardingStore.validateMnemonic(mnemonicString)) {
|
||||
OnboardingStore.importMnemonic(mnemonicString)
|
||||
if (Utils.isMnemonic(mnemonicString) && root.startupStore.validMnemonic(mnemonicString)) {
|
||||
root.mnemonicInput = [];
|
||||
root.startupStore.doPrimaryAction()
|
||||
} else {
|
||||
invalidSeedTxt.visible = true;
|
||||
enabled = false;
|
||||
|
@ -318,9 +296,4 @@ OnboardingBasePage {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
onBackClicked: {
|
||||
root.mnemonicInput = [];
|
||||
root.exit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,14 +14,10 @@ import "../controls"
|
|||
import "../panels"
|
||||
import "../stores"
|
||||
|
||||
OnboardingBasePage {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string userPass
|
||||
|
||||
signal genKeysDone()
|
||||
|
||||
backButtonVisible: false
|
||||
property StartupStore startupStore
|
||||
|
||||
Item {
|
||||
id: container
|
||||
|
@ -81,9 +77,8 @@ OnboardingBasePage {
|
|||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Yes, use Touch ID")
|
||||
onClicked: {
|
||||
OnboardingStore.accountSettings.storeToKeychainValue = Constants.storeToKeychainValueStore;
|
||||
dimBackground.active = true;
|
||||
OnboardingStore.storeToKeyChain(userPass);
|
||||
dimBackground.active = true
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
|
@ -103,8 +98,7 @@ OnboardingBasePage {
|
|||
parent.font.underline = false
|
||||
}
|
||||
onClicked: {
|
||||
OnboardingStore.accountSettings.storeToKeychainValue = Constants.storeToKeychainValueNever;
|
||||
root.genKeysDone();
|
||||
root.startupStore.doSecondaryAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,16 +113,4 @@ OnboardingBasePage {
|
|||
color: Qt.rgba(0, 0, 0, 0.4)
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
enabled: !!OnboardingStore.mainModuleInst
|
||||
target: OnboardingStore.mainModuleInst
|
||||
onStoringPasswordSuccess: {
|
||||
dimBackground.active = false;
|
||||
root.genKeysDone();
|
||||
}
|
||||
onStoringPasswordError: {
|
||||
dimBackground.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,18 +11,13 @@ import "../stores"
|
|||
|
||||
import utils 1.0
|
||||
|
||||
Page {
|
||||
id: page
|
||||
Item {
|
||||
id: root
|
||||
|
||||
signal btnNewUserClicked()
|
||||
signal btnExistingUserClicked()
|
||||
|
||||
background: Rectangle {
|
||||
color: Style.current.background
|
||||
}
|
||||
property StartupStore startupStore
|
||||
|
||||
Component.onCompleted: {
|
||||
if (OnboardingStore.showBeforeGetStartedPopup) {
|
||||
if (root.startupStore.showBeforeGetStartedPopup()) {
|
||||
beforeGetStartedModal.open();
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +25,7 @@ Page {
|
|||
BeforeGetStartedModal {
|
||||
id: beforeGetStartedModal
|
||||
onClosed: {
|
||||
OnboardingStore.showBeforeGetStartedPopup = false;
|
||||
root.startupStore.beforeGetStartedPopupAccepted()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +76,7 @@ Page {
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: qsTr("I am new to Status")
|
||||
onClicked: {
|
||||
page.btnNewUserClicked();
|
||||
root.startupStore.doPrimaryAction()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +87,7 @@ Page {
|
|||
anchors.topMargin: Style.current.bigPadding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onClicked: {
|
||||
page.btnExistingUserClicked();
|
||||
root.startupStore.doSecondaryAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import shared 1.0
|
|||
import shared.views 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.controls 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
|
@ -69,6 +70,7 @@ StatusModal {
|
|||
id: view
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.centerIn: parent
|
||||
passwordStrengthScoreFunction: RootStore.getPasswordStrengthScore
|
||||
titleVisible: false
|
||||
introText: qsTr("Change password used to unlock Status on this device & sign transactions.")
|
||||
createNewPsw: false
|
||||
|
|
|
@ -12,7 +12,6 @@ QtObject {
|
|||
|
||||
property var profileSectionModuleInst: profileSectionModule
|
||||
property var privacyModule: profileSectionModuleInst.privacyModule
|
||||
property var onboardingModuleInst: onboardingModule
|
||||
property var userProfileInst: !!userProfile ? userProfile : null
|
||||
property var walletSectionInst: !!walletSection ? walletSection : null
|
||||
property var appSettings: !!localAppSettings ? localAppSettings : null
|
||||
|
@ -109,13 +108,8 @@ QtObject {
|
|||
chatSectionChatContentInputArea.addToRecentsGif(id)
|
||||
}
|
||||
|
||||
function getPasswordStrengthScore(password, onboarding = false) {
|
||||
if (onboarding) {
|
||||
let userName = root.onboardingModuleInst.importedAccountAlias;
|
||||
return root.onboardingModuleInst.getPasswordStrengthScore(password, userName);
|
||||
} else {
|
||||
return root.privacyModule.getPasswordStrengthScore(password);
|
||||
}
|
||||
function getPasswordStrengthScore(password) {
|
||||
return root.privacyModule.getPasswordStrengthScore(password);
|
||||
}
|
||||
|
||||
function isFetchingHistory(address) {
|
||||
|
|
|
@ -22,7 +22,8 @@ Column {
|
|||
property string introText: qsTr("Create a password to unlock Status on this device & sign transactions.")
|
||||
property string recoverText: qsTr("You will not be able to recover this password if it is lost.")
|
||||
property string strengthenText: qsTr("Minimum %1 characters. To strengthen your password consider including:").arg(minPswLen)
|
||||
property bool onboarding: false
|
||||
|
||||
property var passwordStrengthScoreFunction: function () {}
|
||||
|
||||
readonly property int zBehind: 1
|
||||
readonly property int zFront: 100
|
||||
|
@ -221,7 +222,7 @@ Column {
|
|||
d.containsSymbols = d.symbolsValidator(text)
|
||||
|
||||
// Update strength indicator:
|
||||
strengthInditactor.strength = d.convertStrength(RootStore.getPasswordStrengthScore(newPswInput.text, root.onboarding))
|
||||
strengthInditactor.strength = d.convertStrength(root.passwordStrengthScoreFunction(newPswInput.text))
|
||||
if (textField.text.length === confirmPswInput.text.length) {
|
||||
root.checkPasswordMatches(false)
|
||||
}
|
||||
|
|
|
@ -5,10 +5,37 @@ import QtQuick 2.13
|
|||
import StatusQ.Controls.Validators 0.1
|
||||
|
||||
QtObject {
|
||||
|
||||
readonly property QtObject appState: QtObject {
|
||||
readonly property int onboarding: 0
|
||||
readonly property int login: 1
|
||||
readonly property int main: 2
|
||||
readonly property int startup: 0
|
||||
readonly property int main: 1
|
||||
}
|
||||
|
||||
readonly property QtObject startupFlow: QtObject {
|
||||
readonly property string general: "General"
|
||||
readonly property string firstRunNewUserNewKeys: "FirstRunNewUserNewKeys"
|
||||
readonly property string firstRunNewUserNewKeycardKeys: "FirstRunNewUserNewKeycardKeys"
|
||||
readonly property string firstRunNewUserImportSeedPhrase: "FirstRunNewUserImportSeedPhrase"
|
||||
readonly property string firstRunOldUserSyncCode: "FirstRunOldUserSyncCode"
|
||||
readonly property string firstRunOldUserKeycardImport: "FirstRunOldUserKeycardImport"
|
||||
readonly property string firstRunOldUserImportSeedPhrase: "FirstRunOldUserImportSeedPhrase"
|
||||
readonly property string appLogin: "AppLogin"
|
||||
}
|
||||
|
||||
readonly property QtObject startupState: QtObject {
|
||||
readonly property string noState: "NoState"
|
||||
readonly property string allowNotifications: "AllowNotifications"
|
||||
readonly property string welcome: "Welcome"
|
||||
readonly property string welcomeNewStatusUser: "WelcomeNewStatusUser"
|
||||
readonly property string welcomeOldStatusUser: "WelcomeOldStatusUser"
|
||||
readonly property string userProfileCreate: "UserProfileCreate"
|
||||
readonly property string userProfileChatKey: "UserProfileChatKey"
|
||||
readonly property string userProfileCreatePassword: "UserProfileCreatePassword"
|
||||
readonly property string userProfileConfirmPassword: "UserProfileConfirmPassword"
|
||||
readonly property string userProfileImportSeedPhrase: "UserProfileImportSeedPhrase"
|
||||
readonly property string userProfileEnterSeedPhrase: "UserProfileEnterSeedPhrase"
|
||||
readonly property string biometrics: "Biometrics"
|
||||
readonly property string login: "Login"
|
||||
}
|
||||
|
||||
readonly property QtObject appSection: QtObject {
|
||||
|
|
18
ui/main.qml
18
ui/main.qml
|
@ -18,7 +18,6 @@ import mainui 1.0
|
|||
import AppLayouts.Onboarding 1.0
|
||||
|
||||
StatusWindow {
|
||||
property bool hasAccounts: startupModule.appState !== Constants.appState.onboarding
|
||||
property bool appIsReady: false
|
||||
|
||||
Universal.theme: Universal.System
|
||||
|
@ -134,6 +133,9 @@ StatusWindow {
|
|||
if(state === Constants.appState.main) {
|
||||
// We set main module to the Global singleton once user is logged in and we move to the main app.
|
||||
Global.mainModuleInst = mainModule
|
||||
loader.sourceComponent = app
|
||||
startupOnboarding.unload()
|
||||
startupOnboarding.visible = false
|
||||
|
||||
if(localAccountSensitiveSettings.recentEmojis === "") {
|
||||
localAccountSensitiveSettings.recentEmojis = [];
|
||||
|
@ -282,17 +284,9 @@ StatusWindow {
|
|||
}
|
||||
|
||||
OnboardingLayout {
|
||||
hasAccounts: applicationWindow.hasAccounts
|
||||
onLoadApp: {
|
||||
loader.sourceComponent = app;
|
||||
}
|
||||
|
||||
onOnBoardingStepChanged: {
|
||||
loader.sourceComponent = view;
|
||||
if (!!state) {
|
||||
loader.item.state = state;
|
||||
}
|
||||
}
|
||||
id: startupOnboarding
|
||||
anchors.fill: parent
|
||||
visible: !splashScreen.visible
|
||||
}
|
||||
|
||||
NotificationWindow {
|
||||
|
|
Loading…
Reference in New Issue