@@ -7,7 +7,6 @@ import androidx.compose.runtime.collectAsState
77import androidx.compose.runtime.getValue
88import androidx.compose.runtime.mutableStateOf
99import androidx.compose.runtime.remember
10- import androidx.compose.runtime.rememberCoroutineScope
1110import androidx.compose.runtime.setValue
1211import androidx.compose.ui.platform.LocalContext
1312import cafe.adriel.voyager.core.screen.Screen
@@ -22,7 +21,9 @@ import com.getcode.navigation.screens.LoginGraph
2221import com.getcode.navigation.screens.LoginScreen
2322import com.getcode.util.DeeplinkHandler
2423import com.getcode.util.getActivity
24+ import kotlinx.coroutines.CoroutineScope
2525import kotlinx.coroutines.delay
26+ import kotlinx.coroutines.flow.Flow
2627import kotlinx.coroutines.flow.combine
2728import kotlinx.coroutines.flow.distinctUntilChanged
2829import kotlinx.coroutines.flow.filter
@@ -36,12 +37,13 @@ import kotlinx.coroutines.flow.onEach
3637import kotlinx.coroutines.launch
3738import timber.log.Timber
3839
39- const val AUTH_NAV = " Authentication Navigation"
40+ private const val APP_STARTUP_TAG = " app-startup"
41+ private typealias DeeplinkFlowState = Pair <Pair <DeeplinkHandler .Type , List <Screen >>, SessionManager .SessionState >
4042
4143@Composable
4244fun AuthCheck (
4345 navigator : CodeNavigator ,
44- onNavigate : (List <Screen >, Boolean ) -> Unit ,
46+ onNavigate : (List <Screen >) -> Unit ,
4547 onSwitchAccounts : (String ) -> Unit ,
4648) {
4749 val deeplinkHandler = LocalDeeplinks .current
@@ -60,18 +62,18 @@ fun AuthCheck(
6062 // Allow the seed input screen to complete and avoid
6163 // premature navigation
6264 if (currentRoute is AccessKeyLoginScreen ) {
63- log (" No navigation within seed input" )
65+ startupLog (" No navigation within seed input" )
6466 return @LaunchedEffect
6567 }
6668 if (currentRoute is LoginGraph ) {
67- log (" No navigation within account creation and onboarding" )
68- } else {
69+ startupLog (" No navigation within account creation and onboarding" )
70+ } else {
6971 if (authenticated) {
70- log (" Navigating to home" )
71- onNavigate(listOf (HomeScreen ()), false )
72+ startupLog (" Navigating to home" )
73+ onNavigate(listOf (HomeScreen ()))
7274 } else {
73- log (" Navigating to login" )
74- onNavigate(listOf (LoginScreen ()), false )
75+ startupLog (" Navigating to login" )
76+ onNavigate(listOf (LoginScreen ()))
7577 }
7678 }
7779 } else {
@@ -86,58 +88,80 @@ fun AuthCheck(
8688 LaunchedEffect (deeplinkHandler) {
8789 val scope = this
8890 deeplinkHandler.intent
89- .filterNotNull()
90- .distinctUntilChanged()
91- .mapNotNull { deeplinkHandler.handle() }
92- .flatMapLatest { combine(flowOf(it), SessionManager .authState) { a, b -> a to b } }
93- .filter { (data, authState) ->
94- if (data.first is DeeplinkHandler .Type .Cash || data.first is DeeplinkHandler .Type .Sdk ) {
95- return @filter authState.isAuthenticated == true
96- }
97- return @filter true
98- }
99- .mapNotNull { (data, auth) ->
100- val (type, screens) = data
101- if (type is DeeplinkHandler .Type .Login ) {
102- if (auth.isAuthenticated == true ) {
103- val entropy = (screens.first() as ? LoginScreen )?.seed
104- log(" showing logout confirm" )
105- if (entropy != null ) {
106- deeplinkRouted = true
107- context.getActivity()?.intent = null
108- deeplinkHandler.debounceIntent = null
109- showLogoutMessage(
110- context = context,
111- entropyB64 = entropy,
112- onSwitchAccounts = {
113- scope.launch {
114- delay(300 ) // wait for dismiss
115- onSwitchAccounts(it)
116- deeplinkRouted = false
117- }
118- },
119- onCancel = {
120- deeplinkRouted = false
121- }
122- )
123- return @mapNotNull null
124- }
125- }
126- }
127- screens
91+ .flatMapLatest { combine(flowOf(deeplinkHandler.handle(it)), SessionManager .authState) { a, b -> a to b } }
92+ .filter { (result, authState) ->
93+ startupLog(" checking auth state=${authState.isAuthenticated} " )
94+ // wait for authentication
95+ return @filter result != null && authState.isAuthenticated == true
96+ }.mapNotNull { (result, state) ->
97+ result ? : return @mapNotNull null
98+ result to state
12899 }
129- .onEach { screens ->
100+ .mapSeedToHome()
101+ .map { it.first }
102+ .onEach { (_, screens) ->
130103 deeplinkRouted = true
131- log( " navigated " )
132- onNavigate(screens, true )
104+ startupLog( " navigating from deep link " )
105+ onNavigate(screens)
133106 deeplinkHandler.debounceIntent = null
134107 context.getActivity()?.intent = null
135108 }
109+ .showLogoutConfirmationIfNeeded(
110+ context = context,
111+ scope = scope,
112+ onSwitchAccounts = {
113+ onSwitchAccounts(it)
114+ deeplinkRouted = false
115+ },
116+ onCancel = {
117+ deeplinkRouted = false
118+ }
119+ )
136120 .launchIn(this )
137121 }
138122}
139123
140- private fun log (message : String ) = Timber .tag(AUTH_NAV ).d(message)
124+ fun startupLog (message : String ) = Timber .tag(APP_STARTUP_TAG ).d(message)
125+
126+ private fun Flow<DeeplinkFlowState>.mapSeedToHome (): Flow <DeeplinkFlowState > =
127+ map { (data, auth) ->
128+ startupLog(" checking type" )
129+ val (type, screens) = data
130+ if (type is DeeplinkHandler .Type .Login && auth.isAuthenticated == true ) {
131+ // send the user to home screen
132+ val entropy = (screens.first() as ? LoginScreen )?.seed
133+ val updatedData = type to listOf (HomeScreen (seed = entropy))
134+ updatedData to auth
135+ } else {
136+ data to auth
137+ }
138+ }
139+
140+
141+ private fun Flow<Pair<DeeplinkHandler.Type , List<Screen>>>.showLogoutConfirmationIfNeeded (
142+ context : Context ,
143+ scope : CoroutineScope ,
144+ onSwitchAccounts : (String ) -> Unit ,
145+ onCancel : () -> Unit
146+ ): Flow <Pair <DeeplinkHandler .Type , List <Screen >>> = onEach { (type, screens) ->
147+ if (type is DeeplinkHandler .Type .Login ) {
148+ val entropy = (screens.first() as ? HomeScreen )?.seed
149+ startupLog(" showing logout confirm" )
150+ if (entropy != null ) {
151+ showLogoutMessage(
152+ context = context,
153+ entropyB64 = entropy,
154+ onSwitchAccounts = {
155+ scope.launch {
156+ delay(300 ) // wait for dismiss
157+ onSwitchAccounts(it)
158+ }
159+ },
160+ onCancel = onCancel
161+ )
162+ }
163+ }
164+ }
141165
142166private fun showLogoutMessage (
143167 context : Context ,
0 commit comments