feat: Add logout functionality
Move the onboarding/login state machine to the top level in main.qml, so that logout events can trigger new states. Add Loader to statemachine so that each component is lazy-loaded. Initial tests saved 50MB of memory on startup. Currently, logging out, then logging back in to the same or different account results in a doubling-up of chats/messages/wallet accounts. These need to be reset, however I need help doing that and it would delayed and blown out this PR further. This reset has been done for Onboarding and Login, but needs to be done for chats, wallet, mailservers, etc.
This commit is contained in:
parent
bd8d743385
commit
4ec593baed
|
@ -3,6 +3,7 @@ import ../../status/libstatus/types as status_types
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import ../../status/status
|
import ../../status/status
|
||||||
import view
|
import view
|
||||||
|
import ../../status/accounts as status_accounts
|
||||||
|
|
||||||
type LoginController* = ref object of SignalSubscriber
|
type LoginController* = ref object of SignalSubscriber
|
||||||
status*: Status
|
status*: Status
|
||||||
|
@ -19,11 +20,19 @@ proc delete*(self: LoginController) =
|
||||||
delete self.view
|
delete self.view
|
||||||
delete self.variant
|
delete self.variant
|
||||||
|
|
||||||
proc init*(self: LoginController, nodeAccounts: seq[NodeAccount]) =
|
proc init*(self: LoginController) =
|
||||||
|
let nodeAccounts = self.status.accounts.openAccounts()
|
||||||
self.status.accounts.nodeAccounts = nodeAccounts
|
self.status.accounts.nodeAccounts = nodeAccounts
|
||||||
for nodeAccount in nodeAccounts:
|
for nodeAccount in nodeAccounts:
|
||||||
self.view.addAccountToList(nodeAccount)
|
self.view.addAccountToList(nodeAccount)
|
||||||
|
|
||||||
|
proc reset*(self: LoginController) =
|
||||||
|
self.view.removeAccounts()
|
||||||
|
|
||||||
|
proc handleNodeStopped(self: LoginController, data: Signal) =
|
||||||
|
self.status.events.emit("nodeStopped", Args())
|
||||||
|
self.view.onLoggedOut()
|
||||||
|
|
||||||
proc handleNodeLogin(self: LoginController, data: Signal) =
|
proc handleNodeLogin(self: LoginController, data: Signal) =
|
||||||
let response = NodeSignal(data)
|
let response = NodeSignal(data)
|
||||||
if self.view.currentAccount.account != nil:
|
if self.view.currentAccount.account != nil:
|
||||||
|
@ -31,8 +40,13 @@ proc handleNodeLogin(self: LoginController, data: Signal) =
|
||||||
if ?.response.event.error == "":
|
if ?.response.event.error == "":
|
||||||
self.status.events.emit("login", AccountArgs(account: self.view.currentAccount.account.toAccount))
|
self.status.events.emit("login", AccountArgs(account: self.view.currentAccount.account.toAccount))
|
||||||
|
|
||||||
|
proc handleNodeReady(self: LoginController, data: Signal) =
|
||||||
|
self.status.events.emit("nodeReady", Args())
|
||||||
|
|
||||||
method onSignal(self: LoginController, data: Signal) =
|
method onSignal(self: LoginController, data: Signal) =
|
||||||
if data.signalType == SignalType.NodeLogin:
|
case data.signalType:
|
||||||
self.handleNodeLogin(data)
|
of SignalType.NodeLogin: handleNodeLogin(self, data)
|
||||||
|
of SignalType.NodeStopped: handleNodeStopped(self, data)
|
||||||
|
of SignalType.NodeReady: handleNodeReady(self, data)
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
|
@ -43,6 +43,11 @@ QtObject:
|
||||||
self.accounts.add(account)
|
self.accounts.add(account)
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
|
|
||||||
|
proc removeAccounts*(self: LoginView) =
|
||||||
|
self.beginRemoveRows(newQModelIndex(), self.accounts.len, self.accounts.len)
|
||||||
|
self.accounts = @[]
|
||||||
|
self.endRemoveRows()
|
||||||
|
|
||||||
method rowCount(self: LoginView, index: QModelIndex = nil): int =
|
method rowCount(self: LoginView, index: QModelIndex = nil): int =
|
||||||
return self.accounts.len
|
return self.accounts.len
|
||||||
|
|
||||||
|
@ -86,3 +91,5 @@ QtObject:
|
||||||
|
|
||||||
proc setLastLoginResponse*(self: LoginView, loginResponse: StatusGoError) =
|
proc setLastLoginResponse*(self: LoginView, loginResponse: StatusGoError) =
|
||||||
self.loginResponseChanged(loginResponse.error)
|
self.loginResponseChanged(loginResponse.error)
|
||||||
|
|
||||||
|
proc onLoggedOut*(self: LoginView) {.signal.}
|
|
@ -29,6 +29,9 @@ proc init*(self: OnboardingController) =
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
self.view.addAccountToList(account)
|
self.view.addAccountToList(account)
|
||||||
|
|
||||||
|
proc reset*(self: OnboardingController) =
|
||||||
|
self.view.removeAccounts()
|
||||||
|
|
||||||
proc handleNodeLogin(self: OnboardingController, data: Signal) =
|
proc handleNodeLogin(self: OnboardingController, data: Signal) =
|
||||||
let response = NodeSignal(data)
|
let response = NodeSignal(data)
|
||||||
if self.view.currentAccount.account != nil:
|
if self.view.currentAccount.account != nil:
|
||||||
|
@ -37,7 +40,7 @@ proc handleNodeLogin(self: OnboardingController, data: Signal) =
|
||||||
self.status.events.emit("login", AccountArgs(account: self.view.currentAccount.account.toAccount))
|
self.status.events.emit("login", AccountArgs(account: self.view.currentAccount.account.toAccount))
|
||||||
|
|
||||||
method onSignal(self: OnboardingController, data: Signal) =
|
method onSignal(self: OnboardingController, data: Signal) =
|
||||||
if data.signalType == SignalType.NodeLogin:
|
case data.signalType:
|
||||||
self.handleNodeLogin(data)
|
of SignalType.NodeLogin: handleNodeLogin(self, data)
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
|
@ -41,6 +41,11 @@ QtObject:
|
||||||
self.accounts.add(account)
|
self.accounts.add(account)
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
|
|
||||||
|
proc removeAccounts*(self: OnboardingView) =
|
||||||
|
self.beginResetModel()
|
||||||
|
self.accounts = @[]
|
||||||
|
self.endResetModel()
|
||||||
|
|
||||||
method rowCount(self: OnboardingView, index: QModelIndex = nil): int =
|
method rowCount(self: OnboardingView, index: QModelIndex = nil): int =
|
||||||
return self.accounts.len
|
return self.accounts.len
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ type ProfileController* = object
|
||||||
proc newController*(status: Status): ProfileController =
|
proc newController*(status: Status): ProfileController =
|
||||||
result = ProfileController()
|
result = ProfileController()
|
||||||
result.status = status
|
result.status = status
|
||||||
result.view = newProfileView()
|
result.view = newProfileView(status)
|
||||||
result.variant = newQVariant(result.view)
|
result.variant = newQVariant(result.view)
|
||||||
|
|
||||||
proc delete*(self: ProfileController) =
|
proc delete*(self: ProfileController) =
|
||||||
|
|
|
@ -3,12 +3,15 @@ import views/mailservers_list
|
||||||
import views/contact_list
|
import views/contact_list
|
||||||
import views/profile_info
|
import views/profile_info
|
||||||
import ../../status/profile
|
import ../../status/profile
|
||||||
|
import ../../status/accounts as status_accounts
|
||||||
|
import ../../status/status
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type ProfileView* = ref object of QObject
|
type ProfileView* = ref object of QObject
|
||||||
profile*: ProfileInfoView
|
profile*: ProfileInfoView
|
||||||
mailserversList*: MailServersList
|
mailserversList*: MailServersList
|
||||||
contactList*: ContactList
|
contactList*: ContactList
|
||||||
|
status*: Status
|
||||||
|
|
||||||
proc setup(self: ProfileView) =
|
proc setup(self: ProfileView) =
|
||||||
self.QObject.setup
|
self.QObject.setup
|
||||||
|
@ -16,12 +19,13 @@ QtObject:
|
||||||
proc delete*(self: ProfileView) =
|
proc delete*(self: ProfileView) =
|
||||||
self.QObject.delete
|
self.QObject.delete
|
||||||
|
|
||||||
proc newProfileView*(): ProfileView =
|
proc newProfileView*(status: Status): ProfileView =
|
||||||
new(result, delete)
|
new(result, delete)
|
||||||
result = ProfileView()
|
result = ProfileView()
|
||||||
result.profile = newProfileInfoView()
|
result.profile = newProfileInfoView()
|
||||||
result.mailserversList = newMailServersList()
|
result.mailserversList = newMailServersList()
|
||||||
result.contactList = newContactList()
|
result.contactList = newContactList()
|
||||||
|
result.status = status
|
||||||
result.setup
|
result.setup
|
||||||
|
|
||||||
proc addMailServerToList*(self: ProfileView, mailserver: MailServer) =
|
proc addMailServerToList*(self: ProfileView, mailserver: MailServer) =
|
||||||
|
@ -50,3 +54,6 @@ QtObject:
|
||||||
|
|
||||||
QtProperty[QVariant] profile:
|
QtProperty[QVariant] profile:
|
||||||
read = getProfile
|
read = getProfile
|
||||||
|
|
||||||
|
proc logout*(self: ProfileView) {.slot.} =
|
||||||
|
self.status.profile.logout()
|
||||||
|
|
|
@ -21,7 +21,7 @@ logScope:
|
||||||
|
|
||||||
proc mainProc() =
|
proc mainProc() =
|
||||||
let status = statuslib.newStatusInstance()
|
let status = statuslib.newStatusInstance()
|
||||||
let nodeAccounts = status.initNodeAccounts()
|
status.initNode()
|
||||||
|
|
||||||
let app = newQApplication()
|
let app = newQApplication()
|
||||||
let engine = newQQmlApplicationEngine()
|
let engine = newQQmlApplicationEngine()
|
||||||
|
@ -43,13 +43,12 @@ proc mainProc() =
|
||||||
engine.setRootContextProperty("chatsModel", chat.variant)
|
engine.setRootContextProperty("chatsModel", chat.variant)
|
||||||
|
|
||||||
var node = node.newController(status)
|
var node = node.newController(status)
|
||||||
node.init()
|
|
||||||
engine.setRootContextProperty("nodeModel", node.variant)
|
engine.setRootContextProperty("nodeModel", node.variant)
|
||||||
|
|
||||||
var profile = profile.newController(status)
|
var profile = profile.newController(status)
|
||||||
engine.setRootContextProperty("profileModel", profile.variant)
|
engine.setRootContextProperty("profileModel", profile.variant)
|
||||||
|
|
||||||
status.events.once("login") do(a: Args):
|
status.events.on("login") do(a: Args):
|
||||||
var args = AccountArgs(a)
|
var args = AccountArgs(a)
|
||||||
status.startMessenger()
|
status.startMessenger()
|
||||||
chat.init()
|
chat.init()
|
||||||
|
@ -59,16 +58,37 @@ proc mainProc() =
|
||||||
var login = login.newController(status)
|
var login = login.newController(status)
|
||||||
var onboarding = onboarding.newController(status)
|
var onboarding = onboarding.newController(status)
|
||||||
|
|
||||||
# TODO: replace this with routing
|
|
||||||
let showLogin = nodeAccounts.len > 0
|
|
||||||
engine.setRootContextProperty("showLogin", newQVariant(showLogin))
|
|
||||||
|
|
||||||
login.init(nodeAccounts)
|
|
||||||
engine.setRootContextProperty("loginModel", login.variant)
|
engine.setRootContextProperty("loginModel", login.variant)
|
||||||
|
|
||||||
onboarding.init()
|
|
||||||
engine.setRootContextProperty("onboardingModel", onboarding.variant)
|
engine.setRootContextProperty("onboardingModel", onboarding.variant)
|
||||||
|
|
||||||
|
# Initialize only controllers whose init functions
|
||||||
|
# do not need a running node
|
||||||
|
proc initControllers() =
|
||||||
|
node.init()
|
||||||
|
login.init()
|
||||||
|
onboarding.init()
|
||||||
|
|
||||||
|
initControllers()
|
||||||
|
|
||||||
|
# Handle node.stopped signal when user has logged out
|
||||||
|
status.events.on("nodeStopped") do(a: Args):
|
||||||
|
# TODO: remove this once accounts are not tracked in the AccountsModel
|
||||||
|
status.reset()
|
||||||
|
|
||||||
|
# 1. Reset controller data
|
||||||
|
login.reset()
|
||||||
|
onboarding.reset()
|
||||||
|
# TODO: implement all controller resets
|
||||||
|
# chat.reset()
|
||||||
|
# node.reset()
|
||||||
|
# wallet.reset()
|
||||||
|
# profile.reset()
|
||||||
|
|
||||||
|
# 2. Re-init controllers that don't require a running node
|
||||||
|
initControllers()
|
||||||
|
|
||||||
|
|
||||||
signalController.init()
|
signalController.init()
|
||||||
signalController.addSubscriber(SignalType.Wallet, wallet)
|
signalController.addSubscriber(SignalType.Wallet, wallet)
|
||||||
signalController.addSubscriber(SignalType.Wallet, node)
|
signalController.addSubscriber(SignalType.Wallet, node)
|
||||||
|
@ -76,6 +96,9 @@ proc mainProc() =
|
||||||
signalController.addSubscriber(SignalType.DiscoverySummary, chat)
|
signalController.addSubscriber(SignalType.DiscoverySummary, chat)
|
||||||
signalController.addSubscriber(SignalType.NodeLogin, login)
|
signalController.addSubscriber(SignalType.NodeLogin, login)
|
||||||
signalController.addSubscriber(SignalType.NodeLogin, onboarding)
|
signalController.addSubscriber(SignalType.NodeLogin, onboarding)
|
||||||
|
signalController.addSubscriber(SignalType.NodeStopped, login)
|
||||||
|
signalController.addSubscriber(SignalType.NodeStarted, login)
|
||||||
|
signalController.addSubscriber(SignalType.NodeReady, login)
|
||||||
|
|
||||||
engine.setRootContextProperty("signals", signalController.variant)
|
engine.setRootContextProperty("signals", signalController.variant)
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import libstatus/accounts as status_accounts
|
import libstatus/accounts as status_accounts
|
||||||
import libstatus/types
|
import libstatus/types
|
||||||
import options
|
import options
|
||||||
|
import eventemitter
|
||||||
|
|
||||||
type
|
type
|
||||||
AccountModel* = ref object
|
AccountModel* = ref object
|
||||||
generatedAddresses*: seq[GeneratedAccount]
|
generatedAddresses*: seq[GeneratedAccount]
|
||||||
nodeAccounts*: seq[NodeAccount]
|
nodeAccounts*: seq[NodeAccount]
|
||||||
|
events: EventEmitter
|
||||||
|
|
||||||
proc newAccountModel*(): AccountModel =
|
proc newAccountModel*(events: EventEmitter): AccountModel =
|
||||||
result = AccountModel()
|
result = AccountModel()
|
||||||
|
result.events = events
|
||||||
|
|
||||||
proc generateAddresses*(self: AccountModel): seq[GeneratedAccount] =
|
proc generateAddresses*(self: AccountModel): seq[GeneratedAccount] =
|
||||||
var accounts = status_accounts.generateAddresses()
|
var accounts = status_accounts.generateAddresses()
|
||||||
|
@ -16,7 +19,10 @@ proc generateAddresses*(self: AccountModel): seq[GeneratedAccount] =
|
||||||
account.name = status_accounts.generateAlias(account.derived.whisper.publicKey)
|
account.name = status_accounts.generateAlias(account.derived.whisper.publicKey)
|
||||||
account.photoPath = status_accounts.generateIdenticon(account.derived.whisper.publicKey)
|
account.photoPath = status_accounts.generateIdenticon(account.derived.whisper.publicKey)
|
||||||
self.generatedAddresses.add(account)
|
self.generatedAddresses.add(account)
|
||||||
self.generatedAddresses
|
result = self.generatedAddresses
|
||||||
|
|
||||||
|
proc openAccounts*(self: AccountModel): seq[NodeAccount] =
|
||||||
|
result = status_accounts.openAccounts()
|
||||||
|
|
||||||
proc login*(self: AccountModel, selectedAccountIndex: int, password: string): NodeAccount =
|
proc login*(self: AccountModel, selectedAccountIndex: int, password: string): NodeAccount =
|
||||||
let currentNodeAccount = self.nodeAccounts[selectedAccountIndex]
|
let currentNodeAccount = self.nodeAccounts[selectedAccountIndex]
|
||||||
|
@ -35,3 +41,7 @@ proc importMnemonic*(self: AccountModel, mnemonic: string): GeneratedAccount =
|
||||||
importedAccount.name = status_accounts.generateAlias(importedAccount.derived.whisper.publicKey)
|
importedAccount.name = status_accounts.generateAlias(importedAccount.derived.whisper.publicKey)
|
||||||
importedAccount.photoPath = status_accounts.generateIdenticon(importedAccount.derived.whisper.publicKey)
|
importedAccount.photoPath = status_accounts.generateIdenticon(importedAccount.derived.whisper.publicKey)
|
||||||
result = importedAccount
|
result = importedAccount
|
||||||
|
|
||||||
|
proc reset*(self: AccountModel) =
|
||||||
|
self.nodeAccounts = @[]
|
||||||
|
self.generatedAddresses = @[]
|
||||||
|
|
|
@ -36,17 +36,15 @@ proc ensureDir(dirname: string) =
|
||||||
# removeDir(dirname)
|
# removeDir(dirname)
|
||||||
createDir(dirname)
|
createDir(dirname)
|
||||||
|
|
||||||
proc initNodeAccounts*(): seq[NodeAccount] =
|
proc initNode*() =
|
||||||
const datadir = "./data/"
|
ensureDir(DATADIR)
|
||||||
const keystoredir = "./data/keystore/"
|
ensureDir(KEYSTOREDIR)
|
||||||
const nobackupdir = "./noBackup/"
|
ensureDir(NOBACKUPDIR)
|
||||||
|
|
||||||
ensureDir(datadir)
|
discard $libstatus.initKeystore(KEYSTOREDIR)
|
||||||
ensureDir(keystoredir)
|
|
||||||
ensureDir(nobackupdir)
|
|
||||||
|
|
||||||
discard $libstatus.initKeystore(keystoredir);
|
proc openAccounts*(): seq[NodeAccount] =
|
||||||
let strNodeAccounts = $libstatus.openAccounts(datadir);
|
let strNodeAccounts = $libstatus.openAccounts(DATADIR)
|
||||||
result = Json.decode(strNodeAccounts, seq[NodeAccount])
|
result = Json.decode(strNodeAccounts, seq[NodeAccount])
|
||||||
|
|
||||||
proc saveAccountAndLogin*(
|
proc saveAccountAndLogin*(
|
||||||
|
@ -215,6 +213,8 @@ proc deriveAccounts*(accountId: string): MultiAccounts =
|
||||||
"accountID": accountId,
|
"accountID": accountId,
|
||||||
"paths": [PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET]
|
"paths": [PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET]
|
||||||
}
|
}
|
||||||
# libstatus.multiAccountImportMnemonic never results in an error given ANY input
|
|
||||||
let deriveResult = $libstatus.multiAccountDeriveAddresses($deriveJson)
|
let deriveResult = $libstatus.multiAccountDeriveAddresses($deriveJson)
|
||||||
result = Json.decode(deriveResult, MultiAccounts)
|
result = Json.decode(deriveResult, MultiAccounts)
|
||||||
|
|
||||||
|
proc logout*(): StatusGoError =
|
||||||
|
result = Json.decode($libstatus.logout(), StatusGoError)
|
||||||
|
|
|
@ -175,3 +175,7 @@ let NODE_CONFIG* = %* {
|
||||||
"Enabled": true
|
"Enabled": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DATADIR* = "./data/"
|
||||||
|
const KEYSTOREDIR* = "./data/keystore/"
|
||||||
|
const NOBACKUPDIR* = "./noBackup/"
|
|
@ -33,3 +33,5 @@ proc generateAlias*(p0: GoString): cstring {.importc: "GenerateAlias".}
|
||||||
proc identicon*(p0: GoString): cstring {.importc: "Identicon".}
|
proc identicon*(p0: GoString): cstring {.importc: "Identicon".}
|
||||||
|
|
||||||
proc login*(acctData: cstring, password: cstring): cstring {.importc: "Login".}
|
proc login*(acctData: cstring, password: cstring): cstring {.importc: "Login".}
|
||||||
|
|
||||||
|
proc logout*(): cstring {.importc: "Logout".}
|
||||||
|
|
|
@ -9,6 +9,7 @@ type SignalType* {.pure.} = enum
|
||||||
Wallet = "wallet"
|
Wallet = "wallet"
|
||||||
NodeReady = "node.ready"
|
NodeReady = "node.ready"
|
||||||
NodeStarted = "node.started"
|
NodeStarted = "node.started"
|
||||||
|
NodeStopped = "node.stopped"
|
||||||
NodeLogin = "node.login"
|
NodeLogin = "node.login"
|
||||||
EnvelopeSent = "envelope.sent"
|
EnvelopeSent = "envelope.sent"
|
||||||
EnvelopeExpired = "envelope.expired"
|
EnvelopeExpired = "envelope.expired"
|
||||||
|
|
|
@ -2,6 +2,7 @@ import json
|
||||||
import eventemitter
|
import eventemitter
|
||||||
import libstatus/types
|
import libstatus/types
|
||||||
import libstatus/core as libstatus_core
|
import libstatus/core as libstatus_core
|
||||||
|
import libstatus/accounts as status_accounts
|
||||||
|
|
||||||
type
|
type
|
||||||
MailServer* = ref object
|
MailServer* = ref object
|
||||||
|
@ -45,3 +46,13 @@ proc getContactByID*(id: string): Profile =
|
||||||
let val = parseJSON($response)
|
let val = parseJSON($response)
|
||||||
result = toProfileModel(val)
|
result = toProfileModel(val)
|
||||||
|
|
||||||
|
type
|
||||||
|
ProfileModel* = ref object
|
||||||
|
|
||||||
|
proc newProfileModel*(): ProfileModel =
|
||||||
|
result = ProfileModel()
|
||||||
|
|
||||||
|
proc logout*(self: ProfileModel) =
|
||||||
|
discard status_accounts.logout()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ import accounts as accounts
|
||||||
import wallet as wallet
|
import wallet as wallet
|
||||||
import node as node
|
import node as node
|
||||||
import mailservers as mailservers
|
import mailservers as mailservers
|
||||||
|
import profile
|
||||||
|
import ../signals/types as signal_types
|
||||||
|
|
||||||
type Status* = ref object
|
type Status* = ref object
|
||||||
events*: EventEmitter
|
events*: EventEmitter
|
||||||
|
@ -17,19 +19,33 @@ type Status* = ref object
|
||||||
accounts*: AccountModel
|
accounts*: AccountModel
|
||||||
wallet*: WalletModel
|
wallet*: WalletModel
|
||||||
node*: NodeModel
|
node*: NodeModel
|
||||||
|
profile*: ProfileModel
|
||||||
|
|
||||||
proc newStatusInstance*(): Status =
|
proc newStatusInstance*(): Status =
|
||||||
result = Status()
|
result = Status()
|
||||||
result.events = createEventEmitter()
|
result.events = createEventEmitter()
|
||||||
result.chat = chat.newChatModel(result.events)
|
result.chat = chat.newChatModel(result.events)
|
||||||
result.accounts = accounts.newAccountModel()
|
result.accounts = accounts.newAccountModel(result.events)
|
||||||
result.wallet = wallet.newWalletModel(result.events)
|
result.wallet = wallet.newWalletModel(result.events)
|
||||||
result.wallet.initEvents()
|
result.wallet.initEvents()
|
||||||
result.node = node.newNodeModel()
|
result.node = node.newNodeModel()
|
||||||
result.mailservers = mailservers.newMailserverModel(result.events)
|
result.mailservers = mailservers.newMailserverModel(result.events)
|
||||||
|
result.profile = profile.newProfileModel()
|
||||||
|
|
||||||
proc initNodeAccounts*(self: Status): seq[NodeAccount] =
|
proc initNode*(self: Status) =
|
||||||
libstatus_accounts.initNodeAccounts()
|
libstatus_accounts.initNode()
|
||||||
|
|
||||||
proc startMessenger*(self: Status) =
|
proc startMessenger*(self: Status) =
|
||||||
libstatus_core.startMessenger()
|
libstatus_core.startMessenger()
|
||||||
|
|
||||||
|
proc reset*(self: Status) =
|
||||||
|
# TODO: remove this once accounts are not tracked in the AccountsModel
|
||||||
|
self.accounts.reset()
|
||||||
|
|
||||||
|
# NOT NEEDED self.chat.reset()
|
||||||
|
# NOT NEEDED self.wallet.reset()
|
||||||
|
# NOT NEEDED self.node.reset()
|
||||||
|
# NOT NEEDED self.mailservers.reset()
|
||||||
|
# NOT NEEDED self.profile.reset()
|
||||||
|
|
||||||
|
# TODO: add all resets here
|
||||||
|
|
|
@ -2,6 +2,7 @@ import QtQuick 2.3
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import "../../../../imports"
|
import "../../../../imports"
|
||||||
|
import "../../../../shared"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: signoutContainer
|
id: signoutContainer
|
||||||
|
@ -11,7 +12,7 @@ Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: element10
|
id: txtTitle
|
||||||
text: qsTr("Sign out controls")
|
text: qsTr("Sign out controls")
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 24
|
anchors.leftMargin: 24
|
||||||
|
@ -20,4 +21,15 @@ Item {
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledButton {
|
||||||
|
id: btnLogout
|
||||||
|
anchors.top: txtTitle.bottom
|
||||||
|
anchors.topMargin: Theme.padding
|
||||||
|
label: qsTr("Logout")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
profileModel.logout();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
148
ui/main.qml
148
ui/main.qml
|
@ -2,6 +2,7 @@ import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import Qt.labs.platform 1.1
|
import Qt.labs.platform 1.1
|
||||||
|
import QtQml.StateMachine 1.14 as DSM
|
||||||
import "./onboarding"
|
import "./onboarding"
|
||||||
import "./app"
|
import "./app"
|
||||||
|
|
||||||
|
@ -13,6 +14,8 @@ ApplicationWindow {
|
||||||
visible: true
|
visible: true
|
||||||
font.family: "Inter"
|
font.family: "Inter"
|
||||||
|
|
||||||
|
signal navigateTo(string path)
|
||||||
|
|
||||||
SystemTrayIcon {
|
SystemTrayIcon {
|
||||||
visible: true
|
visible: true
|
||||||
icon.source: "shared/img/status-logo.png"
|
icon.source: "shared/img/status-logo.png"
|
||||||
|
@ -30,18 +33,145 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OnboardingMain {
|
DSM.StateMachine {
|
||||||
id: onboarding
|
id: stateMachine
|
||||||
visible: !app.visible
|
initialState: onboardingState
|
||||||
|
running: true
|
||||||
|
|
||||||
|
DSM.State {
|
||||||
|
id: onboardingState
|
||||||
|
initialState: loginModel.rowCount() ? stateLogin : stateIntro
|
||||||
|
|
||||||
|
DSM.State {
|
||||||
|
id: stateIntro
|
||||||
|
onEntered: loader.sourceComponent = intro
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: keysMainState
|
||||||
|
signal: applicationWindow.navigateTo
|
||||||
|
guard: path === "KeysMain"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DSM.State {
|
||||||
|
id: keysMainState
|
||||||
|
onEntered: loader.sourceComponent = keysMain
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: existingKeyState
|
||||||
|
signal: applicationWindow.navigateTo
|
||||||
|
guard: path === "ExistingKey"
|
||||||
|
}
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: genKeyState
|
||||||
|
signal: applicationWindow.navigateTo
|
||||||
|
guard: path === "GenKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DSM.State {
|
||||||
|
id: existingKeyState
|
||||||
|
onEntered: loader.sourceComponent = existingKey
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: appState
|
||||||
|
signal: onboardingModel.loginResponseChanged
|
||||||
|
guard: !error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DSM.State {
|
||||||
|
id: genKeyState
|
||||||
|
onEntered: loader.sourceComponent = genKey
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: appState
|
||||||
|
signal: onboardingModel.loginResponseChanged
|
||||||
|
guard: !error
|
||||||
|
}
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: existingKeyState
|
||||||
|
signal: applicationWindow.navigateTo
|
||||||
|
guard: path === "ExistingKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DSM.State {
|
||||||
|
id: stateLogin
|
||||||
|
onEntered: loader.sourceComponent = login
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: appState
|
||||||
|
signal: loginModel.loginResponseChanged
|
||||||
|
guard: !error
|
||||||
|
}
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: genKeyState
|
||||||
|
signal: applicationWindow.navigateTo
|
||||||
|
guard: path === "GenKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DSM.FinalState {
|
||||||
|
id: onboardingDoneState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DSM.State {
|
||||||
|
id: appState
|
||||||
|
onEntered: loader.sourceComponent = app
|
||||||
|
|
||||||
|
DSM.SignalTransition {
|
||||||
|
targetState: stateLogin
|
||||||
|
signal: loginModel.onLoggedOut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: loader
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
AppMain {
|
Component {
|
||||||
id: app
|
id: app
|
||||||
// TODO: Set this to a logic result determining when we need to show the onboarding screens
|
AppMain {}
|
||||||
// Set to true to hide the onboarding screens manually
|
}
|
||||||
// Set to false to show the onboarding screens manually
|
|
||||||
visible: false // logic.accountResult !== ""
|
Component {
|
||||||
anchors.fill: parent
|
id: intro
|
||||||
|
Intro {
|
||||||
|
btnGetStarted.onClicked: applicationWindow.navigateTo("KeysMain")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: keysMain
|
||||||
|
KeysMain {
|
||||||
|
btnGenKey.onClicked: applicationWindow.navigateTo("GenKey")
|
||||||
|
btnExistingKey.onClicked: applicationWindow.navigateTo("ExistingKey")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: existingKey
|
||||||
|
ExistingKey {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: genKey
|
||||||
|
GenKey {
|
||||||
|
btnExistingKey.onClicked: applicationWindow.navigateTo("ExistingKey")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: login
|
||||||
|
Login {
|
||||||
|
btnGenKey.onClicked: applicationWindow.navigateTo("GenKey")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,6 +192,19 @@ SwipeView {
|
||||||
standardButtons: StandardButton.Ok
|
standardButtons: StandardButton.Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageDialog {
|
||||||
|
id: passwordsDontMatchError
|
||||||
|
title: "Error"
|
||||||
|
text: "Passwords don't match"
|
||||||
|
icon: StandardIcon.Warning
|
||||||
|
standardButtons: StandardButton.Ok
|
||||||
|
onAccepted: {
|
||||||
|
txtConfirmPassword.clear();
|
||||||
|
swipeView.currentIndex = 1;
|
||||||
|
txtPassword.focus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: onboardingModel
|
target: onboardingModel
|
||||||
ignoreUnknownSignals: true
|
ignoreUnknownSignals: true
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
import QtQuick 2.3
|
|
||||||
import QtQml.StateMachine 1.14 as DSM
|
|
||||||
import QtQuick.Controls 2.3
|
|
||||||
|
|
||||||
Page {
|
|
||||||
id: onboardingMain
|
|
||||||
property string state
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
DSM.StateMachine {
|
|
||||||
id: stateMachine
|
|
||||||
initialState: showLogin ? stateLogin : stateIntro
|
|
||||||
running: onboardingMain.visible
|
|
||||||
|
|
||||||
DSM.State {
|
|
||||||
id: stateIntro
|
|
||||||
onEntered: intro.visible = true
|
|
||||||
onExited: intro.visible = false
|
|
||||||
|
|
||||||
DSM.SignalTransition {
|
|
||||||
targetState: keysMainState
|
|
||||||
signal: intro.btnGetStarted.clicked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DSM.State {
|
|
||||||
id: keysMainState
|
|
||||||
onEntered: keysMain.visible = true
|
|
||||||
onExited: keysMain.visible = false
|
|
||||||
|
|
||||||
DSM.SignalTransition {
|
|
||||||
targetState: existingKeyState
|
|
||||||
signal: keysMain.btnExistingKey.clicked
|
|
||||||
}
|
|
||||||
|
|
||||||
DSM.SignalTransition {
|
|
||||||
targetState: genKeyState
|
|
||||||
signal: keysMain.btnGenKey.clicked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DSM.State {
|
|
||||||
id: existingKeyState
|
|
||||||
onEntered: existingKey.visible = true
|
|
||||||
onExited: existingKey.visible = false
|
|
||||||
|
|
||||||
DSM.SignalTransition {
|
|
||||||
targetState: appState
|
|
||||||
signal: onboardingModel.loginResponseChanged
|
|
||||||
guard: !error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DSM.State {
|
|
||||||
id: genKeyState
|
|
||||||
onEntered: genKey.visible = true
|
|
||||||
onExited: genKey.visible = false
|
|
||||||
|
|
||||||
DSM.SignalTransition {
|
|
||||||
targetState: appState
|
|
||||||
signal: onboardingModel.loginResponseChanged
|
|
||||||
guard: !error
|
|
||||||
}
|
|
||||||
|
|
||||||
DSM.SignalTransition {
|
|
||||||
targetState: existingKeyState
|
|
||||||
signal: genKey.btnExistingKey.clicked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DSM.State {
|
|
||||||
id: stateLogin
|
|
||||||
onEntered: login.visible = true
|
|
||||||
onExited: login.visible = false
|
|
||||||
|
|
||||||
DSM.SignalTransition {
|
|
||||||
targetState: appState
|
|
||||||
signal: loginModel.loginResponseChanged
|
|
||||||
guard: !error
|
|
||||||
}
|
|
||||||
|
|
||||||
DSM.SignalTransition {
|
|
||||||
targetState: genKeyState
|
|
||||||
signal: login.btnGenKey.clicked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DSM.FinalState {
|
|
||||||
id: appState
|
|
||||||
onEntered: app.visible = true
|
|
||||||
onExited: app.visible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Intro {
|
|
||||||
id: intro
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
KeysMain {
|
|
||||||
id: keysMain
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
ExistingKey {
|
|
||||||
id: existingKey
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
GenKey {
|
|
||||||
id: genKey
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
Login {
|
|
||||||
id: login
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*##^##
|
|
||||||
Designer {
|
|
||||||
D{i:0;autoSize:true;height:770;width:1232}
|
|
||||||
}
|
|
||||||
##^##*/
|
|
Loading…
Reference in New Issue