refactor(general): `nim_status_client.nim` file cleaned

Parts which are not refactored yet are moved to `AppController` and
marked there (a comment is added for a sake of clarification) that those
parts will be removed once we complete refactor phase.
This commit is contained in:
Sale Djenic 2021-11-17 16:22:26 +01:00
parent 1ccc98af98
commit 80b94d6519
6 changed files with 188 additions and 219 deletions

View File

@ -35,10 +35,18 @@ import ../global/global_singleton
# This will be removed later once we move to c++ and handle there async things
# and improved some services, like EventsService which should implement
# provider/subscriber principe, similar we should have SettingsService.
import ../../constants
import ../core/[main]
import eventemitter
import status/[fleet]
import ../profile/core as profile
import ../chat/core as chat
import ../wallet/v1/core as wallet
import ../wallet/v2/core as walletV2
import ../node/core as node
import ../utilsView/core as utilsView
import ../provider/core as provider
import ../keycard/core as keycard
import status/types/[account, setting]
#################################################
@ -103,9 +111,15 @@ type
mainModule: main_module.AccessInterface
#################################################
# At the end of refactoring this will be moved to
# appropriate place or removed:
# At the end of refactoring this will be moved to appropriate place or removed:
profile: ProfileController
wallet: wallet.WalletController
wallet2: walletV2.WalletController
chat: ChatController
node: NodeController
utilsController: UtilsController
provider: Web3ProviderController
keycard: KeycardController
#################################################
#################################################
@ -123,12 +137,25 @@ proc mainDidLoad*(self: AppController)
#################################################
#################################################
# At the end of refactoring this will be moved to
# appropriate place or removed:
# At the end of refactoring this will be moved to appropriate place or removed:
proc connect(self: AppController) =
self.statusFoundation.status.events.once("loginCompleted") do(a: Args):
var args = AccountArgs(a)
self.statusFoundation.status.startMessenger()
self.profile.init(args.account)
self.wallet.init()
self.wallet2.init()
self.provider.init()
self.chat.init()
self.utilsController.init()
self.node.init()
self.wallet.onLogin()
self.statusFoundation.status.events.once("nodeStopped") do(a: Args):
# TODO: remove this once accounts are not tracked in the AccountsModel
self.statusFoundation.status.reset()
# 2. Re-init controllers that don't require a running node
self.keycard.init()
#################################################
proc newAppController*(statusFoundation: StatusFoundation): AppController =
@ -202,9 +229,15 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
)
#################################################
# At the end of refactoring this will be moved to
# appropriate place or removed:
# At the end of refactoring this will be moved to appropriate place or removed:
result.profile = profile.newController(statusFoundation.status, statusFoundation, changeLanguage)
result.wallet = wallet.newController(statusFoundation.status, statusFoundation)
result.wallet2 = walletV2.newController(statusFoundation.status, statusFoundation)
result.chat = chat.newController(statusFoundation.status, statusFoundation, OPENURI)
result.node = node.newController(statusFoundation)
result.utilsController = utilsView.newController(statusFoundation.status, statusFoundation)
result.provider = provider.newController(statusFoundation.status)
result.keycard = keycard.newController(statusFoundation.status)
result.connect()
#################################################
@ -230,9 +263,15 @@ proc delete*(self: AppController) =
self.mainModule.delete
#################################################
# At the end of refactoring this will be moved to
# appropriate place or removed:
# At the end of refactoring this will be moved to appropriate place or removed:
self.profile.delete
self.wallet.delete
self.wallet2.delete
self.chat.delete
self.node.delete
self.utilsController.delete
self.provider.delete
self.keycard.delete
#################################################
self.localAppSettingsVariant.delete
@ -255,9 +294,15 @@ proc delete*(self: AppController) =
proc startupDidLoad*(self: AppController) =
#################################################
# At the end of refactoring this will be moved to
# appropriate place or removed:
# At the end of refactoring this will be moved to appropriate place or removed:
singletonInstance.engine.setRootContextProperty("profileModel", self.profile.variant)
singletonInstance.engine.setRootContextProperty("walletModel", self.wallet.variant)
singletonInstance.engine.setRootContextProperty("walletV2Model", self.wallet2.variant)
singletonInstance.engine.setRootContextProperty("chatsModel", self.chat.variant)
singletonInstance.engine.setRootContextProperty("nodeModel", self.node.variant)
singletonInstance.engine.setRootContextProperty("utilsModel", self.utilsController.variant)
singletonInstance.engine.setRootContextProperty("web3Provider", self.provider.variant)
singletonInstance.engine.setRootContextProperty("keycardModel", self.keycard.variant)
#################################################
singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant)
@ -275,6 +320,11 @@ proc mainDidLoad*(self: AppController) =
self.mainModule.checkForStoringPassword()
proc start*(self: AppController) =
#################################################
# At the end of refactoring this will be moved to appropriate place or removed:
self.keycard.init()
#################################################
self.accountsService.init()
self.startupModule.load()

View File

@ -1,4 +1,5 @@
import NimQml, chronicles, task_runner
import ../../constants
import status/status as status_lib_status
import
./tasks/marathon,
@ -18,19 +19,23 @@ type StatusFoundation* = ref object
mailserverController*: MailserverController
mailserverWorker*: MailserverWorker
proc newStatusFoundation*(status: Status): StatusFoundation =
proc newStatusFoundation*(fleetConfig: string): StatusFoundation =
result = StatusFoundation()
result.status = status
result.mailserverController = newMailserverController(status)
result.status = newStatusInstance(fleetConfig)
result.status.initNode(STATUSGODIR, KEYSTOREDIR)
result.mailserverController = newMailserverController(result.status.events)
result.mailserverWorker = newMailserverWorker(cast[ByteAddress](result.mailserverController.vptr))
result.threadpool = newThreadPool()
result.marathon = newMarathon(result.mailserverWorker)
result.signalsManager = newSignalsManager(status.events)
result.signalsManager = newSignalsManager(result.status.events)
proc delete*(self: StatusFoundation) =
self.threadpool.teardown()
self.marathon.teardown()
self.signalsManager.delete()
self.status.reset()
proc onLoggedIn*(self: StatusFoundation) =
self.marathon.onLoggedIn()

View File

@ -1,8 +1,13 @@
import # std libs
strutils
import # vendor libs
chronicles, NimQml, json_serialization
import # status-desktop libs
status/status, ../../common as task_runner_common
import events
import ../../common as task_runner_common
import eventemitter
logScope:
topics = "mailserver controller"
@ -14,11 +19,11 @@ logScope:
################################################################################
QtObject:
type MailserverController* = ref object of QObject
status*: Status
events: EventEmitter
proc newMailserverController*(status: Status): MailserverController =
proc newMailserverController*(events: EventEmitter): MailserverController =
new(result)
result.status = status
result.events = events
result.setup()
proc setup(self: MailserverController) =
@ -26,3 +31,8 @@ QtObject:
proc delete*(self: MailserverController) =
self.QObject.delete
proc receiveEvent(self: MailserverController, eventTuple: string) {.slot.} =
let event = Json.decode(eventTuple, tuple[name: string, arg: MailserverArgs])
trace "forwarding event from long-running mailserver task to the main thread", event=eventTuple
self.events.emit(event.name, event.arg)

View File

@ -11,15 +11,13 @@ type NodeController* = ref object
statusFoundation: StatusFoundation
view*: NodeView
variant*: QVariant
networkAccessMananger*: QNetworkAccessManager
isWakuV2: bool
proc newController*(statusFoundation: StatusFoundation, nam: QNetworkAccessManager): NodeController =
proc newController*(statusFoundation: StatusFoundation): NodeController =
result = NodeController()
result.statusFoundation = statusFoundation
result.view = newNodeView(statusFoundation)
result.variant = newQVariant(result.view)
result.networkAccessMananger = nam
proc delete*(self: NodeController) =
delete self.variant

View File

@ -1,4 +1,4 @@
import NimQml, json, strutils, sugar, sequtils, tables
import NimQml, json, tables
import json_serialization
import status/[status, settings]
import status/contacts as status_contacts

View File

@ -1,126 +1,45 @@
import NimQml, chronicles, os, strformat, times, md5, json
import app/chat/core as chat
import app/wallet/v1/core as wallet
import app/wallet/v2/core as walletV2
import app/node/core as node
import app/utilsView/core as utilsView
import app/keycard/core as keycard
import status/types/[account]
import status_go
import status/status as statuslib
import eventemitter
import app/core/main
import constants
import app/global/global_singleton
import app/boot/app_controller
logScope:
topics = "status-app"
var signalsManagerQObjPointer: pointer
logScope:
topics = "main"
proc isExperimental(): string =
result = if getEnv("EXPERIMENTAL") == "1": "1" else: "0" # value explicity passed to avoid trusting input
proc mainProc() =
if defined(macosx) and defined(production):
setCurrentDir(getAppDir())
proc determineResourcePath(): string =
result = if defined(windows) and defined(production): "/../resources/resources.rcc" else: "/../resources.rcc"
ensureDirectories(DATADIR, TMPDIR, LOGDIR)
proc determineFleetsPath(): string =
result = if defined(windows) and defined(production): "/../resources/fleets.json" else: "/../fleets.json"
let fleets =
if defined(windows) and defined(production):
"/../resources/fleets.json"
else:
"/../fleets.json"
let
fleetConfig = readFile(joinPath(getAppDir(), fleets))
status = statuslib.newStatusInstance(fleetConfig)
let statusFoundation = newStatusFoundation(status)
defer: statusFoundation.delete()
status.initNode(STATUSGODIR, KEYSTOREDIR)
let uiScaleFilePath = joinPath(DATADIR, "ui-scale")
enableHDPI(uiScaleFilePath)
initializeOpenGL()
let app = newQGuiApplication()
defer: app.delete()
let appController = newAppController(statusFoundation)
defer: appController.delete()
let resources =
if defined(windows) and defined(production):
"/../resources/resources.rcc"
else:
"/../resources.rcc"
QResource.registerResource(app.applicationDirPath & resources)
var eventStr = ""
proc determineOpenUri(): string =
if OPENURI.len > 0:
eventStr = $(%* { "uri": OPENURI })
let singleInstance = newSingleInstance($toMD5(DATADIR), eventStr)
defer: singleInstance.delete()
if singleInstance.secondInstance():
info "Terminating the app as the second instance"
quit()
result = $(%* { "uri": OPENURI })
let statusAppIcon =
if defined(production):
if defined(macosx):
"" # not used in macOS
elif defined(windows):
"/../resources/status.svg"
else:
"/../status.svg"
proc determineStatusAppIconPath(): string =
if defined(production):
if defined(macosx):
return "" # not used in macOS
elif defined(windows):
return "/../resources/status.svg"
else:
if defined(macosx):
"" # not used in macOS
else:
"/../status-dev.svg"
if not defined(macosx):
app.icon(app.applicationDirPath & statusAppIcon)
let networkAccessFactory = newQNetworkAccessManagerFactory(TMPDIR & "netcache")
singletonInstance.engine.addImportPath("qrc:/./StatusQ/src")
singletonInstance.engine.addImportPath("qrc:/./imports")
singletonInstance.engine.setNetworkAccessManagerFactory(networkAccessFactory)
singletonInstance.engine.setRootContextProperty("uiScaleFilePath", newQVariant(uiScaleFilePath))
# Register events objects
let dockShowAppEvent = newStatusDockShowAppEventObject(singletonInstance.engine)
defer: dockShowAppEvent.delete()
let osThemeEvent = newStatusOSThemeEventObject(singletonInstance.engine)
defer: osThemeEvent.delete()
app.installEventFilter(dockShowAppEvent)
app.installEventFilter(osThemeEvent)
let netAccMgr = newQNetworkAccessManager(singletonInstance.engine.getNetworkAccessManager())
status.events.on("network:connected") do(e: Args):
# This is a workaround for Qt bug https://bugreports.qt.io/browse/QTBUG-55180
# that was apparently reintroduced in 5.14.1 Unfortunately, the only workaround
# that could be found uses obsolete properties and methods
# (https://doc.qt.io/qt-5/qnetworkaccessmanager-obsolete.html), so this will
# need to be something we keep in mind when upgrading to Qt 6.
# The workaround is to manually set the NetworkAccessible property of the
# QNetworkAccessManager once peers have dropped (network connection is lost).
netAccMgr.clearConnectionCache()
netAccMgr.setNetworkAccessible(NetworkAccessibility.Accessible)
# We need this global variable in order to be able to access the application
# from the non-closure callback passed to `statusgo_backend.setSignalEventCallback`
signalsManagerQObjPointer = cast[pointer](statusFoundation.signalsManager.vptr)
defer:
signalsManagerQObjPointer = nil
return "/../status.svg"
else:
if defined(macosx):
return "" # not used in macOS
else:
return "/../status-dev.svg"
proc prepareLogging() =
when compiles(defaultChroniclesStream.output.writer):
defaultChroniclesStream.output.writer =
proc (logLevel: LogLevel, msg: LogOutputStr) {.gcsafe, raises: [Defect].} =
@ -133,95 +52,7 @@ proc mainProc() =
let logFile = fmt"app_{getTime().toUnix}.log"
discard defaultChroniclesStream.outputs[1].open(LOGDIR & logFile, fmAppend)
var wallet = wallet.newController(status, statusFoundation)
defer: wallet.delete()
singletonInstance.engine.setRootContextProperty("walletModel", wallet.variant)
var wallet2 = walletV2.newController(status, statusFoundation)
defer: wallet2.delete()
singletonInstance.engine.setRootContextProperty("walletV2Model", wallet2.variant)
var chat = chat.newController(status, statusFoundation, OPENURI)
defer: chat.delete()
singletonInstance.engine.setRootContextProperty("chatsModel", chat.variant)
var node = node.newController(statusFoundation, netAccMgr)
defer: node.delete()
singletonInstance.engine.setRootContextProperty("nodeModel", node.variant)
var utilsController = utilsView.newController(status, statusFoundation)
defer: utilsController.delete()
singletonInstance.engine.setRootContextProperty("utilsModel", utilsController.variant)
var keycard = keycard.newController(status)
defer: keycard.delete()
status.events.once("loginCompleted") do(a: Args):
var args = AccountArgs(a)
# At the end of refactoring all this will be in the AppController class.
status.startMessenger()
wallet.init()
wallet2.init()
chat.init()
utilsController.init()
node.init()
wallet.onLogin()
# this should be the last defer in the scope
defer:
info "Status app is shutting down..."
singletonInstance.delete()
singletonInstance.engine.setRootContextProperty("keycardModel", keycard.variant)
singletonInstance.engine.setRootContextProperty("singleInstance", newQVariant(singleInstance))
let isExperimental = if getEnv("EXPERIMENTAL") == "1": "1" else: "0" # value explicity passed to avoid trusting input
let experimentalFlag = newQVariant(isExperimental)
singletonInstance.engine.setRootContextProperty("isExperimental", experimentalFlag)
# Initialize only controllers whose init functions
# do not need a running node
proc initControllers() =
keycard.init()
initControllers()
# Handle node.stopped signal when user has logged out
status.events.once("nodeStopped") do(a: Args):
# TODO: remove this once accounts are not tracked in the AccountsModel
status.reset()
# 1. Reset controller data
# TODO: implement all controller resets
# chat.reset()
# node.reset()
# wallet.reset()
# wallet2.reset()
# profile.reset()
# 2. Re-init controllers that don't require a running node
initControllers()
var signalsManagerQVariant = newQVariant(statusFoundation.signalsManager)
defer: signalsManagerQVariant.delete()
var mailserverControllerQVariant = newQVariant(statusFoundation.mailserverController)
defer: mailserverControllerQVariant.delete()
singletonInstance.engine.setRootContextProperty("signals", signalsManagerQVariant)
singletonInstance.engine.setRootContextProperty("mailserver", mailserverControllerQVariant)
var prValue = newQVariant(if defined(production): true else: false)
singletonInstance.engine.setRootContextProperty("production", prValue)
# # We're applying default language before we load qml. Also we're aware that
# # switch language at runtime will have some impact to cpu usage.
# # https://doc.qt.io/archives/qtjambi-4.5.2_01/com/trolltech/qt/qtjambi-linguist-programmers.html
# changeLanguage("en")
appController.start()
proc setupRemoteSignalsHandling() =
# 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
# we cannot capture any local variables here (we must rely on globals)
@ -231,9 +62,84 @@ proc mainProc() =
status_go.setSignalEventCallback(callback)
# Qt main event loop is entered here
# The termination of the loop will be performed when exit() or quit() is called
info "Starting application..."
proc mainProc() =
if defined(macosx) and defined(production):
setCurrentDir(getAppDir())
ensureDirectories(DATADIR, TMPDIR, LOGDIR)
let isExperimental = isExperimental()
let resourcesPath = determineResourcePath()
let fleetsPath = determineFleetsPath()
let openUri = determineOpenUri()
let statusAppIconPath = determineStatusAppIconPath()
let fleetConfig = readFile(joinPath(getAppDir(), fleetsPath))
let statusFoundation = newStatusFoundation(fleetConfig)
let uiScaleFilePath = joinPath(DATADIR, "ui-scale")
enableHDPI(uiScaleFilePath)
initializeOpenGL()
let app = newQGuiApplication()
let appController = newAppController(statusFoundation)
let singleInstance = newSingleInstance($toMD5(DATADIR), openUri)
let networkAccessFactory = newQNetworkAccessManagerFactory(TMPDIR & "netcache")
let isProductionQVariant = newQVariant(if defined(production): true else: false)
let isExperimentalQVariant = newQVariant(isExperimental)
let signalsManagerQVariant = newQVariant(statusFoundation.signalsManager)
let mailserverControllerQVariant = newQVariant(statusFoundation.mailserverController)
QResource.registerResource(app.applicationDirPath & resourcesPath)
# Register events objects
let dockShowAppEvent = newStatusDockShowAppEventObject(singletonInstance.engine)
let osThemeEvent = newStatusOSThemeEventObject(singletonInstance.engine)
# We need this global variable in order to be able to access the application
# from the non-closure callback passed to `statusgo_backend.setSignalEventCallback`
signalsManagerQObjPointer = cast[pointer](statusFoundation.signalsManager.vptr)
setupRemoteSignalsHandling()
if not defined(macosx):
app.icon(app.applicationDirPath & statusAppIconPath)
prepareLogging()
singletonInstance.engine.addImportPath("qrc:/./StatusQ/src")
singletonInstance.engine.addImportPath("qrc:/./imports")
singletonInstance.engine.setNetworkAccessManagerFactory(networkAccessFactory)
singletonInstance.engine.setRootContextProperty("uiScaleFilePath", newQVariant(uiScaleFilePath))
singletonInstance.engine.setRootContextProperty("singleInstance", newQVariant(singleInstance))
singletonInstance.engine.setRootContextProperty("isExperimental", isExperimentalQVariant)
singletonInstance.engine.setRootContextProperty("signals", signalsManagerQVariant)
singletonInstance.engine.setRootContextProperty("mailserver", mailserverControllerQVariant)
singletonInstance.engine.setRootContextProperty("production", isProductionQVariant)
app.installEventFilter(dockShowAppEvent)
app.installEventFilter(osThemeEvent)
defer:
info "shutting down..."
signalsManagerQObjPointer = nil
isProductionQVariant.delete()
isExperimentalQVariant.delete()
signalsManagerQVariant.delete()
mailserverControllerQVariant.delete()
networkAccessFactory.delete()
dockShowAppEvent.delete()
osThemeEvent.delete()
statusFoundation.delete()
appController.delete()
singleInstance.delete()
app.delete()
# Checks below must be always after "defer", in case anything fails destructors will freed a memory.
if singleInstance.secondInstance():
info "Terminating the app as the second instance"
quit()
info "starting application controller..."
appController.start()
info "starting application..."
app.exec()
when isMainModule: