Refactor regV5 navigation to remove some unnecessary layers.

This commit is contained in:
Greyson Parrelli
2025-12-12 12:51:26 -05:00
committed by Alex Hart
parent 2ca4748e00
commit c25f6d0bc4
7 changed files with 155 additions and 142 deletions

View File

@@ -4618,6 +4618,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="0a940e79e256cc7fbec69d8eb35362ac79599ab4b21ab4846aba64ba4960a192" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata-ktx" version="2.10.0">
<artifact name="lifecycle-livedata-ktx-2.10.0.aar">
<sha256 value="3b42f425ab72b1d142ada7e075d78d9ab1500703729c1a342148eff4c0c23dcf" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-livedata-ktx-2.10.0.module">
<sha256 value="4d37fcd30801b06d5c508914b01154ee397071b0d5fcd634a48a45ded2dee09e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata-ktx" version="2.8.7">
<artifact name="lifecycle-livedata-ktx-2.8.7.aar">
<sha256 value="80af6960fb8b5d5b26bc04086613435e78a7c214be8b68b461bb79dedc431f76" origin="Generated by Gradle"/>
@@ -4749,6 +4757,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="f7cd5051bacb3f4b8ec7dc40ac6342a61eea1147592dda9f364a24cf882c7ddb" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-reactivestreams" version="2.10.0">
<artifact name="lifecycle-reactivestreams-2.10.0.aar">
<sha256 value="a68df4b53f0cc9c063691b2658cd059ec9e7f0d84439f0b458d95efc6cbe605c" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-reactivestreams-2.10.0.module">
<sha256 value="bc873cdd0c60ed59878fcdd122aeb571171158465454c0e674686d410dcf4f6c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-reactivestreams" version="2.8.7">
<artifact name="lifecycle-reactivestreams-2.8.7.aar">
<sha256 value="305da124ecc7108bb8ff28b4140a75ce034e81e0ac6e4d51cddb476e2607e92e" origin="Generated by Gradle"/>
@@ -4791,6 +4807,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="28b7d48306489ebba7b9e1d65563377f994be708e7a9757e47ddc5c0421ad1a4" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-reactivestreams-ktx" version="2.10.0">
<artifact name="lifecycle-reactivestreams-ktx-2.10.0.aar">
<sha256 value="a46076ff7cb61666d60ea516236d50886bc06152411277e900c1ff4d9d8b812d" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-reactivestreams-ktx-2.10.0.module">
<sha256 value="74cf3d5d8d148203b471b8e749153bb8d8c2ba1118f8f794f7b329d6a6b5341a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-reactivestreams-ktx" version="2.8.7">
<artifact name="lifecycle-reactivestreams-ktx-2.8.7.aar">
<sha256 value="e7d818e836f21895d65afd982b6a89ce5c7826c696bd273613445429c3da0d7c" origin="Generated by Gradle"/>
@@ -5114,6 +5138,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="b6803f906a89fd85c77df343dd4aeeb349125ca79e12d55d68d4cf42f42231f6" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-runtime-compose-desktop" version="2.10.0">
<artifact name="lifecycle-runtime-compose-desktop-2.10.0.jar">
<sha256 value="4d233eeecc6e2769a76442830c5fd1884057c36d0ce2745167193a10cc8976f8" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-runtime-compose-desktop-2.10.0.module">
<sha256 value="257b0bc6df73ad860e6b378b94890867964c7430da9c884a65f667cfe5cbdc10" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-runtime-compose-desktop" version="2.9.4">
<artifact name="lifecycle-runtime-compose-desktop-2.9.4.jar">
<md5 value="8f8fe81afd5c0e7c0f3e8a38142f8aa2" origin="Generated by Gradle"/>
@@ -5141,6 +5173,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="aaadee69ebaa3bae99ba7b5eb46aa7d38bd0be636d63f418cb43ec307a8f31da" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-runtime-desktop" version="2.10.0">
<artifact name="lifecycle-runtime-desktop-2.10.0.jar">
<sha256 value="6f70b76fb7a262f47b99317c23271c28a89ad18400547b4bfefbd2ef04500f90" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-runtime-desktop-2.10.0.module">
<sha256 value="2eeef8084cf67129e199227a3fabacf8506f57faeecb0aab0f765d5c4e59040a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-runtime-desktop" version="2.8.7">
<artifact name="lifecycle-runtime-desktop-2.8.7.module">
<md5 value="edc88458a7e5ff7fe2314aea7f42d7f5" origin="Generated by Gradle"/>
@@ -5374,6 +5414,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="87ae364cb972aa5db8f80a97dde1b8dc5533841c43bfa798abdad82cf2515fe1" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-service" version="2.10.0">
<artifact name="lifecycle-service-2.10.0.aar">
<sha256 value="60bea13bbea42b6aa007e5a48c0b432c9b3dd05887482a43baa8f5a9042dc2e2" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-service-2.10.0.module">
<sha256 value="4f13973559bff5ea98e056556488c526a705df7776559d6551bfefaecf15a79a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-service" version="2.6.1">
<artifact name="lifecycle-service-2.6.1.module">
<md5 value="10b87bd8171b0293794b99ed3b9f9d27" origin="Generated by Gradle"/>
@@ -5749,6 +5797,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="84450bb22ee6ce369c3ff52042e1d308cac63991782dacd95b8e5e357a97a221" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-viewmodel-compose-jvmstubs" version="2.10.0">
<artifact name="lifecycle-viewmodel-compose-jvmstubs-2.10.0.jar">
<sha256 value="e7a2558fe359d92d5ad455540589b25b2b5a9d77e4a3df7df26a139c08664c89" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-viewmodel-compose-jvmstubs-2.10.0.module">
<sha256 value="9dfac7b9733b7aa0c334571548c74b0cf675daeb31125c4e5131a5e249c4c3e8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-viewmodel-compose-jvmstubs" version="2.9.0">
<artifact name="lifecycle-viewmodel-compose-jvmstubs-2.9.0.jar">
<sha256 value="02d761721139eb2d175b0efb44e318408c6a3e38bfa923dde886eaa506069d44" origin="Generated by Gradle"/>
@@ -5770,6 +5826,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-viewmodel-desktop" version="2.10.0">
<artifact name="lifecycle-viewmodel-desktop-2.10.0.jar">
<sha256 value="e7c86a5e5c66fd41fd3cc9c9ad31a2997110b1a6380c441ba3f7b6b0b070e32b" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-viewmodel-desktop-2.10.0.module">
<sha256 value="cda319d7c9e32602859a72be81c0026e56d1797ef9ba67e9173e905d3f74774f" origin="Generated by Gradle"/>
</artifact>
@@ -6067,6 +6126,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-viewmodel-savedstate-desktop" version="2.10.0">
<artifact name="lifecycle-viewmodel-savedstate-desktop-2.10.0.jar">
<sha256 value="92944597842a697623d0746d719d8af3acb9f4c780da611062fa60d993944d2c" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-viewmodel-savedstate-desktop-2.10.0.module">
<sha256 value="59b7367fbd82b74a6053291a12743cac0c2cf1fe96929378c51d82bf8fe9a95d" origin="Generated by Gradle"/>
</artifact>
@@ -6410,6 +6472,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="ec5f820c5856ab72029dc58acd54cee1e1952ec7e6233848dd10e9071f0eedbc" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.navigation3" name="navigation3-runtime-desktop" version="1.0.0">
<artifact name="navigation3-runtime-desktop-1.0.0.jar">
<sha256 value="db659bc9be9528a8af3ac65f218f632b7bbc1978b039522ae5d51210f4e0ee68" origin="Generated by Gradle"/>
</artifact>
<artifact name="navigation3-runtime-desktop-1.0.0.module">
<sha256 value="eeab8a14560ab88ff0c96fcc10a142b8beea4cc93704add5627667921a9312e2" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.navigation3" name="navigation3-ui" version="1.0.0">
<artifact name="navigation3-ui-1.0.0.module">
<md5 value="3777271d1686b054e2464aadb712b084" origin="Generated by Gradle"/>
@@ -6851,6 +6921,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="06a0201247847e59303e4ffb468c9419c614708f568787e0abd5a92c6dadad40" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.savedstate" name="savedstate-compose-desktop" version="1.4.0">
<artifact name="savedstate-compose-desktop-1.4.0.jar">
<sha256 value="c02a98b0c2b35cdde245c0bfe6e7ff453c7b6d06a5eb8681d25b7f54b874ea48" origin="Generated by Gradle"/>
</artifact>
<artifact name="savedstate-compose-desktop-1.4.0.module">
<sha256 value="808ff723a153acce160489a5bc4b58ca1d7033fe84a9060f52d17db227eaa4cd" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.savedstate" name="savedstate-compose-jvmstubs" version="1.3.0">
<artifact name="savedstate-compose-jvmstubs-1.3.0.jar">
<sha256 value="4a94787a6e5bcfe143390a4f2737d4425d2ea867f2b34a847de6cda3c6ccc58a" origin="Generated by Gradle"/>
@@ -6879,6 +6957,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="945f3c713ab1f3896db216d199f924441453961ee2bbd74bb47ae8919dbaf759" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.savedstate" name="savedstate-desktop" version="1.4.0">
<artifact name="savedstate-desktop-1.4.0.jar">
<sha256 value="e965ed7a011be83a271ac7d82249a776368479ee3a2b07948c3445b51d640856" origin="Generated by Gradle"/>
</artifact>
<artifact name="savedstate-desktop-1.4.0.module">
<sha256 value="e73f2cf8818ece347d8b5dff990d0db639a7dfb8fcc85eeecb2618383f7ecef7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.savedstate" name="savedstate-ktx" version="1.2.1">
<artifact name="savedstate-ktx-1.2.1.aar">
<md5 value="232e75b56e6fea8591d591f98b2ecff2" origin="Generated by Gradle"/>

View File

@@ -82,6 +82,9 @@ dependencies {
implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.navigation3.ui)
// Permissions
implementation(libs.accompanist.permissions)
// Compose BOM
platform(libs.androidx.compose.bom).let { composeBom ->
implementation(composeBom)

View File

@@ -1,13 +0,0 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.registration.sample
import androidx.lifecycle.ViewModel
import org.signal.core.ui.navigation.ResultEventBus
class AppViewModel : ViewModel() {
val resultEventBus = ResultEventBus()
}

View File

@@ -3,13 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
@file:OptIn(ExperimentalPermissionsApi::class)
package org.signal.registration.sample
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.viewModels
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
@@ -39,15 +39,12 @@ import androidx.navigation3.runtime.rememberDecoratedNavEntries
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.ui.NavDisplay
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import kotlinx.serialization.Serializable
import org.signal.core.ui.compose.theme.SignalTheme
import org.signal.core.ui.navigation.ResultEffect
import org.signal.core.ui.navigation.ResultEventBus
import org.signal.registration.NetworkController
import org.signal.registration.RegistrationActivity
import org.signal.registration.RegistrationDependencies
import org.signal.registration.StorageController
import org.signal.registration.sample.MainActivity.Companion.REGISTRATION_RESULT
import org.signal.registration.RegistrationNavHost
import org.signal.registration.RegistrationRepository
import org.signal.registration.sample.screens.RegistrationCompleteScreen
import org.signal.registration.sample.screens.main.MainScreen
import org.signal.registration.sample.screens.main.MainScreenViewModel
@@ -89,6 +86,9 @@ sealed interface SampleRoute : NavKey {
@Serializable
data object Main : SampleRoute
@Serializable
data object Registration : SampleRoute
@Serializable
data object RegistrationComplete : SampleRoute
@@ -104,15 +104,14 @@ class MainActivity : ComponentActivity() {
const val REGISTRATION_RESULT = "registration_result"
}
private val viewModel: AppViewModel by viewModels()
private val registrationLauncher: ActivityResultLauncher<Unit> = registerForActivityResult(RegistrationActivity.RegistrationContract()) { success ->
viewModel.resultEventBus.sendResult(REGISTRATION_RESULT, success)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val registrationRepository = RegistrationRepository(
networkController = RegistrationDependencies.get().networkController,
storageController = RegistrationDependencies.get().storageController
)
setContent {
SignalTheme {
Surface(
@@ -122,11 +121,9 @@ class MainActivity : ComponentActivity() {
val backStack = rememberNavBackStack(SampleRoute.Main)
SampleNavHost(
onLaunchRegistration = { registrationLauncher.launch(Unit) },
backStack = backStack,
resultEventBus = viewModel.resultEventBus,
storageController = RegistrationDependencies.get().storageController,
networkController = RegistrationDependencies.get().networkController,
registrationRepository = registrationRepository,
registrationDependencies = RegistrationDependencies.get(),
onStartOver = {
backStack.clear()
backStack.add(SampleRoute.Main)
@@ -140,20 +137,18 @@ class MainActivity : ComponentActivity() {
@Composable
private fun SampleNavHost(
onLaunchRegistration: () -> Unit,
onStartOver: () -> Unit,
registrationRepository: RegistrationRepository,
registrationDependencies: RegistrationDependencies,
backStack: NavBackStack<NavKey>,
resultEventBus: ResultEventBus,
storageController: StorageController,
networkController: NetworkController,
modifier: Modifier = Modifier
) {
val entryProvider: (NavKey) -> NavEntry<NavKey> = entryProvider {
entry<SampleRoute.Main> {
val viewModel: MainScreenViewModel = viewModel(
factory = MainScreenViewModel.Factory(
storageController = storageController,
onLaunchRegistration = onLaunchRegistration,
storageController = registrationDependencies.storageController,
onLaunchRegistration = { backStack.add(SampleRoute.Registration) },
onOpenPinSettings = { backStack.add(SampleRoute.PinSettings) }
)
)
@@ -164,19 +159,22 @@ private fun SampleNavHost(
onPauseOrDispose { }
}
ResultEffect<Boolean>(resultEventBus, REGISTRATION_RESULT) { success ->
if (success) {
viewModel.refreshData()
backStack.add(SampleRoute.RegistrationComplete)
}
}
MainScreen(
state = state,
onEvent = { viewModel.onEvent(it) }
)
}
entry<SampleRoute.Registration> {
RegistrationNavHost(
registrationRepository,
modifier = Modifier.fillMaxSize(),
onRegistrationComplete = {
backStack.add(SampleRoute.RegistrationComplete)
}
)
}
entry<SampleRoute.RegistrationComplete> {
RegistrationCompleteScreen(onStartOver = onStartOver)
}
@@ -186,7 +184,7 @@ private fun SampleNavHost(
) {
val viewModel: PinSettingsViewModel = viewModel(
factory = PinSettingsViewModel.Factory(
networkController = networkController,
networkController = registrationDependencies.networkController,
onBack = { backStack.removeLastOrNull() }
)
)

View File

@@ -7,11 +7,11 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import org.signal.core.ui.compose.theme.SignalTheme
import org.signal.registration.screens.RegistrationHostScreen
/**
* Activity entry point for the registration flow.
@@ -39,16 +39,11 @@ class RegistrationActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
setContent {
val permissionsState = rememberMultiplePermissionsState(
permissions = viewModel.getRequiredPermissions()
)
SignalTheme(incognitoKeyboardEnabled = false) {
Surface {
RegistrationHostScreen(
RegistrationNavHost(
registrationRepository = repository,
viewModel = viewModel,
permissionsState = permissionsState,
modifier = Modifier.fillMaxSize(),
onRegistrationComplete = {
setResult(RESULT_OK)
finish()

View File

@@ -3,6 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
@file:OptIn(ExperimentalPermissionsApi::class)
package org.signal.registration
import android.os.Parcelable
@@ -15,7 +17,6 @@ import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -27,6 +28,7 @@ import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.ui.NavDisplay
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.MultiplePermissionsState
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import org.signal.core.ui.navigation.ResultEffect
@@ -48,6 +50,8 @@ import org.signal.registration.screens.pinentry.PinEntryScreen
import org.signal.registration.screens.restore.RestoreViaQrScreen
import org.signal.registration.screens.restore.RestoreViaQrScreenEvents
import org.signal.registration.screens.restore.RestoreViaQrState
import org.signal.registration.screens.util.navigateBack
import org.signal.registration.screens.util.navigateTo
import org.signal.registration.screens.verificationcode.VerificationCodeScreen
import org.signal.registration.screens.verificationcode.VerificationCodeViewModel
import org.signal.registration.screens.welcome.WelcomeScreen
@@ -113,8 +117,9 @@ private const val CAPTCHA_RESULT = "captcha_token"
/**
* Sets up the navigation graph for the registration flow using Navigation 3.
*
* @param registrationViewModel The shared ViewModel for the registration flow.
* @param permissionsState The permissions state managed at the activity level.
* @param registrationRepository The repository for registration data.
* @param registrationViewModel Optional ViewModel for testing. If null, creates one internally.
* @param permissionsState Optional permissions state for testing. If null, creates one internally.
* @param modifier Modifier to be applied to the NavDisplay.
* @param onRegistrationComplete Callback invoked when registration is successfully completed.
*/
@@ -122,20 +127,23 @@ private const val CAPTCHA_RESULT = "captcha_token"
@Composable
fun RegistrationNavHost(
registrationRepository: RegistrationRepository,
registrationViewModel: RegistrationViewModel,
permissionsState: MultiplePermissionsState,
registrationViewModel: RegistrationViewModel? = null,
permissionsState: MultiplePermissionsState? = null,
modifier: Modifier = Modifier,
onRegistrationComplete: () -> Unit = {}
) {
val registrationState by registrationViewModel.state.collectAsStateWithLifecycle()
val navigator = remember { RegistrationNavigator(eventEmitter = registrationViewModel::onEvent) }
val viewModel: RegistrationViewModel = registrationViewModel ?: viewModel(
factory = RegistrationViewModel.Factory(registrationRepository)
)
val registrationState by viewModel.state.collectAsStateWithLifecycle()
val permissions: MultiplePermissionsState = permissionsState ?: rememberMultiplePermissionsState(viewModel.getRequiredPermissions())
val entryProvider = entryProvider {
registrationEntries(
navigationEntries(
registrationRepository = registrationRepository,
registrationViewModel = registrationViewModel,
permissionsState = permissionsState,
navigator = navigator,
registrationViewModel = viewModel,
permissionsState = permissions,
onRegistrationComplete = onRegistrationComplete
)
}
@@ -152,7 +160,7 @@ fun RegistrationNavHost(
NavDisplay(
entries = entries,
onBack = { registrationViewModel.onEvent(RegistrationFlowEvent.NavigateBack) },
onBack = { viewModel.onEvent(RegistrationFlowEvent.NavigateBack) },
modifier = modifier,
transitionSpec = {
// Slide in from right and fade in when navigating forward
@@ -204,25 +212,22 @@ fun RegistrationNavHost(
)
}
/**
* Defines all navigation entries for the registration flow.
*/
@OptIn(ExperimentalPermissionsApi::class)
private fun EntryProviderScope<NavKey>.registrationEntries(
private fun EntryProviderScope<NavKey>.navigationEntries(
registrationRepository: RegistrationRepository,
registrationViewModel: RegistrationViewModel,
permissionsState: MultiplePermissionsState,
navigator: RegistrationNavigator,
onRegistrationComplete: () -> Unit
) {
val parentEventEmitter: (RegistrationFlowEvent) -> Unit = registrationViewModel::onEvent
// --- Welcome Screen
entry<RegistrationRoute.Welcome> {
WelcomeScreen(
onEvent = { event ->
when (event) {
WelcomeScreenEvents.Continue -> navigator.navigate(RegistrationRoute.Permissions(forRestore = false))
WelcomeScreenEvents.DoesNotHaveOldPhone -> navigator.navigate(RegistrationRoute.Restore)
WelcomeScreenEvents.HasOldPhone -> navigator.navigate(RegistrationRoute.Permissions(forRestore = true))
WelcomeScreenEvents.Continue -> parentEventEmitter.navigateTo(RegistrationRoute.Permissions(forRestore = false))
WelcomeScreenEvents.DoesNotHaveOldPhone -> parentEventEmitter.navigateTo(RegistrationRoute.Restore)
WelcomeScreenEvents.HasOldPhone -> parentEventEmitter.navigateTo(RegistrationRoute.Permissions(forRestore = true))
}
}
)
@@ -234,9 +239,9 @@ private fun EntryProviderScope<NavKey>.registrationEntries(
permissionsState = permissionsState,
onProceed = {
if (key.forRestore) {
navigator.navigate(RegistrationRoute.RestoreViaQr)
parentEventEmitter.navigateTo(RegistrationRoute.RestoreViaQr)
} else {
navigator.navigate(RegistrationRoute.PhoneNumberEntry)
parentEventEmitter.navigateTo(RegistrationRoute.PhoneNumberEntry)
}
}
)
@@ -281,10 +286,10 @@ private fun EntryProviderScope<NavKey>.registrationEntries(
when (event) {
is CaptchaScreenEvents.CaptchaCompleted -> {
registrationViewModel.resultBus.sendResult(CAPTCHA_RESULT, event.token)
navigator.goBack()
parentEventEmitter.navigateBack()
}
CaptchaScreenEvents.Cancel -> {
navigator.goBack()
parentEventEmitter.navigateBack()
}
}
}
@@ -370,7 +375,7 @@ private fun EntryProviderScope<NavKey>.registrationEntries(
when (event) {
AccountLockedScreenEvents.Next -> {
// TODO: Navigate to appropriate next screen (likely back to welcome or phone entry)
navigator.navigate(RegistrationRoute.Welcome)
parentEventEmitter.navigateTo(RegistrationRoute.Welcome)
}
AccountLockedScreenEvents.LearnMore -> {
// TODO: Open learn more URL
@@ -393,7 +398,7 @@ private fun EntryProviderScope<NavKey>.registrationEntries(
// TODO: Retry QR code generation
}
RestoreViaQrScreenEvents.Cancel -> {
navigator.goBack()
parentEventEmitter.navigateBack()
}
RestoreViaQrScreenEvents.UseProxy -> {
// TODO: Navigate to proxy settings
@@ -420,20 +425,3 @@ private fun EntryProviderScope<NavKey>.registrationEntries(
}
}
}
/**
* Navigator for the registration flow.
* Handles navigation events by updating the back stack.
*/
private class RegistrationNavigator(
private val eventEmitter: (RegistrationFlowEvent) -> Unit
) {
fun navigate(route: RegistrationRoute) {
eventEmitter(RegistrationFlowEvent.NavigateToScreen(route))
}
fun goBack() {
eventEmitter(RegistrationFlowEvent.NavigateBack)
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.registration.screens
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.MultiplePermissionsState
import org.signal.registration.RegistrationNavHost
import org.signal.registration.RegistrationRepository
import org.signal.registration.RegistrationViewModel
/**
* Entry point for the registration flow.
*
* This composable sets up the entire registration navigation flow and can be
* embedded into the main app's navigation or launched as a standalone flow.
*
* @param viewModel The shared ViewModel for the registration flow.
* @param permissionsState The permissions state managed at the activity level.
* @param modifier Modifier to be applied to the root container.
* @param onRegistrationComplete Callback invoked when the registration process is successfully completed.
*/
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun RegistrationHostScreen(
registrationRepository: RegistrationRepository,
viewModel: RegistrationViewModel,
permissionsState: MultiplePermissionsState,
modifier: Modifier = Modifier,
onRegistrationComplete: () -> Unit = {}
) {
RegistrationNavHost(
registrationRepository = registrationRepository,
registrationViewModel = viewModel,
permissionsState = permissionsState,
modifier = modifier.fillMaxSize(),
onRegistrationComplete = onRegistrationComplete
)
}