Show the "You're in" screen

This commit is contained in:
Benoit Marty
2023-07-06 11:35:38 +02:00
committed by Benoit Marty
parent d7a7751a76
commit 7e237977eb
8 changed files with 90 additions and 3 deletions

View File

@@ -42,6 +42,7 @@ import io.element.android.appnav.intent.IntentResolver
import io.element.android.appnav.intent.ResolvedIntent
import io.element.android.appnav.root.RootPresenter
import io.element.android.appnav.root.RootView
import io.element.android.features.login.api.LoginUserStory
import io.element.android.features.login.api.oidc.OidcAction
import io.element.android.features.login.api.oidc.OidcActionFlow
import io.element.android.features.preferences.api.CacheService
@@ -55,6 +56,7 @@ import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
@@ -74,6 +76,7 @@ class RootFlowNode @AssistedInject constructor(
private val bugReportEntryPoint: BugReportEntryPoint,
private val intentResolver: IntentResolver,
private val oidcActionFlow: OidcActionFlow,
private val loginUserStory: LoginUserStory,
) :
BackstackNode<RootFlowNode.NavTarget>(
backstack = BackStack(
@@ -90,8 +93,7 @@ class RootFlowNode @AssistedInject constructor(
}
private fun observeLoggedInState() {
authenticationService.isLoggedIn()
.distinctUntilChanged()
isUserLoggedInFlow()
.combine(
cacheService.cacheIndex().onEach {
Timber.v("cacheIndex=$it")
@@ -114,6 +116,16 @@ class RootFlowNode @AssistedInject constructor(
.launchIn(lifecycleScope)
}
private fun isUserLoggedInFlow(): Flow<Boolean> {
return combine(
authenticationService.isLoggedIn(),
loginUserStory.loginFlowIsDone()
) { isLoggedIn, loginFlowIsDone ->
isLoggedIn && loginFlowIsDone
}
.distinctUntilChanged()
}
private fun switchToLoggedInFlow(sessionId: SessionId, cacheIndex: Int) {
backstack.safeRoot(NavTarget.LoggedInFlow(sessionId, cacheIndex))
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.login.api
import kotlinx.coroutines.flow.Flow
interface LoginUserStory {
fun loginFlowIsDone(): Flow<Boolean>
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.login.impl
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.login.api.LoginUserStory
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import javax.inject.Inject
@SingleIn(AppScope::class)
@ContributesBinding(AppScope::class)
class DefaultLoginUserStory @Inject constructor() : LoginUserStory {
// True by default, will be set to false when the login user story is started, and set to true again once it's done.
private val loginFlowIsDone: MutableStateFlow<Boolean> = MutableStateFlow(true)
override fun loginFlowIsDone(): Flow<Boolean> = loginFlowIsDone
fun setLoginFlowIsDone(value: Boolean) {
loginFlowIsDone.value = value
}
}

View File

@@ -60,6 +60,7 @@ class LoginFlowNode @AssistedInject constructor(
private val customTabAvailabilityChecker: CustomTabAvailabilityChecker,
private val customTabHandler: CustomTabHandler,
private val accountProviderDataSource: AccountProviderDataSource,
private val defaultLoginUserStory: DefaultLoginUserStory,
) : BackstackNode<LoginFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.ConfirmAccountProvider,
@@ -77,6 +78,11 @@ class LoginFlowNode @AssistedInject constructor(
private val inputs: Inputs = inputs()
override fun onBuilt() {
super.onBuilt()
defaultLoginUserStory.setLoginFlowIsDone(false)
}
sealed interface NavTarget : Parcelable {
@Parcelize
object ConfirmAccountProvider : NavTarget

View File

@@ -24,6 +24,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.features.login.impl.DefaultLoginUserStory
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
@@ -36,6 +37,7 @@ import javax.inject.Inject
class LoginPasswordPresenter @Inject constructor(
private val authenticationService: MatrixAuthenticationService,
private val accountProviderDataSource: AccountProviderDataSource,
private val defaultLoginUserStory: DefaultLoginUserStory,
) : Presenter<LoginPasswordState> {
@Composable
@@ -77,6 +79,8 @@ class LoginPasswordPresenter @Inject constructor(
loggedInState.value = Async.Loading()
authenticationService.login(formState.login.trim(), formState.password)
.onSuccess { sessionId ->
// We will not navigate to the WaitList screen, so the login user story is done
defaultLoginUserStory.setLoginFlowIsDone(true)
loggedInState.value = Async.Success(sessionId)
}
.onFailure { failure ->

View File

@@ -19,4 +19,5 @@ package io.element.android.features.login.impl.screens.waitlistscreen
sealed interface WaitListEvents {
object AttemptLogin : WaitListEvents
object ClearError : WaitListEvents
object Continue : WaitListEvents
}

View File

@@ -24,6 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import io.element.android.features.login.impl.DefaultLoginUserStory
import io.element.android.features.login.impl.screens.loginpassword.LoginFormState
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
@@ -38,6 +39,7 @@ class WaitListPresenter @AssistedInject constructor(
@Assisted private val formState: LoginFormState,
private val buildMeta: BuildMeta,
private val authenticationService: MatrixAuthenticationService,
private val defaultLoginUserStory: DefaultLoginUserStory,
) : Presenter<WaitListState> {
@AssistedFactory
@@ -68,6 +70,7 @@ class WaitListPresenter @AssistedInject constructor(
}
}
WaitListEvents.ClearError -> loginAction.value = Async.Uninitialized
WaitListEvents.Continue -> defaultLoginUserStory.setLoginFlowIsDone(true)
}
}

View File

@@ -208,7 +208,7 @@ private fun WaitListContent(
}
if (state.loginAction is Async.Success) {
Button(
onClick = { TODO() },
onClick = { state.eventSink.invoke(WaitListEvents.Continue) },
colors = ButtonDefaults.buttonColors(
containerColor = Color.White,
contentColor = Color.Black,