chore(onboarding): add scaffolding for the new onboarding (#16980)

Part of #16832

Adds the basic files needed for the new onboarding, aka onboarding V2.
It does not do anything yet, but it's ready to be implemented.

It is locked behind a feature flag.
To enable it,  run the app with `export FLAG_ONBOARDING_V2_ENABLED=1`
This commit is contained in:
Jonathan Rainville 2024-12-19 11:00:00 -05:00 committed by GitHub
parent 8aebb81137
commit 3dd5fa9443
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 136 additions and 1 deletions

View File

@ -37,9 +37,10 @@ import app_service/service/metrics/service as metrics_service
import app/modules/shared_modules/keycard_popup/module as keycard_shared_module import app/modules/shared_modules/keycard_popup/module as keycard_shared_module
import app/modules/startup/module as startup_module import app/modules/startup/module as startup_module
import app/modules/onboarding/module as onboarding_module
import app/modules/main/module as main_module import app/modules/main/module as main_module
import app/core/notifications/notifications_manager import app/core/notifications/notifications_manager
import app/global/global_singleton import app/global/[global_singleton, feature_flags]
import app/global/app_signals import app/global/app_signals
import app/core/[main] import app/core/[main]
@ -105,6 +106,7 @@ type
# Modules # Modules
startupModule: startup_module.AccessInterface startupModule: startup_module.AccessInterface
onboardingModule: onboarding_module.AccessInterface
mainModule: main_module.AccessInterface mainModule: main_module.AccessInterface
################################################# #################################################
@ -116,6 +118,7 @@ proc applyNecessaryActionsAfterLoggingIn(self: AppController)
# Startup Module Delegate Interface # Startup Module Delegate Interface
proc startupDidLoad*(self: AppController) proc startupDidLoad*(self: AppController)
proc onboardingDidLoad*(self: AppController)
proc userLoggedIn*(self: AppController): string proc userLoggedIn*(self: AppController): string
proc appReady*(self: AppController) proc appReady*(self: AppController)
proc logout*(self: AppController) proc logout*(self: AppController)
@ -248,6 +251,11 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.keycardService, result.keycardService,
result.devicesService result.devicesService
) )
if singletonInstance.featureFlags().getOnboardingV2Enabled():
result.onboardingModule = onboarding_module.newModule[AppController](
result,
statusFoundation.events,
)
result.mainModule = main_module.newModule[AppController]( result.mainModule = main_module.newModule[AppController](
result, result,
statusFoundation.events, statusFoundation.events,
@ -304,6 +312,9 @@ proc delete*(self: AppController) =
if not self.startupModule.isNil: if not self.startupModule.isNil:
self.startupModule.delete self.startupModule.delete
self.startupModule = nil self.startupModule = nil
if not self.onboardingModule.isNil:
self.onboardingModule.delete
self.onboardingModule = nil
self.mainModule.delete self.mainModule.delete
self.languageService.delete self.languageService.delete
@ -387,6 +398,7 @@ proc checkForStoringPasswordToKeychain(self: AppController) =
self.keychainService.storeData(account.keyUid, self.startupModule.getPin()) self.keychainService.storeData(account.keyUid, self.startupModule.getPin())
proc startupDidLoad*(self: AppController) = proc startupDidLoad*(self: AppController) =
# TODO move these functions to onboardingDidLoad
singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant) singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant)
singletonInstance.engine.setRootContextProperty("localAccountSettings", self.localAccountSettingsVariant) singletonInstance.engine.setRootContextProperty("localAccountSettings", self.localAccountSettingsVariant)
singletonInstance.engine.setRootContextProperty("globalUtils", self.globalUtilsVariant) singletonInstance.engine.setRootContextProperty("globalUtils", self.globalUtilsVariant)
@ -398,6 +410,10 @@ proc startupDidLoad*(self: AppController) =
# We need this to set app width/height appropriatelly on the app start. # We need this to set app width/height appropriatelly on the app start.
self.startupModule.startUpUIRaised() self.startupModule.startUpUIRaised()
proc onboardingDidLoad*(self: AppController) =
debug "NEW ONBOARDING LOADED"
# TODO when removing the old startup module, we should move the functions above here
proc mainDidLoad*(self: AppController) = proc mainDidLoad*(self: AppController) =
self.applyNecessaryActionsAfterLoggingIn() self.applyNecessaryActionsAfterLoggingIn()
self.startupModule.moveToAppState() self.startupModule.moveToAppState()
@ -411,6 +427,8 @@ proc start*(self: AppController) =
self.devicesService.init() self.devicesService.init()
self.startupModule.load() self.startupModule.load()
if singletonInstance.featureFlags().getOnboardingV2Enabled():
self.onboardingModule.load()
proc load(self: AppController) = proc load(self: AppController) =
self.settingsService.init() self.settingsService.init()
@ -484,6 +502,10 @@ proc finishAppLoading*(self: AppController) =
self.startupModule.onAppLoaded() self.startupModule.onAppLoaded()
self.startupModule = nil self.startupModule = nil
if not self.onboardingModule.isNil:
self.onboardingModule.onAppLoaded()
self.onboardingModule = nil
self.mainModule.checkAndPerformProfileMigrationIfNeeded() self.mainModule.checkAndPerformProfileMigrationIfNeeded()
proc logout*(self: AppController) = proc logout*(self: AppController) =

View File

@ -7,6 +7,7 @@ const DEFAULT_FLAG_CONNECTOR_ENABLED* = true
const DEFAULT_FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED = true const DEFAULT_FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED = true
const DEFAULT_FLAG_PAYMENT_REQUEST_ENABLED = true const DEFAULT_FLAG_PAYMENT_REQUEST_ENABLED = true
const DEFAULT_FLAG_SIMPLE_SEND_ENABLED = false const DEFAULT_FLAG_SIMPLE_SEND_ENABLED = false
const DEFAULT_FLAG_ONBOARDING_V2_ENABLED = false
proc boolToEnv*(defaultValue: bool): string = proc boolToEnv*(defaultValue: bool): string =
return if defaultValue: "1" else: "0" return if defaultValue: "1" else: "0"
@ -19,6 +20,7 @@ QtObject:
sendViaPersonalChatEnabled: bool sendViaPersonalChatEnabled: bool
paymentRequestEnabled: bool paymentRequestEnabled: bool
simpleSendEnabled: bool simpleSendEnabled: bool
onboardingV2Enabled: bool
proc setup(self: FeatureFlags) = proc setup(self: FeatureFlags) =
self.QObject.setup() self.QObject.setup()
@ -28,6 +30,7 @@ QtObject:
self.sendViaPersonalChatEnabled = getEnv("FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED", boolToEnv(DEFAULT_FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED)) != "0" self.sendViaPersonalChatEnabled = getEnv("FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED", boolToEnv(DEFAULT_FLAG_SEND_VIA_PERSONAL_CHAT_ENABLED)) != "0"
self.paymentRequestEnabled = getEnv("FLAG_PAYMENT_REQUEST_ENABLED", boolToEnv(DEFAULT_FLAG_PAYMENT_REQUEST_ENABLED)) != "0" self.paymentRequestEnabled = getEnv("FLAG_PAYMENT_REQUEST_ENABLED", boolToEnv(DEFAULT_FLAG_PAYMENT_REQUEST_ENABLED)) != "0"
self.simpleSendEnabled = getEnv("FLAG_SIMPLE_SEND_ENABLED", boolToEnv(DEFAULT_FLAG_SIMPLE_SEND_ENABLED)) != "0" self.simpleSendEnabled = getEnv("FLAG_SIMPLE_SEND_ENABLED", boolToEnv(DEFAULT_FLAG_SIMPLE_SEND_ENABLED)) != "0"
self.onboardingV2Enabled = getEnv("FLAG_ONBOARDING_V2_ENABLED", boolToEnv(DEFAULT_FLAG_ONBOARDING_V2_ENABLED)) != "0"
proc delete*(self: FeatureFlags) = proc delete*(self: FeatureFlags) =
self.QObject.delete() self.QObject.delete()
@ -71,3 +74,9 @@ QtObject:
QtProperty[bool] simpleSendEnabled: QtProperty[bool] simpleSendEnabled:
read = getSimpleSendEnabled read = getSimpleSendEnabled
proc getOnboardingV2Enabled*(self: FeatureFlags): bool {.slot.} =
return self.onboardingV2Enabled
QtProperty[bool] onboardingV2Enabled:
read = getOnboardingV2Enabled

View File

@ -0,0 +1,24 @@
import chronicles
import io_interface
import app/core/eventemitter
logScope:
topics = "onboarding-controller"
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
events: EventEmitter
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter):
Controller =
result = Controller()
result.delegate = delegate
result.events = events
proc delete*(self: Controller) =
discard
proc init*(self: Controller) =
discard

View File

@ -0,0 +1,16 @@
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onAppLoaded*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
# This way (using concepts) is used only for the modules managed by AppController
type
DelegateInterface* = concept c
c.onboardingDidLoad()

View File

@ -0,0 +1,49 @@
import NimQml, chronicles, json
import io_interface
import view, controller
import app/global/global_singleton
import app/core/eventemitter
export io_interface
logScope:
topics = "onboarding-module"
type
Module*[T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface
delegate: T
view: View
viewVariant: QVariant
controller: Controller
proc newModule*[T](delegate: T, events: EventEmitter): Module[T] =
result = Module[T]()
result.delegate = delegate
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, events)
{.push warning[Deprecated]: off.}
method delete*[T](self: Module[T]) =
self.view.delete
self.viewVariant.delete
self.controller.delete
method onAppLoaded*[T](self: Module[T]) =
singletonInstance.engine.setRootContextProperty("onboardingModule", newQVariant())
self.view.delete
self.view = nil
self.viewVariant.delete
self.viewVariant = nil
self.controller.delete
self.controller = nil
method load*[T](self: Module[T]) =
singletonInstance.engine.setRootContextProperty("onboardingModule", self.viewVariant)
self.controller.init()
self.delegate.onboardingDidLoad()
{.pop.}

View File

@ -0,0 +1,15 @@
import NimQml, json
import io_interface
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
proc delete*(self: View) =
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.delegate = delegate