onboarding process updated, import account feature handled

This commit is contained in:
Sale Djenic 2021-10-14 10:04:15 +02:00 committed by Iuri Matias
parent e9bb9b256e
commit 30f11c06d4
51 changed files with 757 additions and 383 deletions

View File

@ -42,6 +42,7 @@ proc load*(self: AppController)
# Startup Module Delegate Interface # Startup Module Delegate Interface
proc startupDidLoad*(self: AppController) proc startupDidLoad*(self: AppController)
proc accountCreated*(self: AppController)
# Main Module Delegate Interface # Main Module Delegate Interface
proc mainDidLoad*(self: AppController) proc mainDidLoad*(self: AppController)
@ -95,12 +96,14 @@ proc delete*(self: AppController) =
proc startupDidLoad*(self: AppController) = proc startupDidLoad*(self: AppController) =
echo "StartupDidLoad" echo "StartupDidLoad"
singletonInstance.engine.load(newQUrl("qrc:///main.qml"))
# self.login.init() # self.login.init()
# self.onboarding.init() # self.onboarding.init()
proc mainDidLoad*(self: AppController) = proc mainDidLoad*(self: AppController) =
# This to will be adapted to appropriate modules later: # This to will be adapted to appropriate modules later:
self.appService.onLoggedIn() self.appService.onLoggedIn()
self.startupModule.moveToAppState()
# Reset login and onboarding to remove any mnemonic that would have been saved in the accounts list # Reset login and onboarding to remove any mnemonic that would have been saved in the accounts list
# self.login.reset() # self.login.reset()
@ -121,4 +124,8 @@ proc load*(self: AppController) =
self.chatService.init() self.chatService.init()
self.communityService.init() self.communityService.init()
self.mainModule.load() self.mainModule.load()
proc accountCreated*(self: AppController) =
echo "AppController account created"
self.load()

View File

@ -1,29 +1,29 @@
import Tables import Tables
import controller_interface import controller_interface
import io_interface
import ../../../app_service/service/accounts/service_interface as accounts_service import ../../../app_service/service/accounts/service_interface as accounts_service
export controller_interface export controller_interface
type type
Controller*[T: controller_interface.DelegateInterface] = Controller* = ref object of controller_interface.AccessInterface
ref object of controller_interface.AccessInterface delegate: io_interface.AccessInterface
delegate: T
accountsService: accounts_service.ServiceInterface accountsService: accounts_service.ServiceInterface
proc newController*[T](delegate: T, proc newController*(delegate: io_interface.AccessInterface,
accountsService: accounts_service.ServiceInterface): accountsService: accounts_service.ServiceInterface):
Controller[T] = Controller =
result = Controller[T]() result = Controller()
result.delegate = delegate result.delegate = delegate
result.accountsService = accountsService result.accountsService = accountsService
method delete*[T](self: Controller[T]) = method delete*(self: Controller) =
discard discard
method init*[T](self: Controller[T]) = method init*(self: Controller) =
discard discard
method shouldStartWithOnboardingScreen*[T](self: Controller[T]): bool = method shouldStartWithOnboardingScreen*(self: Controller): bool =
return self.accountsService.openedAccounts().len > 0 return self.accountsService.openedAccounts().len == 0

View File

@ -10,9 +10,4 @@ method init*(self: AccessInterface) {.base.} =
method shouldStartWithOnboardingScreen*(self: AccessInterface): bool {.base.} = method shouldStartWithOnboardingScreen*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
type
## Abstract class (concept) which must be implemented by object/s used in this
## module.
DelegateInterface* = concept c

View File

@ -1,22 +1,19 @@
type # Defines how parent module accesses this module
AccessInterface* {.pure inheritable.} = ref object of RootObj include ./private_interfaces/module_base_interface
## Abstract class for any input/interaction with this module. include ./private_interfaces/module_access_interface
method delete*(self: AccessInterface) {.base.} = # Defines how this module view communicates with this module
raise newException(ValueError, "No implementation available") include ./private_interfaces/module_view_delegate_interface
method load*(self: AccessInterface) {.base.} = # Defines how this controller communicates with this module
raise newException(ValueError, "No implementation available") include ./private_interfaces/module_controller_delegate_interface
# View Delegate Interface
# Delegate for the view must be declared here due to use of QtObject and multi
# inheritance, which is not well supported in Nim.
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
# Defines how submodules of this module communicate with this module
include ./private_interfaces/module_onboarding_delegate_interface
include ./private_interfaces/module_login_delegate_interface
# This way (using concepts) is used only for the modules managed by AppController
type type
## Abstract class (concept) which must be implemented by object/s used in this
## module.
DelegateInterface* = concept c DelegateInterface* = concept c
c.startupDidLoad() c.startupDidLoad()
c.accountCreated()

View File

@ -0,0 +1,63 @@
import NimQml
QtObject:
type AccountItem* = ref object of QObject
name: string
identicon: string
keyUid: string
thumbnailImage: string
largeImage: string
proc setup(self: AccountItem) =
self.QObject.setup
proc delete*(self: AccountItem) =
self.QObject.delete
proc setAccountItemData*(self: AccountItem, name, identicon, keyUid,
thumbnailImage, largeImage: string) =
self.name = name
self.identicon = identicon
self.keyUid = keyUid
self.thumbnailImage = thumbnailImage
self.largeImage = largeImage
proc newAccountItem*(): AccountItem =
new(result, delete)
result.setup
proc newAccountItem*(name, identicon, keyUid, thumbnailImage,
largeImage: string): AccountItem =
new(result, delete)
result.setup
result.setAccountItemData(name, identicon, keyUid, thumbnailImage, largeImage)
proc getName(self: AccountItem): string {.slot.} =
result = self.name
QtProperty[string] name:
read = getName
proc getIdenticon(self: AccountItem): string {.slot.} =
result = self.identicon
QtProperty[string] identicon:
read = getIdenticon
proc getKeyUid(self: AccountItem): string {.slot.} =
result = self.keyUid
QtProperty[string] keyUid:
read = getKeyUid
proc getThumbnailImage(self: AccountItem): string {.slot.} =
result = self.thumbnailImage
QtProperty[string] thumbnailImage:
read = getThumbnailImage
proc getLargeImage(self: AccountItem): string {.slot.} =
result = self.largeImage
QtProperty[string] largeImage:
read = getLargeImage

View File

@ -1,6 +1,7 @@
import Tables import Tables
import controller_interface import controller_interface
import io_interface
import status/[signals] import status/[signals]
import ../../../../app_service/[main] import ../../../../app_service/[main]
@ -9,25 +10,27 @@ import ../../../../app_service/service/accounts/service_interface as accounts_se
export controller_interface export controller_interface
type type
Controller*[T: controller_interface.DelegateInterface] = Controller* = ref object of controller_interface.AccessInterface
ref object of controller_interface.AccessInterface delegate: io_interface.AccessInterface
delegate: T
appService: AppService appService: AppService
accountsService: accounts_service.ServiceInterface accountsService: accounts_service.ServiceInterface
proc newController*[T](delegate: T, proc newController*(delegate: io_interface.AccessInterface,
appService: AppService, appService: AppService,
accountsService: accounts_service.ServiceInterface): accountsService: accounts_service.ServiceInterface):
Controller[T] = Controller =
result = Controller[T]() result = Controller()
result.delegate = delegate result.delegate = delegate
result.appService = appService result.appService = appService
result.accountsService = accountsService result.accountsService = accountsService
method delete*[T](self: Controller[T]) = method delete*(self: Controller) =
discard discard
method init*[T](self: Controller[T]) = method getOpenedAccounts*(self: Controller): seq[AccountDto] =
return self.accountsService.openedAccounts()
method init*(self: Controller) =
self.appService.status.events.on(SignalType.NodeStopped.event) do(e:Args): self.appService.status.events.on(SignalType.NodeStopped.event) do(e:Args):
echo "-NEW-LOGIN-- NodeStopped: ", repr(e) echo "-NEW-LOGIN-- NodeStopped: ", repr(e)
#self.status.events.emit("nodeStopped", Args()) #self.status.events.emit("nodeStopped", Args())

View File

@ -1,4 +1,4 @@
#import ../../../../app_service/service/community/service_interface as community_service import ../../../../app_service/service/accounts/service_interface
type type
AccessInterface* {.pure inheritable.} = ref object of RootObj AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -9,10 +9,6 @@ method delete*(self: AccessInterface) {.base.} =
method init*(self: AccessInterface) {.base.} = method init*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getOpenedAccounts*(self: AccessInterface): seq[AccountDto] {.base.} =
type raise newException(ValueError, "No implementation available")
## Abstract class (concept) which must be implemented by object/s used in this
## module.
DelegateInterface* = concept c

View File

@ -1,24 +1,12 @@
type # Defines how parent module accesses this module
AccessInterface* {.pure inheritable.} = ref object of RootObj include ./private_interfaces/module_base_interface
## Abstract class for any input/interaction with this module. include ./private_interfaces/module_access_interface
method delete*(self: AccessInterface) {.base.} = # Defines how this module view communicates with this module
raise newException(ValueError, "No implementation available") include ./private_interfaces/module_view_delegate_interface
method load*(self: AccessInterface) {.base.} = # Defines how this controller communicates with this module
raise newException(ValueError, "No implementation available") include ./private_interfaces/module_controller_delegate_interface
method isLoaded*(self: AccessInterface): bool {.base.} = # Defines how submodules of this module communicate with this module
raise newException(ValueError, "No implementation available") # will be added if needed
# View Delegate Interface
# Delegate for the view must be declared here due to use of QtObject and multi
# inheritance, which is not well supported in Nim.
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
type
## Abstract class (concept) which must be implemented by object/s used in this
## module.
DelegateInterface* = concept c
c.loginDidLoad()

View File

@ -1,21 +0,0 @@
import NimQml
QtObject:
type
Item* = ref object of QObject
proc setup(self: Item) =
self.QObject.setup
proc delete*(self: Item) =
self.QObject.delete
proc newItem*(): Item =
new(result, delete)
result.setup()
proc id*(self: Item): string {.slot.} =
self.id
QtProperty[string] id:
read = id

View File

@ -1,17 +1,60 @@
import NimQml import NimQml, Tables, strutils, strformat
import item
import account_item
type
ModelRole {.pure.} = enum
Account = UserRole + 1
QtObject: QtObject:
type type
Model* = ref object of QAbstractListModel Model* = ref object of QAbstractListModel
sections: seq[Item] items: seq[AccountItem]
proc setup(self: Model) = proc delete(self: Model) =
self.QAbstractListModel.setup self.items = @[]
proc delete*(self: Model) =
self.QAbstractListModel.delete self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc newModel*(): Model = proc newModel*(): Model =
new(result, delete) new(result, delete)
result.setup result.setup
proc countChanged*(self: Model) {.signal.}
proc count*(self: Model): int {.slot.} =
self.items.len
QtProperty[int] count:
read = count
notify = countChanged
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
ModelRole.Account.int:"account"
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.items.len):
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.Account:
result = newQVariant(item)
proc setItems*(self: Model, items: seq[AccountItem]) =
self.beginResetModel()
self.items = items
self.endResetModel()
self.countChanged()

View File

@ -1,5 +1,7 @@
import NimQml import NimQml
import io_interface, view, controller import io_interface
import ../io_interface as delegate_interface
import view, controller, account_item
import ../../../../app/boot/global_singleton import ../../../../app/boot/global_singleton
import ../../../../app_service/[main] import ../../../../app_service/[main]
@ -8,42 +10,68 @@ import ../../../../app_service/service/accounts/service_interface as accounts_se
export io_interface export io_interface
type type
Module* [T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface Module* = ref object of io_interface.AccessInterface
delegate: T delegate: delegate_interface.AccessInterface
view: View view: View
viewVariant: QVariant viewVariant: QVariant
controller: controller.AccessInterface controller: controller.AccessInterface
moduleLoaded: bool moduleLoaded: bool
proc newModule*[T](delegate: T, proc newModule*(delegate: delegate_interface.AccessInterface,
appService: AppService, appService: AppService,
accountsService: accounts_service.ServiceInterface): accountsService: accounts_service.ServiceInterface):
Module[T] = Module =
result = Module[T]() result = Module()
result.delegate = delegate result.delegate = delegate
result.view = view.newView(result) result.view = view.newView(result)
result.viewVariant = newQVariant(result.view) result.viewVariant = newQVariant(result.view)
result.controller = controller.newController[Module[T]](result, appService, result.controller = controller.newController(result, appService, accountsService)
accountsService)
result.moduleLoaded = false result.moduleLoaded = false
singletonInstance.engine.setRootContextProperty("loginModule", result.viewVariant) singletonInstance.engine.setRootContextProperty("loginModule", result.viewVariant)
method delete*[T](self: Module[T]) = method delete*(self: Module) =
self.view.delete self.view.delete
self.viewVariant.delete self.viewVariant.delete
self.controller.delete self.controller.delete
method load*[T](self: Module[T]) = 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) singletonInstance.engine.setRootContextProperty("loginModule", self.viewVariant)
self.controller.init() self.controller.init()
self.view.load() self.view.load()
self.moduleLoaded = true let openedAccounts = self.controller.getOpenedAccounts()
self.delegate.loginDidLoad() if(openedAccounts.len > 0):
var accounts: seq[AccountItem]
for acc in openedAccounts:
var thumbnailImage: string
var largeImage: string
self.extractImages(acc, thumbnailImage, largeImage)
accounts.add(newAccountItem(acc.name, acc.identicon, acc.keyUid,
thumbnailImage, largeImage))
method isLoaded*[T](self: Module[T]): bool = self.view.setAccountsList(accounts)
# set the first account as a slected one
let selected = openedAccounts[0]
var thumbnailImage: string
var largeImage: string
self.extractImages(selected, thumbnailImage, largeImage)
self.view.setSelectedAccount(selected.name, selected.identicon, selected.keyUid,
thumbnailImage, largeImage)
method isLoaded*(self: Module): bool =
return self.moduleLoaded return self.moduleLoaded
method viewDidLoad*(self: Module) = method viewDidLoad*(self: Module) =
discard self.moduleLoaded = true
self.delegate.loginDidLoad()

View File

@ -0,0 +1,5 @@
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
# Since nim doesn't support using concepts in second level nested types we
# define delegate interfaces within access interface.

View File

@ -0,0 +1,2 @@
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,22 +1,59 @@
import NimQml import NimQml
import model import account_item, model
import io_interface import io_interface
QtObject: QtObject:
type type
View* = ref object of QObject View* = ref object of QObject
delegate: io_interface.AccessInterface delegate: io_interface.AccessInterface
selectedAccount: AccountItem
selectedAccountVariant: QVariant
model: Model model: Model
modelVariant: QVariant
proc delete*(self: View) = proc delete*(self: View) =
self.selectedAccount.delete
self.selectedAccountVariant.delete
self.model.delete self.model.delete
self.modelVariant.delete
self.QObject.delete self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View = proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete) new(result, delete)
result.QObject.setup result.QObject.setup
result.delegate = delegate result.delegate = delegate
result.selectedAccount = newAccountItem()
result.selectedAccountVariant = newQVariant(result.selectedAccount)
result.model = newModel() result.model = newModel()
result.modelVariant = newQVariant(result.model)
proc load*(self: View) = proc load*(self: View) =
self.delegate.viewDidLoad() self.delegate.viewDidLoad()
proc selectedAccountChanged*(self: View) {.signal.}
proc getSelectedAccount(self: View): QVariant {.slot.} =
return self.selectedAccountVariant
proc setSelectedAccount*(self: View, name, identicon, keyUid, thumbnailImage,
largeImage: string) =
self.selectedAccount.setAccountItemData(name, identicon, keyUid, thumbnailImage,
largeImage)
self.selectedAccountChanged()
QtProperty[QVariant] selectedAccount:
read = getSelectedAccount
notify = selectedAccountChanged
proc modelChanged*(self: View) {.signal.}
proc getModel(self: View): QVariant {.slot.} =
return self.modelVariant
proc setAccountsList*(self: View, accounts: seq[AccountItem]) =
self.model.setItems(accounts)
self.modelChanged()
QtProperty[QVariant] accountsModel:
read = getModel
notify = modelChanged

View File

@ -1,6 +1,7 @@
import NimQml import NimQml
import io_interface, view, controller import io_interface
import view, controller
import ../../../app/boot/global_singleton import ../../../app/boot/global_singleton
import onboarding/module as onboarding_module import onboarding/module as onboarding_module
@ -20,20 +21,6 @@ type
onboardingModule: onboarding_module.AccessInterface onboardingModule: onboarding_module.AccessInterface
loginModule: login_module.AccessInterface loginModule: login_module.AccessInterface
#################################################
# Forward declaration section
# Controller Delegate Interface
# Onboarding Module Delegate Interface
proc onboardingDidLoad*[T](self: Module[T])
# Login Module Delegate Interface
proc loginDidLoad*[T](self: Module[T])
#################################################
proc newModule*[T](delegate: T, proc newModule*[T](delegate: T,
appService: AppService, appService: AppService,
accountsService: accounts_service.ServiceInterface): accountsService: accounts_service.ServiceInterface):
@ -42,13 +29,11 @@ proc newModule*[T](delegate: T,
result.delegate = delegate result.delegate = delegate
result.view = view.newView(result) result.view = view.newView(result)
result.viewVariant = newQVariant(result.view) result.viewVariant = newQVariant(result.view)
result.controller = controller.newController[Module[T]](result, accountsService) result.controller = controller.newController(result, accountsService)
# Submodules # Submodules
result.onboardingModule = onboarding_module.newModule[Module[T]](result, result.onboardingModule = onboarding_module.newModule(result, appService, accountsService)
appService, accountsService) result.loginModule = login_module.newModule(result, appService, accountsService)
result.loginModule = login_module.newModule[Module[T]](result, appService,
accountsService)
method delete*[T](self: Module[T]) = method delete*[T](self: Module[T]) =
self.onboardingModule.delete self.onboardingModule.delete
@ -61,14 +46,15 @@ method load*[T](self: Module[T]) =
singletonInstance.engine.setRootContextProperty("startupModule", self.viewVariant) singletonInstance.engine.setRootContextProperty("startupModule", self.viewVariant)
self.controller.init() self.controller.init()
self.view.load() self.view.load()
self.view.setStartWithOnboardingScreen(self.controller.shouldStartWithOnboardingScreen())
var initialAppState = AppState.OnboardingState
if(not self.controller.shouldStartWithOnboardingScreen()):
initialAppState = AppState.LoginState
self.view.setAppState(initialAppState)
self.onboardingModule.load() self.onboardingModule.load()
self.loginModule.load() self.loginModule.load()
method viewDidLoad*[T](self: Module[T]) =
discard
proc checkIfModuleDidLoad[T](self: Module[T]) = proc checkIfModuleDidLoad[T](self: Module[T]) =
if(not self.onboardingModule.isLoaded()): if(not self.onboardingModule.isLoaded()):
return return
@ -78,8 +64,17 @@ proc checkIfModuleDidLoad[T](self: Module[T]) =
self.delegate.startupDidLoad() self.delegate.startupDidLoad()
proc onboardingDidLoad*[T](self: Module[T]) = method viewDidLoad*[T](self: Module[T]) =
self.checkIfModuleDidLoad() self.checkIfModuleDidLoad()
proc loginDidLoad*[T](self: Module[T]) = method onboardingDidLoad*[T](self: Module[T]) =
self.checkIfModuleDidLoad() self.checkIfModuleDidLoad()
method loginDidLoad*[T](self: Module[T]) =
self.checkIfModuleDidLoad()
method accountCreated*[T](self: Module[T]) =
self.delegate.accountCreated()
method moveToAppState*[T](self: Module[T]) =
self.view.setAppState(AppState.MainAppState)

View File

@ -1,6 +1,7 @@
import Tables import Tables, chronicles
import controller_interface import controller_interface
import io_interface
import status/[signals] import status/[signals]
import ../../../../app_service/[main] import ../../../../app_service/[main]
@ -8,39 +9,62 @@ import ../../../../app_service/service/accounts/service_interface as accounts_se
export controller_interface export controller_interface
logScope:
topics = "onboarding-controller"
type type
Controller*[T: controller_interface.DelegateInterface] = Controller* =
ref object of controller_interface.AccessInterface ref object of controller_interface.AccessInterface
delegate: T delegate: io_interface.AccessInterface
appService: AppService appService: AppService
accountsService: accounts_service.ServiceInterface accountsService: accounts_service.ServiceInterface
selectedAccountId: string selectedAccountId: string
proc newController*[T](delegate: T, proc newController*(delegate: io_interface.AccessInterface,
appService: AppService, appService: AppService,
accountsService: accounts_service.ServiceInterface): accountsService: accounts_service.ServiceInterface):
Controller[T] = Controller =
result = Controller[T]() result = Controller()
result.delegate = delegate result.delegate = delegate
result.appService = appService result.appService = appService
result.accountsService = accountsService result.accountsService = accountsService
method delete*[T](self: Controller[T]) = method delete*(self: Controller) =
discard discard
method init*[T](self: Controller[T]) = method init*(self: Controller) =
self.appService.status.events.on(SignalType.NodeLogin.event) do(e:Args): self.appService.status.events.on(SignalType.NodeLogin.event) do(e:Args):
echo "-NEW-ONBOARDING-- OnNodeLoginEvent: ", repr(e) let signal = NodeSignal(e)
#self.handleNodeLogin(NodeSignal(e)) echo "-NEW-ONBOARDING-- OnNodeLoginEvent: ", repr(signal)
if signal.event.error == "":
echo "-NEW-ONBOARDING-- OnNodeLoginEventA: ", repr(signal.event.error)
self.delegate.accountCreated()
else:
error "error: ", methodName="init", errDesription = "onboarding login error " & signal.event.error
method getGeneratedAccounts*[T](self: Controller[T]): seq[GeneratedAccountDto] = method getGeneratedAccounts*(self: Controller): seq[GeneratedAccountDto] =
return self.accountsService.generatedAccounts() return self.accountsService.generatedAccounts()
method setSelectedAccountId*[T](self: Controller[T], id: string) = method getImportedAccount*(self: Controller): GeneratedAccountDto =
self.selectedAccountId = id return self.accountsService.getImportedAccount()
method storeSelectedAccountAndLogin*[T](self: Controller[T], password: string) = method setSelectedAccountByIndex*(self: Controller, index: int) =
let account = self.accountsService.setupAccount(self.appService.status.fleet.config, let accounts = self.getGeneratedAccounts()
self.selectedAccountId, password) self.selectedAccountId = accounts[index].id
echo "RECEIVED ACCOUNT: ", repr(account) method storeSelectedAccountAndLogin*(self: Controller, password: string) =
if(not self.accountsService.setupAccount(self.appService.status.fleet.config,
self.selectedAccountId, password)):
self.delegate.setupAccountError()
method validateMnemonic*(self: Controller, mnemonic: string): string =
return self.accountsService.validateMnemonic(mnemonic)
method importMnemonic*(self: Controller, mnemonic: string) =
if(self.accountsService.importMnemonic(mnemonic)):
self.selectedAccountId = self.getImportedAccount().id
self.delegate.importAccountSuccess()
else:
self.delegate.importAccountError()

View File

@ -14,15 +14,19 @@ method getGeneratedAccounts*(self: AccessInterface):
seq[GeneratedAccountDto] {.base.} = seq[GeneratedAccountDto] {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setSelectedAccountId*(self: AccessInterface, id: string) {.base.} = method setSelectedAccountByIndex*(self: AccessInterface, index: int) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method storeSelectedAccountAndLogin*(self: AccessInterface, password: string) method storeSelectedAccountAndLogin*(self: AccessInterface, password: string)
{.base.} = {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getImportedAccount*(self: AccessInterface): GeneratedAccountDto {.base.} =
raise newException(ValueError, "No implementation available")
type method validateMnemonic*(self: AccessInterface, mnemonic: string):
## Abstract class (concept) which must be implemented by object/s used in this string {.base.} =
## module. raise newException(ValueError, "No implementation available")
DelegateInterface* = concept c
method importMnemonic*(self: AccessInterface, mnemonic: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,31 +1,12 @@
type # Defines how parent module accesses this module
AccessInterface* {.pure inheritable.} = ref object of RootObj include ./private_interfaces/module_base_interface
## Abstract class for any input/interaction with this module. include ./private_interfaces/module_access_interface
method delete*(self: AccessInterface) {.base.} = # Defines how this module view communicates with this module
raise newException(ValueError, "No implementation available") include ./private_interfaces/module_view_delegate_interface
method load*(self: AccessInterface) {.base.} = # Defines how this controller communicates with this module
raise newException(ValueError, "No implementation available") include ./private_interfaces/module_controller_delegate_interface
method isLoaded*(self: AccessInterface): bool {.base.} = # Defines how submodules of this module communicate with this module
raise newException(ValueError, "No implementation available") # will be added if needed
# View Delegate Interface
# Delegate for the view must be declared here due to use of QtObject and multi
# inheritance, which is not well supported in Nim.
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method setSelectedAccountId*(self: AccessInterface, id: string) {.base.} =
raise newException(ValueError, "No implementation available")
method storeSelectedAccountAndLogin*(self: AccessInterface, password: string)
{.base.} =
raise newException(ValueError, "No implementation available")
type
## Abstract class (concept) which must be implemented by object/s used in this
## module.
DelegateInterface* = concept c
c.onboardingDidLoad()

View File

@ -32,7 +32,7 @@ QtObject:
method roleNames(self: Model): Table[int, string] = method roleNames(self: Model): Table[int, string] =
{ {
ModelRole.Id.int:"accountId", ModelRole.Id.int:"accountId",
ModelRole.Alias.int:"alias", ModelRole.Alias.int:"username",
ModelRole.Identicon.int:"identicon", ModelRole.Identicon.int:"identicon",
ModelRole.Address.int:"address", ModelRole.Address.int:"address",
ModelRole.KeyUid.int:"keyUid" ModelRole.KeyUid.int:"keyUid"

View File

@ -1,5 +1,7 @@
import NimQml import NimQml
import io_interface, view, controller, item import io_interface
import ../io_interface as delegate_interface
import view, controller, item
import ../../../../app/boot/global_singleton import ../../../../app/boot/global_singleton
import ../../../../app_service/[main] import ../../../../app_service/[main]
@ -8,33 +10,32 @@ import ../../../../app_service/service/accounts/service_interface as accounts_se
export io_interface export io_interface
type type
Module* [T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface Module* = ref object of io_interface.AccessInterface
delegate: T delegate: delegate_interface.AccessInterface
view: View view: View
viewVariant: QVariant viewVariant: QVariant
controller: controller.AccessInterface controller: controller.AccessInterface
moduleLoaded: bool moduleLoaded: bool
proc newModule*[T](delegate: T, proc newModule*(delegate: delegate_interface.AccessInterface,
appService: AppService, appService: AppService,
accountsService: accounts_service.ServiceInterface): accountsService: accounts_service.ServiceInterface):
Module[T] = Module =
result = Module[T]() result = Module()
result.delegate = delegate result.delegate = delegate
result.view = view.newView(result) result.view = view.newView(result)
result.viewVariant = newQVariant(result.view) result.viewVariant = newQVariant(result.view)
result.controller = controller.newController[Module[T]](result, appService, result.controller = controller.newController(result, appService, accountsService)
accountsService)
result.moduleLoaded = false result.moduleLoaded = false
singletonInstance.engine.setRootContextProperty("onboardingModule", result.viewVariant) singletonInstance.engine.setRootContextProperty("onboardingModule", result.viewVariant)
method delete*[T](self: Module[T]) = method delete*(self: Module) =
self.view.delete self.view.delete
self.viewVariant.delete self.viewVariant.delete
self.controller.delete self.controller.delete
method load*[T](self: Module[T]) = method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("onboardingModule", self.viewVariant) singletonInstance.engine.setRootContextProperty("onboardingModule", self.viewVariant)
self.controller.init() self.controller.init()
self.view.load() self.view.load()
@ -44,19 +45,38 @@ method load*[T](self: Module[T]) =
for acc in generatedAccounts: for acc in generatedAccounts:
accounts.add(initItem(acc.id, acc.alias, acc.identicon, acc.address, acc.keyUid)) accounts.add(initItem(acc.id, acc.alias, acc.identicon, acc.address, acc.keyUid))
self.view.setAccountList(accounts) self.view.setAccountList(accounts)
self.moduleLoaded = true method isLoaded*(self: Module): bool =
self.delegate.onboardingDidLoad()
method isLoaded*[T](self: Module[T]): bool =
return self.moduleLoaded return self.moduleLoaded
method viewDidLoad*(self: Module) = method viewDidLoad*(self: Module) =
discard self.moduleLoaded = true
self.delegate.onboardingDidLoad()
method setSelectedAccountId*[T](self: Module[T], id: string) = method setSelectedAccountByIndex*(self: Module, index: int) =
self.controller.setSelectedAccountId(id) self.controller.setSelectedAccountByIndex(index)
method storeSelectedAccountAndLogin*[T](self: Module[T], password: string) = method storeSelectedAccountAndLogin*(self: Module, password: string) =
self.controller.storeSelectedAccountAndLogin(password) self.controller.storeSelectedAccountAndLogin(password)
method accountCreated*(self: Module) =
self.delegate.accountCreated()
method setupAccountError*(self: Module) =
self.view.setupAccountError()
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) =
self.view.importAccountError()
method importAccountSuccess*(self: Module) =
self.view.importAccountSuccess()

View File

@ -0,0 +1,5 @@
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
# Since nim doesn't support using concepts in second level nested types we
# define delegate interfaces within access interface.

View File

@ -0,0 +1,11 @@
method accountCreated*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method setupAccountError*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method importAccountError*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method importAccountSuccess*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,21 @@
import ../../../../../app_service/service/accounts/service_interface
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")

View File

@ -24,20 +24,63 @@ QtObject:
proc load*(self: View) = proc load*(self: View) =
self.delegate.viewDidLoad() self.delegate.viewDidLoad()
proc setAccountList*(self: View, accounts: seq[Item]) =
self.model.setItems(accounts)
proc modelChanged*(self: View) {.signal.} proc modelChanged*(self: View) {.signal.}
proc getModel(self: View): QVariant {.slot.} = proc getModel(self: View): QVariant {.slot.} =
return self.modelVariant return self.modelVariant
proc setAccountList*(self: View, accounts: seq[Item]) =
self.model.setItems(accounts)
self.modelChanged()
QtProperty[QVariant] accountsModel: QtProperty[QVariant] accountsModel:
read = getModel read = getModel
notify = modelChanged notify = modelChanged
proc setSelectedAccountId*(self: View, id: string) {.slot.} = proc importedAccountChanged*(self: View) {.signal.}
self.delegate.setSelectedAccountId(id)
proc getImportedAccountIdenticon*(self: View): string {.slot.} =
return self.delegate.getImportedAccount().identicon
QtProperty[string] importedAccountIdenticon:
read = getImportedAccountIdenticon
notify = importedAccountChanged
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 setSelectedAccountByIndex*(self: View, index: int) {.slot.} =
self.delegate.setSelectedAccountByIndex(index)
proc storeSelectedAccountAndLogin*(self: View, password: string) {.slot.} = proc storeSelectedAccountAndLogin*(self: View, password: string) {.slot.} =
self.delegate.storeSelectedAccountAndLogin(password) self.delegate.storeSelectedAccountAndLogin(password)
proc accountSetupError*(self: View) {.signal.}
proc setupAccountError*(self: View) =
self.accountSetupError()
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) {.signal.}
proc importAccountError*(self: View) =
self.accountImportError() # In QML we can connect to this signal and notify a user
proc importAccountSuccess*(self: View) =
self.importedAccountChanged()

View File

@ -0,0 +1,8 @@
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method moveToAppState*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,5 @@
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
# Since nim doesn't support using concepts in second level nested types we
# define delegate interfaces within access interface.

View File

@ -0,0 +1,2 @@
method loginDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,5 @@
method onboardingDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method accountCreated*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,2 @@
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,11 +1,17 @@
import NimQml import NimQml
import io_interface import io_interface
type
AppState* {.pure.} = enum
OnboardingState = 0
LoginState
MainAppState
QtObject: QtObject:
type type
View* = ref object of QObject View* = ref object of QObject
delegate: io_interface.AccessInterface delegate: io_interface.AccessInterface
startWithOnboardingScreen: bool appState: AppState
proc delete*(self: View) = proc delete*(self: View) =
self.QObject.delete self.QObject.delete
@ -14,24 +20,24 @@ QtObject:
new(result, delete) new(result, delete)
result.QObject.setup result.QObject.setup
result.delegate = delegate result.delegate = delegate
result.startWithOnboardingScreen = true result.appState = AppState.OnboardingState
proc load*(self: View) = proc load*(self: View) =
# In some point, here, we will setup some exposed main module related things. # In some point, here, we will setup some exposed main module related things.
self.delegate.viewDidLoad() self.delegate.viewDidLoad()
proc startWithOnboardingScreenChanged*(self: View) {.signal.} proc appStateChanged*(self: View, state: int) {.signal.}
proc getStartWithOnboardingScreen(self: View): bool {.slot.} = proc getAppState(self: View): int {.slot.} =
return self.startWithOnboardingScreen return self.appState.int
proc setStartWithOnboardingScreen*(self: View, value: bool) {.slot.} = proc setAppState*(self: View, state: AppState) =
if(self.startWithOnboardingScreen == value): if(self.appState == state):
return return
self.startWithOnboardingScreen = value self.appState = state
self.startWithOnboardingScreenChanged() self.appStateChanged(self.appState.int)
QtProperty[bool] startWithOnboardingScreen: QtProperty[int] appState:
read = getStartWithOnboardingScreen read = getAppState
notify = startWithOnboardingScreenChanged notify = appStateChanged

View File

@ -5,7 +5,7 @@ import json
include ../../../common/json_utils include ../../../common/json_utils
type type
Image* = ref object Image* = object
keyUid*: string keyUid*: string
imgType*: string imgType*: string
uri*: string uri*: string
@ -14,7 +14,7 @@ type
fileSize: int fileSize: int
resizeTarget: int resizeTarget: int
type AccountDto* = ref object type AccountDto* = object
name*: string name*: string
timestamp*: int64 timestamp*: int64
identicon*: string identicon*: string
@ -22,6 +22,9 @@ type AccountDto* = ref object
keyUid*: string keyUid*: string
images*: seq[Image] images*: seq[Image]
proc isValid*(self: AccountDto): bool =
result = self.name.len > 0 and self.keyUid.len > 0
proc toImage(jsonObj: JsonNode): Image = proc toImage(jsonObj: JsonNode): Image =
result = Image() result = Image()
discard jsonObj.getProp("keyUid", result.keyUid) discard jsonObj.getProp("keyUid", result.keyUid)

View File

@ -17,7 +17,7 @@ type DerivedAccounts* = object
defaultWallet*: DerivedAccountDetails defaultWallet*: DerivedAccountDetails
eip1581*: DerivedAccountDetails eip1581*: DerivedAccountDetails
type GeneratedAccountDto* = ref object type GeneratedAccountDto* = object
id*: string id*: string
publicKey*: string publicKey*: string
address*: string address*: string
@ -28,6 +28,10 @@ type GeneratedAccountDto* = ref object
alias*: string alias*: string
identicon*: string identicon*: string
proc isValid*(self: GeneratedAccountDto): bool =
result = self.id.len > 0 and self.publicKey.len > 0 and
self.address.len > 0 and self.keyUid.len > 0
proc toDerivedAccountDetails(jsonObj: JsonNode, derivationPath: string): proc toDerivedAccountDetails(jsonObj: JsonNode, derivationPath: string):
DerivedAccountDetails = DerivedAccountDetails =
# Mapping this DTO is not strightforward since only keys are used for id. We # Mapping this DTO is not strightforward since only keys are used for id. We

View File

@ -4,6 +4,7 @@ import service_interface
import ./dto/accounts import ./dto/accounts
import ./dto/generated_accounts import ./dto/generated_accounts
import status/statusgo_backend_new/accounts as status_go import status/statusgo_backend_new/accounts as status_go
import status/statusgo_backend_new/general as status_go_general
import ../../common/[account_constants, utils, string_utils] import ../../common/[account_constants, utils, string_utils]
import ../../../constants as main_constants import ../../../constants as main_constants
@ -17,12 +18,25 @@ const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALL
type type
Service* = ref object of ServiceInterface Service* = ref object of ServiceInterface
generatedAccounts: seq[GeneratedAccountDto] generatedAccounts: seq[GeneratedAccountDto]
loggedInAccount: AccountDto
importedAccount: GeneratedAccountDto
isFirstTimeAccountLogin: bool
method delete*(self: Service) = method delete*(self: Service) =
discard discard
proc newService*(): Service = proc newService*(): Service =
result = Service() result = Service()
result.isFirstTimeAccountLogin = false
method getLoggedInAccount*(self: Service): AccountDto =
return self.loggedInAccount
method getImportedAccount*(self: Service): GeneratedAccountDto =
return self.importedAccount
method isFirstTimeAccountLogin*(self: Service): bool =
return self.isFirstTimeAccountLogin
method init*(self: Service) = method init*(self: Service) =
try: try:
@ -43,6 +57,20 @@ method init*(self: Service) =
except Exception as e: except Exception as e:
error "error: ", methodName="init", errName = e.name, errDesription = e.msg error "error: ", methodName="init", errName = e.name, errDesription = e.msg
method validateMnemonic*(self: Service, mnemonic: string): string =
try:
let response = status_go_general.validateMnemonic(mnemonic)
var error = "response doesn't contain \"error\""
if(response.result.contains("error")):
error = response.result["error"].getStr
# An empty error means that mnemonic is valid.
return error
except Exception as e:
error "error: ", methodName="validateMnemonic", errName = e.name, errDesription = e.msg
method generatedAccounts*(self: Service): seq[GeneratedAccountDto] = method generatedAccounts*(self: Service): seq[GeneratedAccountDto] =
if(self.generatedAccounts.len == 0): if(self.generatedAccounts.len == 0):
error "There was some issue initiating account service" error "There was some issue initiating account service"
@ -82,6 +110,7 @@ proc saveAccountAndLogin(self: Service, hashedPassword: string, account,
error = response.result["error"].getStr error = response.result["error"].getStr
if error == "": if error == "":
debug "Account saved succesfully" debug "Account saved succesfully"
self.isFirstTimeAccountLogin = true
result = toAccountDto(account) result = toAccountDto(account)
return return
@ -91,70 +120,93 @@ proc saveAccountAndLogin(self: Service, hashedPassword: string, account,
except Exception as e: except Exception as e:
error "error: ", methodName="saveAccountAndLogin", errName = e.name, errDesription = e.msg error "error: ", methodName="saveAccountAndLogin", errName = e.name, errDesription = e.msg
proc prepareAccountJsonObject(self: Service, account: GeneratedAccountDto): JsonNode =
result = %* {
"name": account.alias,
"address": account.address,
"identicon": account.identicon,
"key-uid": account.keyUid,
"keycard-pairing": nil
}
proc getAccountDataForAccountId(self: Service, accountId: string): JsonNode = proc getAccountDataForAccountId(self: Service, accountId: string): JsonNode =
for acc in self.generatedAccounts: for acc in self.generatedAccounts:
if(acc.id == accountId): if(acc.id == accountId):
result = %* { return self.prepareAccountJsonObject(acc)
"name": acc.alias,
"address": acc.address, if(self.importedAccount.isValid()):
"identicon": acc.identicon, if(self.importedAccount.id == accountId):
"key-uid": acc.keyUid, return self.prepareAccountJsonObject(self.importedAccount)
"keycard-pairing": nil
} proc prepareSubaccountJsonObject(self: Service, account: GeneratedAccountDto):
JsonNode =
result = %* [
{
"public-key": account.derivedAccounts.defaultWallet.publicKey,
"address": account.derivedAccounts.defaultWallet.address,
"color": "#4360df",
"wallet": true,
"path": PATH_DEFAULT_WALLET,
"name": "Status account"
},
{
"public-key": account.derivedAccounts.whisper.publicKey,
"address": account.derivedAccounts.whisper.address,
"name": account.alias,
"identicon": account.identicon,
"path": PATH_WHISPER,
"chat": true
}
]
proc getSubaccountDataForAccountId(self: Service, accountId: string): JsonNode = proc getSubaccountDataForAccountId(self: Service, accountId: string): JsonNode =
for acc in self.generatedAccounts: for acc in self.generatedAccounts:
if(acc.id == accountId): if(acc.id == accountId):
result = %* [ return self.prepareSubaccountJsonObject(acc)
{
"public-key": acc.derivedAccounts.defaultWallet.publicKey, if(self.importedAccount.isValid()):
"address": acc.derivedAccounts.defaultWallet.address, if(self.importedAccount.id == accountId):
"color": "#4360df", return self.prepareSubaccountJsonObject(self.importedAccount)
"wallet": true,
"path": PATH_DEFAULT_WALLET,
"name": "Status account"
},
{
"public-key": acc.derivedAccounts.whisper.publicKey,
"address": acc.derivedAccounts.whisper.address,
"name": acc.alias,
"identicon": acc.identicon,
"path": PATH_WHISPER,
"chat": true
}
]
proc prepareAccountSettingsJsonObject(self: Service, account: GeneratedAccountDto,
installationId: string): JsonNode =
result = %* {
"key-uid": account.keyUid,
"mnemonic": account.mnemonic,
"public-key": account.derivedAccounts.whisper.publicKey,
"name": account.alias,
"address": account.address,
"eip1581-address": account.derivedAccounts.eip1581.address,
"dapps-address": account.derivedAccounts.defaultWallet.address,
"wallet-root-address": account.derivedAccounts.walletRoot.address,
"preview-privacy?": true,
"signing-phrase": generateSigningPhrase(3),
"log-level": "INFO",
"latest-derived-path": 0,
"networks/networks": DEFAULT_NETWORKS,
"currency": "usd",
"identicon": account.identicon,
"waku-enabled": true,
"wallet/visible-tokens": {
"mainnet": ["SNT"]
},
"appearance": 0,
"networks/current-network": DEFAULT_NETWORK_NAME,
"installation-id": installationId
}
proc getAccountSettings(self: Service, accountId: string, proc getAccountSettings(self: Service, accountId: string,
installationId: string): JsonNode = installationId: string): JsonNode =
for acc in self.generatedAccounts: for acc in self.generatedAccounts:
if(acc.id == accountId): if(acc.id == accountId):
result = %* { return self.prepareAccountSettingsJsonObject(acc, installationId)
"key-uid": acc.keyUid,
"mnemonic": acc.mnemonic,
"public-key": acc.derivedAccounts.whisper.publicKey,
"name": acc.alias,
"address": acc.address,
"eip1581-address": acc.derivedAccounts.eip1581.address,
"dapps-address": acc.derivedAccounts.defaultWallet.address,
"wallet-root-address": acc.derivedAccounts.walletRoot.address,
"preview-privacy?": true,
"signing-phrase": generateSigningPhrase(3),
"log-level": "INFO",
"latest-derived-path": 0,
"networks/networks": DEFAULT_NETWORKS,
"currency": "usd",
"identicon": acc.identicon,
"waku-enabled": true,
"wallet/visible-tokens": {
"mainnet": ["SNT"]
},
"appearance": 0,
"networks/current-network": DEFAULT_NETWORK_NAME,
"installation-id": installationId
}
proc getDefaultNodeConfig*(self: Service, fleetConfig: FleetConfig, installationId: string): if(self.importedAccount.isValid()):
JsonNode = if(self.importedAccount.id == accountId):
return self.prepareAccountSettingsJsonObject(self.importedAccount, installationId)
proc getDefaultNodeConfig*(self: Service, fleetConfig: FleetConfig,
installationId: string): JsonNode =
let networkConfig = getNetworkConfig(DEFAULT_NETWORK_NAME) let networkConfig = getNetworkConfig(DEFAULT_NETWORK_NAME)
let upstreamUrl = networkConfig["config"]["UpstreamConfig"]["URL"] let upstreamUrl = networkConfig["config"]["UpstreamConfig"]["URL"]
let fleet = Fleet.PROD let fleet = Fleet.PROD
@ -184,18 +236,50 @@ proc getDefaultNodeConfig*(self: Service, fleetConfig: FleetConfig, installation
# result["ListenAddr"] = if existsEnv("STATUS_PORT"): newJString("0.0.0.0:" & $getEnv("STATUS_PORT")) else: newJString("0.0.0.0:30305") # result["ListenAddr"] = if existsEnv("STATUS_PORT"): newJString("0.0.0.0:" & $getEnv("STATUS_PORT")) else: newJString("0.0.0.0:30305")
method setupAccount*(self: Service, fleetConfig: FleetConfig, accountId, method setupAccount*(self: Service, fleetConfig: FleetConfig, accountId,
password: string): AccountDto = password: string): bool =
try: try:
let installationId = $genUUID() let installationId = $genUUID()
let accountDataJson = self.getAccountDataForAccountId(accountId) let accountDataJson = self.getAccountDataForAccountId(accountId)
let subaccountDataJson = self.getSubaccountDataForAccountId(accountId) let subaccountDataJson = self.getSubaccountDataForAccountId(accountId)
let settingsJSON = self.getAccountSettings(accountId, installationId) let settingsJson = self.getAccountSettings(accountId, installationId)
let nodeConfig = self.getDefaultNodeConfig(fleetConfig, installationId) let nodeConfigJson = self.getDefaultNodeConfig(fleetConfig, installationId)
if(accountDataJson.isNil or subaccountDataJson.isNil or settingsJson.isNil or
nodeConfigJson.isNil):
let description = "at least one json object is not prepared well"
error "error: ", methodName="setupAccount", errDesription = description
return false
let hashedPassword = hashString(password) let hashedPassword = hashString(password)
discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS) discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS)
return self.saveAccountAndLogin(hashedPassword, accountDataJson,
subaccountDataJson, settingsJSON, nodeConfig) self.loggedInAccount = self.saveAccountAndLogin(hashedPassword,
accountDataJson, subaccountDataJson, settingsJson, nodeConfigJson)
return self.getLoggedInAccount.isValid()
except Exception as e: except Exception as e:
error "error: ", methodName="setupAccount", errName = e.name, errDesription = e.msg error "error: ", methodName="setupAccount", errName = e.name, errDesription = e.msg
return false
method importMnemonic*(self: Service, mnemonic: string): bool =
try:
let response = status_go.multiAccountImportMnemonic(mnemonic)
self.importedAccount = toGeneratedAccountDto(response.result)
let responseDerived = status_go.deriveAccounts(self.importedAccount.id, PATHS)
self.importedAccount.derivedAccounts = toDerivedAccounts(responseDerived.result)
let responseAlias = status_go.generateAlias(
self.importedAccount.derivedAccounts.whisper.publicKey)
self.importedAccount.alias = responseAlias.result.getStr
let responseIdenticon = status_go.generateIdenticon(
self.importedAccount.derivedAccounts.whisper.publicKey)
self.importedAccount.identicon = responseIdenticon.result.getStr
return self.importedAccount.isValid()
except Exception as e:
error "error: ", methodName="importMnemonic", errName = e.name, errDesription = e.msg
return false

View File

@ -26,6 +26,22 @@ method generatedAccounts*(self: ServiceInterface):
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setupAccount*(self: ServiceInterface, fleetConfig: FleetConfig, method setupAccount*(self: ServiceInterface, fleetConfig: FleetConfig,
accountId, password: string): accountId, password: string): bool {.base.} =
AccountDto {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getLoggedInAccount*(self: ServiceInterface): AccountDto {.base.} =
raise newException(ValueError, "No implementation available")
method getImportedAccount*(self: ServiceInterface): GeneratedAccountDto
{.base.} =
raise newException(ValueError, "No implementation available")
method isFirstTimeAccountLogin*(self: ServiceInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method validateMnemonic*(self: ServiceInterface, mnemonic: string):
string {.base.} =
raise newException(ValueError, "No implementation available")
method importMnemonic*(self: ServiceInterface, mnemonic: string): bool {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -269,7 +269,7 @@ proc mainProc() =
# https://doc.qt.io/archives/qtjambi-4.5.2_01/com/trolltech/qt/qtjambi-linguist-programmers.html # https://doc.qt.io/archives/qtjambi-4.5.2_01/com/trolltech/qt/qtjambi-linguist-programmers.html
changeLanguage("en") changeLanguage("en")
singletonInstance.engine.load(newQUrl("qrc:///main.qml")) #singletonInstance.engine.load(newQUrl("qrc:///main.qml"))
# Please note that this must use the `cdecl` calling convention because # Please note that this must use the `cdecl` calling convention because
# it will be passed as a regular C function to statusgo_backend. This means that # it will be passed as a regular C function to statusgo_backend. This means that

View File

@ -10,7 +10,7 @@ import shared.status 1.0
import StatusQ.Controls 0.1 as StatusQControls import StatusQ.Controls 0.1 as StatusQControls
import StatusQ.Components 0.1 import StatusQ.Components 0.1
Rectangle { Rectangle {
id: accountViewDelegate id: accountViewDelegate
property string username: "Jotaro Kujo" property string username: "Jotaro Kujo"
@ -20,7 +20,7 @@ Rectangle {
property var onAccountSelect: function() {} property var onAccountSelect: function() {}
property var isSelected: function() {} property var isSelected: function() {}
property bool selected: { property bool selected: {
return isSelected(accountId, keyUid) return isSelected(index, keyUid)
} }
property bool isHovered: false property bool isHovered: false
@ -85,7 +85,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
onAccountSelect(accountId) onAccountSelect(index)
} }
onEntered: { onEntered: {
accountViewDelegate.isHovered = true accountViewDelegate.isHovered = true

View File

@ -1,37 +1,31 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import "./samples/"
import "../controls"
import utils 1.0 import utils 1.0
ListView { ListView {
property var accounts: AccountsData {} id: accountsView
property var isSelected: function () {} property var isSelected: function () {}
property var onAccountSelect: function () {} property var onAccountSelect: function () {}
id: accountsView
anchors.fill: parent anchors.fill: parent
model: accounts
focus: true focus: true
spacing: Style.current.smallPadding spacing: Style.current.smallPadding
clip: true clip: true
delegate: AccountView { delegate: AccountViewDelegate {
username: model.alias username: model.username
identicon: model.thumbnailImage || model.identicon identicon: model.thumbnailImage || model.identicon
keyUid: model.keyUid keyUid: model.keyUid
address: model.address || '' address: model.address || ''
isSelected: function (accountId, keyUid) { isSelected: function (index, keyUid) {
return accountsView.isSelected(accountId, keyUid) return accountsView.isSelected(index, keyUid)
} }
onAccountSelect: function (accountId) { onAccountSelect: function (index) {
accountsView.onAccountSelect(accountId) accountsView.onAccountSelect(index)
} }
} }
} }
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -1,31 +1,37 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13 import QtGraphicalEffects 1.13
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import utils 1.0 import utils 1.0
import "../shared"
import "./Login"
import "../../../../shared"
import "../../../../shared/popups"
import "../panels"
import "../stores"
// TODO: replace with StatusModal
ModalPopup { ModalPopup {
property string selectedId: "" property int selectedIndex: 0
property var onClosed: function () {} property var onClosed: function () {}
property var onNextClick: function () {} property var onNextClick: function () {}
id: popup id: popup
//% "Choose a chat name" //% "Choose a chat name"
title: qsTrId("intro-wizard-title2") title: qsTrId("intro-wizard-title2")
height: 504
AccountList { AccountListPanel {
id: accountList id: accountList
anchors.fill: parent anchors.fill: parent
interactive: false interactive: false
accounts: onboardingModule.accountsModel model: OnboardingStore.onBoardingModul.accountsModel
isSelected: function (accId) { isSelected: function (index) {
return accId === selectedId return index === selectedIndex
} }
onAccountSelect: function(accId) { onAccountSelect: function(index) {
selectedId = accId selectedIndex = index
} }
} }
footer: StatusRoundButton { footer: StatusRoundButton {
@ -38,14 +44,8 @@ ModalPopup {
icon.width: 20 icon.width: 20
icon.height: 16 icon.height: 16
onClicked : { onClicked : {
onNextClick(selectedId); onNextClick(selectedIndex);
popup.close() popup.close()
} }
} }
} }
/*##^##
Designer {
D{i:0;formeditorColor:"#ffffff";height:500;width:400}
}
##^##*/

View File

@ -40,6 +40,7 @@ ModalPopup {
StatusSmartIdenticon { StatusSmartIdenticon {
id: identicon id: identicon
source: OnboardingStore.onBoardingModul.importedAccountIdenticon
anchors.top: info.bottom anchors.top: info.bottom
anchors.topMargin: Style.current.bigPadding anchors.topMargin: Style.current.bigPadding
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@ -53,7 +54,7 @@ ModalPopup {
anchors.top: identicon.bottom anchors.top: identicon.bottom
anchors.topMargin: Style.current.padding anchors.topMargin: Style.current.padding
anchors.horizontalCenter: identicon.horizontalCenter anchors.horizontalCenter: identicon.horizontalCenter
text: OnboardingStore.currentAccount.username text: OnboardingStore.onBoardingModul.importedAccountAlias
font.weight: Font.Bold font.weight: Font.Bold
font.pixelSize: 15 font.pixelSize: 15
} }
@ -62,7 +63,7 @@ ModalPopup {
anchors.top: username.bottom anchors.top: username.bottom
anchors.topMargin: Style.current.halfPadding anchors.topMargin: Style.current.halfPadding
anchors.horizontalCenter: username.horizontalCenter anchors.horizontalCenter: username.horizontalCenter
text: OnboardingStore.currentAccount.address text: OnboardingStore.onBoardingModul.importedAccountAddress
width: 120 width: 120
} }

View File

@ -149,16 +149,12 @@ ModalPopup {
} }
} }
// Connections { Connections {
// target: onboardingModel target: onboardingModule
// ignoreUnknownSignals: true onAccountSetupError: {
// onLoginResponseChanged: { importLoginError.open()
// if (error) { }
// loading = false }
// importLoginError.open()
// }
// }
// }
onClicked: { onClicked: {
if (storingPasswordModal) if (storingPasswordModal)
@ -170,18 +166,16 @@ ModalPopup {
else else
{ {
loading = true loading = true
// loginModel.isCurrentFlow = false;
// onboardingModel.isCurrentFlow = true;
const result = onboardingModule.storeSelectedAccountAndLogin(repeatPasswordField.text); const result = onboardingModule.storeSelectedAccountAndLogin(repeatPasswordField.text);
const error = JSON.parse(result).error const error = JSON.parse(result).error
if (error) { if (error) {
importError.text += error importError.text += error
return importError.open() return importError.open()
} }
onboardingModel.firstTimeLogin = true
applicationWindow.checkForStoringPassToKeychain(onboardingModel.currentAccount.username, // NEED TO HANDLE IT
repeatPasswordField.text, true) // applicationWindow.checkForStoringPassToKeychain(onboardingModel.currentAccount.username,
// repeatPasswordField.text, true)
} }
} }
} }

View File

@ -3,15 +3,14 @@ pragma Singleton
import QtQuick 2.13 import QtQuick 2.13
QtObject { QtObject {
property var onBoardingModel: onboardingModel property var onBoardingModul: onboardingModule
property var currentAccount: onboardingModel.currentAccount
function importMnemonic(mnemonic) { function importMnemonic(mnemonic) {
onboardingModel.importMnemonic(mnemonic) onBoardingModul.importMnemonic(mnemonic)
} }
function setCurrentAccount(selectedAccountIdx) { function setCurrentAccount(selectedAccountIdx) {
onboardingModel.setCurrentAccount(selectedAccountIdx) onBoardingModul.setSelectedAccountByIndex(selectedAccountIdx)
} }
property ListModel accountsSampleData: ListModel { property ListModel accountsSampleData: ListModel {

View File

@ -1,5 +1,9 @@
import QtQuick 2.13 import QtQuick 2.13
import "../popups"
import "../stores"
import "../shared"
Item { Item {
property var onClosed: function () {} property var onClosed: function () {}
id: genKeyView id: genKeyView
@ -12,9 +16,9 @@ Item {
GenKeyModal { GenKeyModal {
property bool wentNext: false property bool wentNext: false
id: genKeyModal id: genKeyModal
onNextClick: function (accId) { onNextClick: function (selectedIndex) {
wentNext = true wentNext = true
onboardingModule.setSelectedAccountId(accId) OnboardingStore.setCurrentAccount(selectedIndex)
createPasswordModal.open() createPasswordModal.open()
} }
onClosed: function () { onClosed: function () {
@ -31,9 +35,3 @@ Item {
} }
} }
} }
/*##^##
Designer {
D{i:0;autoSize:true;formeditorColor:"#ffffff";height:480;width:640}
}
##^##*/

View File

@ -25,16 +25,10 @@ Item {
id: loginView id: loginView
anchors.fill: parent anchors.fill: parent
function setCurrentFlow(isLogin) {
LoginStore.loginModelInst.isCurrentFlow = isLogin;
OnboardingStore.onBoardingModel.isCurrentFlow = !isLogin;
}
function doLogin(password) { function doLogin(password) {
if (loading || password.length === 0) if (loading || password.length === 0)
return return
setCurrentFlow(true);
loading = true loading = true
LoginStore.login(password) LoginStore.login(password)
applicationWindow.checkForStoringPassToKeychain(LoginStore.currentAccount.username, password, false) applicationWindow.checkForStoringPassToKeychain(LoginStore.currentAccount.username, password, false)
@ -116,7 +110,6 @@ Item {
ConfirmAddExistingKeyModal { ConfirmAddExistingKeyModal {
id: confirmAddExstingKeyModal id: confirmAddExstingKeyModal
onOpenModalClick: function () { onOpenModalClick: function () {
setCurrentFlow(false);
onExistingKeyClicked() onExistingKeyClicked()
} }
} }
@ -128,7 +121,6 @@ Item {
resetLogin() resetLogin()
} }
onOpenModalClick: function () { onOpenModalClick: function () {
setCurrentFlow(true);
onExistingKeyClicked() onExistingKeyClicked()
} }
} }
@ -185,7 +177,7 @@ Item {
anchors.top: changeAccountBtn.bottom anchors.top: changeAccountBtn.bottom
anchors.topMargin: Style.current.padding * 2 anchors.topMargin: Style.current.padding * 2
enabled: !loading enabled: !loading
placeholderText: loading ? placeholderText: loading ?
//% "Connecting..." //% "Connecting..."
qsTrId("connecting") : qsTrId("connecting") :
//% "Enter password" //% "Enter password"
@ -259,7 +251,6 @@ Item {
anchors.topMargin: 16 anchors.topMargin: 16
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
onClicked: { onClicked: {
setCurrentFlow(false);
onGenKeyClicked() onGenKeyClicked()
} }
} }

View File

@ -42,10 +42,11 @@ Item {
width: walletView.width width: walletView.width
Component.onCompleted: { Component.onCompleted: {
if(RootStore.firstTimeLogin){ // Read in RootStore
RootStore.firstTimeLogin = false // if(RootStore.firstTimeLogin){
RootStore.setInitialRange() // RootStore.firstTimeLogin = false
} // RootStore.setInitialRange()
// }
} }
Timer { Timer {

View File

@ -25,7 +25,12 @@ QtObject {
property var historyView: walletModel.historyView property var historyView: walletModel.historyView
property bool firstTimeLogin: onboardingModel.isFirstTimeLogin // This should be exposed to the UI via "walletModule", WalletModule should use
// Accounts Service which keeps the info about that. Then in the View of WalletModule
// we may have either QtProperty or Q_INVOKABLE function (proc marked as slot)
// depends on logic/need.
//property bool firstTimeLogin: walletModule.isFirstTimeLogin
property var tokens: { property var tokens: {
let count = walletModel.tokensView.defaultTokenList.rowCount() let count = walletModel.tokensView.defaultTokenList.rowCount()

View File

@ -54,10 +54,11 @@ Item {
width: walletView.width width: walletView.width
Component.onCompleted: { Component.onCompleted: {
if (walletView.store.onboardingModelInst.firstTimeLogin) { // Read in RootStore
walletView.store.onboardingModelInst.firstTimeLogin = false; // if (walletView.store.onboardingModelInst.firstTimeLogin) {
walletView.store.walletModelInst.setInitialRange(); // walletView.store.onboardingModelInst.firstTimeLogin = false;
} // walletView.store.walletModelInst.setInitialRange();
// }
} }
leftPanel: LeftTabView { leftPanel: LeftTabView {

View File

@ -24,7 +24,7 @@ Item {
//% "Invalid seed phrase" //% "Invalid seed phrase"
errorText.text = qsTrId("custom-seed-phrase") errorText.text = qsTrId("custom-seed-phrase")
} else { } else {
errorText.text = onboardingModel.validateMnemonic(mnemonicTextField.textField.text) errorText.text = onboardingModule.validateMnemonic(mnemonicTextField.textField.text)
const regex = new RegExp('word [a-z]+ not found in the dictionary', 'i'); const regex = new RegExp('word [a-z]+ not found in the dictionary', 'i');
if (regex.test(errorText.text)) { if (regex.test(errorText.text)) {
//% "Invalid seed phrase" //% "Invalid seed phrase"

View File

@ -3,6 +3,10 @@ pragma Singleton
import QtQuick 2.13 import QtQuick 2.13
QtObject { QtObject {
readonly property int onboardingAppState: 0
readonly property int loginAppSate: 1
readonly property int mainAppState: 2
readonly property int communityImported: 0 readonly property int communityImported: 0
readonly property int communityImportingInProgress: 1 readonly property int communityImportingInProgress: 1
readonly property int communityImportingError: 2 readonly property int communityImportingError: 2

View File

@ -20,7 +20,7 @@ import "./app/AppLayouts/Onboarding/views"
import "./app" import "./app"
StatusWindow { StatusWindow {
property bool hasAccounts: startupModule.startWithOnboardingScreen property bool hasAccounts: startupModule.appState !== Constants.onboardingAppState
property bool removeMnemonicAfterLogin: false property bool removeMnemonicAfterLogin: false
property alias dragAndDrop: dragTarget property alias dragAndDrop: dragTarget
property bool popupOpened: false property bool popupOpened: false
@ -361,7 +361,8 @@ StatusWindow {
DSM.SignalTransition { DSM.SignalTransition {
targetState: appState targetState: appState
signal: onboardingModel.moveToAppState signal: startupModule.appStateChanged
guard: state == Constants.mainAppState
} }
} }
@ -371,7 +372,8 @@ StatusWindow {
DSM.SignalTransition { DSM.SignalTransition {
targetState: appState targetState: appState
signal: onboardingModel.moveToAppState signal: startupModule.appStateChanged
guard: state == Constants.mainAppState
} }
} }
@ -381,7 +383,8 @@ StatusWindow {
DSM.SignalTransition { DSM.SignalTransition {
targetState: appState targetState: appState
signal: onboardingModel.moveToAppState signal: startupModule.appStateChanged
guard: state == Constants.mainAppState
} }
} }
@ -391,7 +394,8 @@ StatusWindow {
DSM.SignalTransition { DSM.SignalTransition {
targetState: appState targetState: appState
signal: loginModel.moveToAppState signal: startupModule.appStateChanged
guard: state == Constants.mainAppState
} }
DSM.SignalTransition { DSM.SignalTransition {